Releases: directus/rstore
v0.8.0
🚀 Enhancements
- Revamped cache and layers for faster performance (2b59e9b)
 - nuxt: Newer nuxt dependency and related improvements (baa8497)
 - query: Use onServerPrefetch to allow SSR without awaiting the query in script setup (1f3394f)
 - drizzle: Realtime updates with websockets (1070ed5)
 - Offline (9556f2b)
 
🔥 Performance
- Simpler relation lookup (1e0319f)
 
🩹 Fixes
- Call plugin setup simultaneously so they have the Nuxt context, preventing errors (d0ec259)
 - directus: Import form 
#importsinstead ofnuxt/app(6c854e4) - drizzle: Use 
/api/rstore-realtime/wsas default websocket path (420ee34) 
📖 Documentation
🏡 Chore
- Update pnpm (5c05331)
 
❤️ Contributors
- Guillaume Chau (@Akryum)
 
v0.7.8
v0.7.7
🚀 Enhancements
🔥 Performance
- Shallow items in the cache (3c0b535)
 
🩹 Fixes
- Improve item wrapping for fetchPolicy 'no-cache' (d0fbe6e)
 - Deduplicate wrapped items ownKeys (4673a1f)
 - Handle JSON stringifying wrapped items (d0158f3)
 - gc: Don't run a garbage collection on query unmount (f180d31)
 
❤️ Contributors
- Guillaume Chau (@Akryum)
 
v0.7.6
v0.7.5
🚀 Enhancements
- New garbage collector (2378647)
 - Expose useQueryTracking (42865ff)
 - Allow passing meta to find options (ed8e3d0)
 - AddToQueryTracking (cdfc3f8)
 
🩹 Fixes
- devtools: Cache count per collection not working if no layer is selected (2a157a3)
 - More stable tracking query id (b1d8ae1)
 - Enumerate relations and computed so watch deep works (b737d9a)
 - Export setActiveStore (3484a28)
 
📖 Documentation
🌊 Types
- New RstoreGlobal augmentable interface to apply custom store types to plugin hooks etc. (0bc5706)
 
❤️ Contributors
- Guillaume Chau (@Akryum)
 - Rijk Van Zanten (@rijkvanzanten)
 
v0.7.4
v0.7.3
🚀 Enhancements
- devtools: Improved filters (a3f59d3)
 - devtools: Cache layers (d7d1038)
 - devtools: Go to cache tab from collections (dd18099)
 - cache: Handle frozen items in writeItem (6586aec)
 - nuxt-drizzle: Extras in query transform (803a569)
 - CreateMany, updateMany, deleteMany (40aaa50)
 
🔥 Performance
- cache: Iterate over state directly (7016950)
 - devtools: Considerably reduce overhead of adding history records (453f85a)
 
🩹 Fixes
- Relations undefined if fetchPolicy is no-cache (9845539)
 - Use item as proxy target to improve dx in browser devtools (a11b218)
 - devtools: Local icon (35bcdc5)
 - nuxt-drizzle: Allow any field in where clauses (84301f1)
 - nuxt-drizzle: OrderBy allow any column name (b7d07b5)
 - cache: Handle existing frozen items in cache (f4bb64f)
 
❤️ Contributors
- Guillaume Chau (@Akryum)
 
v0.7.2
v0.7.1
🚀 Enhancements
- nuxt-drizzle: Expose filterWhere (e79683d)
 
🔥 Performance
🩹 Fixes
- nuxt-drizzle: Key from primary keys adding up numbers (cffbef7)
 - nuxt-drizzle: Multi primary key update (e91be9c)
 
📖 Documentation
- Fix nuxt setup (bce3518)
 - Update nuxt folders (6c25b2a)
 - nuxt-drizzle: Fix drizzleImport (25de30b)
 - Collection hooks (be08544)
 
❤️ Contributors
- Guillaume Chau (@Akryum)
 
v0.7.0
Breaking changes
Throughout the codebase, the term Model was changed to Collection to prevent confusion with Vue's own model API. A lot of APIs also were simplified and cleaned up, or changed to improve type checking with TypeScript.
New Features
Collection Hooks
To make it easier to get started, you can now directly define some functions on the collections themselves to implement logic for fetching and mutating the data.
import { defineCollection } from '@rstore/vue'
export const todoCollection = defineCollection({
  name: 'todos',
  // Interact with a REST/GraphQL/etc. API
  hooks: {
    fetchFirst: ({ key }) => fetch(`/api/todos/${key}`).then(r => r.json()),
    fetchMany: ({ params }) => fetch('/api/todos').then(r => r.json()),
    create: ({ item }) => { /* ... */ },
    update: ({ key, item }) => { /* ... */ },
    delete: ({ key }) => { /* ... */ },
  },
})New Relations API
You should now use defineRelations instead of the collection relations option, to enjoy better typechecking. The syntax has also changed to be less error-prone, allow matching on multiple fields and with a custom filter function.
import { createStore, defineCollection, defineRelations } from '@rstore/vue'
const collection1 = defineCollection({ name: 'collection1' })
const collection2 = defineCollection({ name: 'collection2' })
const collection1Relations = defineRelations(collection1, ({ collection }) => ({
  relatedItems: {
    to: collection(collection2, {
      on: {
        'collection2.foreignKey': 'collection1.id', // Type checked!
      },
      // filter: (itemCol1, itemCol2) => true
    }),
    many: true, // One-to-many relation
  },
}))
const store = await createStore({
  schema: [
    collection1,
    collection2,
    collection1Relations,
  ],
})Simplified Nuxt setup
You can remove the export default [...] array and instead directly export the collections from the schema files in Nuxt projects, which is much simpler and less error-prone.
export const users = RStoreSchema.withItemType<User>().defineCollection({
  name: 'users',
})
export const bots = RStoreSchema.withItemType<Bot>().defineCollection({
  name: 'bots',
})New Query API
The new query and liveQuery methods replace queryFirst, queryMany, liveQueryFirst and liveQueryMany to provide improved TypeScript checking (see the Migration Guide for more details).
const { data: todo } = store.Todo.query(q => q.first('some-key'))
const email = ref('@acme.com')
const { data: someUsers } = store.User.query(q => q.many({
  filter: item => item.email.endsWith(email.value),
  params: {
    email: {
      $like: `%${email.value}`,
    },
  },
}))Simplified Modules API
The syntax to define Modules has been greatly simplified (see the Migration Guide for more details).
import { defineModule } from '@rstore/vue'
export const useAuth = defineModule('auth', ({ store, defineState, defineMutation, onResolve }) => {
  const state = defineState({
    // Create some state here
    currentUserKey: null as string | null,
  })
  return {
    // Expose things here
  }
})The mutation state also got simplified, without the need for .value:
<script setup lang="ts">
const auth = useAuth() // A module defined with defineModule
</script>
<template>
  <UAlert
    v-if="auth.login.$error"
    :description="auth.login.$error.message"
    color="error"
  />
  <UButton
    type="submit"
    :loading="auth.login.$loading"
  />
</template>Optimistic updates
Mutations automatically apply the changes without waiting for server responses, making the UI feel more responsive. You can disable optimistic updates by passing the optimistic: false option to the mutation method.
const newTodo = await store.todos.create({
  title: 'New Todo',
  completed: false,
}, {
  optimistic: false,
})You can customize the expected result of the mutation by passing an object to the optimistic option. It will be merged with the data you pass to the mutation method but can override it.
const newTodo = await store.todos.create({
  title: 'New Todo',
  completed: false,
}, {
  optimistic: {
    id: 'temp-id',
    createdAt: new Date().toISOString(),
  },
})
/*
The optimistic object will be:
{
  id: 'temp-id', // added
  title: 'New Todo',
  completed: false,
  createdAt: '2024-06-01T12:00:00.000Z', // added
}
*/To know if a record is optimistic, you can check the $isOptimistic property on the record.
const todo = await store.todos.findFirst('some-id')
if (todo.$isOptimistic) {
  console.log('This record is optimistic')
}Experimental Garbage Collection
You can enable a new experimental experimentalGarbageCollection option so rstore will automatically purge the cache from items that are not returned by queries (usually when they are deleted on the servers).
createStore({
  experimentalGarbageCollection: true,
})For nuxt:
// nuxt.config.ts
export default defineNuxtConfig({
	rstore: {
		experimentalGarbageCollection: true,
	},
});Layered Plugins
Several new Plugins features allow handling complex layered data flows:
- Plugin Categories to automatically sort the call order of plugins: 
'virtual', 'local', 'remote', 'processing'. - Sorting overrides in the plugin definitions.
 - Abort API to prevent calling further plugin callbacks for a hook.
 - Auto-aborting of 
fetchFirst,fetchMany,createItemandupdateItemwhen callingsetResultwith a non-empty result. 
import { definePlugin } from '@rstore/vue'
export default definePlugin({
  name: 'my-plugin',
  // Will be after 'virtual' and 'local' plugins and before 'processing' plugins
  category: 'remote',
  // Override sorting
  before: {
    plugins: ['another-plugin'],
    categories: ['remote'],
  },
  // after: { ... },
  setup({ hook }) {
    hook('deleteItem', ({ abort }) => {
      // ...
      abort()
    })
  },
})Drizzle Hooks
The Nuxt+Drizzle module received a host of new features around hooking into the generated API.
import * as tables from 'path-to-your-drizzle-schema'
export default defineNitroPlugin(() => {
  // Deny access to all the other tables
  allowTables([
    tables.todos,
  ])
  // Implement permissions or other logic
  hooksForTable(tables.todos, {
    'index.get.before': async (payload) => {
      console.log('Specific hook for todos - index.get.before', payload.collection, payload.query, payload.params)
    },
    'index.get.after': async (payload) => {
      console.log('Specific hook for todos - index.get.after', payload.collection, payload.result.map(r => r.id))
    },
    'item.patch.after': async (payload) => {
      console.log('Specific hook for todos - item.patch.after', payload.collection, payload.result.id)
    },
  })
})Devtools improvements
- New Cache Tab
 
- Improved Plugins Tab
 
- Many other improvements and fixes
 
Detailed changelog
🚀 Enhancements
- New schema and relation declarations (844635b)
 - devtools: New cache tab (5be73c2)
 - New query API (04c4e34)
 - cache: ReadItems new limit option (bc7d1fa)
 - Experimental garbage collection (a119680)
 - devtools: Auto update cache (e7326da)
 - Cache layers (43519e0)
 - Optimistic updates (04a5c63)
 - devtools: Gc + cache layer events in history (9a1ccef)
 - UseStore + new setup docs (cbd97d6)
 - New module API (07c2c9c)
 - SetActiveStore for tests (d32cc67)
 - nuxt-drizzle: Remove deprecated option drizzleImport.default (2e737ba)
 - nuxt-drizzle: Drizzle hooks (c7cb660)
 - nuxt-drizzle: Adapt to new relations syntax (f2a5088)
 - nuxt-drizzle: Use new define model syntax (3edb432)
 - Use nuxt module dependencies (9be4ab5)
 - nuxt: Simpler schema syntax with export const (b6aa3fa)
 - nuxt-drizzle: HooksForTable (6e9f3d0)
 - nuxt-drizzle: AllowTables (c8361d3)
 - Sort plugins (b727f31)
 - plugin: Auto abort fetchFirst & fetchMany (7ce07ae)
 - plugin: Category (ec1a41b)
 - Collection hooks (c6824b1)
 - module: $loading, $error, $time no longer Refs (d98f1c2)
 - plugin: Abort API (541d41d)
 - plugin: Abort API for fetchRelations (b72474a)
 - plugin: Auto abort createItem and updateItem (0d8218a)
 
🔥 Performance
🩹 Fixes
- devtools: Cache model item height (12b5ee9)
 - Disallow excess properties in withItemType().defineModel() (c7c2f1f)
 - Always wrap items out of find methods (f7cc489)
 - Store.$getModel should use 
item.$model(2383304) - devtools: Automatically update cache filtering (52c6da2)
 - devtools: Sort models (a50c7de)
 - Clone properties for mutations (c1d614d)
 - Optimistic updates using serialized item (8f21f2f)
 - Don't always clone properties in pickNonSpecialProps (86ceeff)
 - devtools: Cache model item height (276b3c5)
 - Lower minimum dep version of @nuxt/kit (8503aa5)
 - nuxt-drizzle: Missing getQuery import (39d613e)
 - **nuxt-drizzl...