@lunora/client 1.0.0-alpha.2 → 1.0.0-alpha.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/LICENSE.md +6 -0
  2. package/README.md +2 -0
  3. package/__assets__/package-og.svg +1 -1
  4. package/dist/auth/index.d.mts +1 -1
  5. package/dist/auth/index.d.ts +1 -1
  6. package/dist/index.d.mts +144 -24
  7. package/dist/index.d.ts +144 -24
  8. package/dist/index.mjs +10 -9
  9. package/dist/packem_shared/CONFLICT_ERROR_CODE-B8gQ8tyU.mjs +33 -0
  10. package/dist/packem_shared/{DEFAULT_MAX_BUFFER-BDkqO5PW.mjs → DEFAULT_MAX_BUFFER-7hFnzNk9.mjs} +6 -3
  11. package/dist/packem_shared/{LunoraClient-UiULzH_1.mjs → LunoraClient-kXpHNyaE.mjs} +1562 -267
  12. package/dist/packem_shared/OfflineQueue-GGYJRmhF.mjs +1 -0
  13. package/dist/packem_shared/SubscriptionRegistry-DjGKZsqq.mjs +1 -0
  14. package/dist/packem_shared/{applyDelta-4jFGTPA3.mjs → applyDelta-CRKZ1PBt.mjs} +21 -1
  15. package/dist/packem_shared/createInMemoryPersistence-DZ2VHWgm.mjs +79 -0
  16. package/dist/packem_shared/{createInMemoryQueryCache-B1PQ9Twl.mjs → createInMemoryQueryCache-DiaGZkA2.mjs} +25 -51
  17. package/dist/packem_shared/createLocalStore-jRoqmazl.mjs +2 -0
  18. package/dist/packem_shared/createMutatorRunner-BETvCd0p.mjs +31 -0
  19. package/dist/packem_shared/{createServerClient-BjZc3gD8.mjs → createServerClient-DF-3mLmb.mjs} +1 -1
  20. package/dist/packem_shared/idb-utility-DrSVX43Q.mjs +48 -0
  21. package/dist/packem_shared/local-store-BveBeFEo.mjs +106 -0
  22. package/dist/packem_shared/{lunora-client.d-DGvyuJ_p.d.mts → lunora-client.d-BYkEjCEJ.d.mts} +1001 -58
  23. package/dist/packem_shared/{lunora-client.d-DGvyuJ_p.d.ts → lunora-client.d-BYkEjCEJ.d.ts} +1001 -58
  24. package/dist/packem_shared/{OfflineQueue-D5p_QgF_.mjs → offline-queue-B9vfdSqp.mjs} +49 -6
  25. package/dist/packem_shared/{preload.d-BoDmFqSG.d.ts → preload.d-B-vyHnml.d.ts} +1 -1
  26. package/dist/packem_shared/{preload.d-dSaRMuhL.d.mts → preload.d-DrfuisCE.d.mts} +1 -1
  27. package/dist/packem_shared/subscription-BjynOXCU.mjs +68 -0
  28. package/dist/query/index.d.mts +2 -2
  29. package/dist/query/index.d.ts +2 -2
  30. package/dist/ssr/index.d.mts +3 -3
  31. package/dist/ssr/index.d.ts +3 -3
  32. package/dist/ssr/index.mjs +1 -1
  33. package/package.json +5 -2
  34. package/dist/packem_shared/CONFLICT_ERROR_CODE-aUdVbEDw.mjs +0 -4
  35. package/dist/packem_shared/SubscriptionRegistry-B-Qx_Gux.mjs +0 -26
  36. package/dist/packem_shared/createInMemoryPersistence-CW82inU5.mjs +0 -105
  37. package/dist/packem_shared/createLocalStore-DSUfoLqY.mjs +0 -36
@@ -1,10 +1,16 @@
1
+ const isStaleVersion = (current, stamped) => current !== void 0 && stamped !== current;
2
+
1
3
  let idCounter = 0;
2
4
  const nextId = () => {
3
5
  if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
4
6
  return crypto.randomUUID();
5
7
  }
6
8
  idCounter += 1;
7
- return `m_${Date.now().toString(36)}_${idCounter.toString(36)}`;
9
+ const entropy = typeof crypto !== "undefined" && typeof crypto.getRandomValues === "function" ? [...crypto.getRandomValues(new Uint8Array(8))].map((byte) => byte.toString(16).padStart(2, "0")).join("") : (
10
+ // eslint-disable-next-line sonarjs/pseudo-random -- non-cryptographic uniqueness entropy, not a security token; only reached when neither crypto.randomUUID nor crypto.getRandomValues exists
11
+ Math.random().toString(16).slice(2, 12)
12
+ );
13
+ return `m_${Date.now().toString(36)}_${idCounter.toString(36)}_${entropy}`;
8
14
  };
9
15
  const reportPersistenceError = (handler, operation, error, mutationId) => {
10
16
  if (handler) {
@@ -19,12 +25,19 @@ class OfflineQueue {
19
25
  maxItems;
20
26
  onPersistenceError;
21
27
  persistence;
28
+ onEvict;
29
+ onSizeChange;
30
+ /** App/schema version stamped on persisted writes; mismatched records are purged on hydrate. */
31
+ version;
22
32
  items = [];
23
- constructor(options = {}, persistence) {
33
+ constructor(options = {}, deps = {}) {
24
34
  this.maxItems = options.maxItems ?? 1e3;
25
35
  this.queueBeforeFirstConnect = options.queueBeforeFirstConnect ?? false;
26
36
  this.onPersistenceError = options.onPersistenceError;
27
- this.persistence = persistence;
37
+ this.persistence = deps.persistence;
38
+ this.onEvict = deps.onEvict;
39
+ this.onSizeChange = deps.onSizeChange;
40
+ this.version = deps.version;
28
41
  }
29
42
  get size() {
30
43
  return this.items.length;
@@ -33,7 +46,14 @@ class OfflineQueue {
33
46
  const item = entry;
34
47
  item.id ??= nextId();
35
48
  this.items.push(item);
36
- this.persistence?.append({ args: item.args, functionPath: item.functionPath, id: item.id, identity: item.identity, shardKey: item.shardKey }).catch((error) => {
49
+ this.persistence?.append({
50
+ args: item.args,
51
+ functionPath: item.functionPath,
52
+ id: item.id,
53
+ identity: item.identity,
54
+ shardKey: item.shardKey,
55
+ ...this.version === void 0 ? {} : { version: this.version }
56
+ }).catch((error) => {
37
57
  reportPersistenceError(this.onPersistenceError, "append", error, item.id);
38
58
  });
39
59
  while (this.items.length > this.maxItems) {
@@ -47,8 +67,10 @@ class OfflineQueue {
47
67
  const error = new Error("offline queue overflow");
48
68
  error.code = "OFFLINE_QUEUE_OVERFLOW";
49
69
  dropped.reject(error);
70
+ this.onEvict?.(dropped, error);
50
71
  }
51
72
  }
73
+ this.notifySize();
52
74
  }
53
75
  /**
54
76
  * Restore mutations persisted in a prior session and re-queue them in FIFO
@@ -62,12 +84,24 @@ class OfflineQueue {
62
84
  if (!this.persistence) {
63
85
  return [];
64
86
  }
65
- const persisted = await this.persistence.load();
87
+ let persisted;
88
+ try {
89
+ persisted = await this.persistence.load();
90
+ } catch (error) {
91
+ reportPersistenceError(this.onPersistenceError, "load", error);
92
+ throw error;
93
+ }
66
94
  const shardKeys = /* @__PURE__ */ new Set();
67
95
  for (const mutation of persisted) {
68
96
  if (this.items.some((item) => item.id === mutation.id)) {
69
97
  continue;
70
98
  }
99
+ if (isStaleVersion(this.version, mutation.version)) {
100
+ this.persistence.remove(mutation.id).catch((error) => {
101
+ reportPersistenceError(this.onPersistenceError, "remove", error, mutation.id);
102
+ });
103
+ continue;
104
+ }
71
105
  this.items.push({
72
106
  args: mutation.args,
73
107
  functionPath: mutation.functionPath,
@@ -79,6 +113,7 @@ class OfflineQueue {
79
113
  });
80
114
  shardKeys.add(mutation.shardKey);
81
115
  }
116
+ this.notifySize();
82
117
  return [...shardKeys];
83
118
  }
84
119
  /**
@@ -91,6 +126,7 @@ class OfflineQueue {
91
126
  if (!predicate) {
92
127
  const drained2 = [...this.items];
93
128
  this.items.length = 0;
129
+ this.notifySize();
94
130
  return drained2;
95
131
  }
96
132
  const drained = [];
@@ -100,6 +136,7 @@ class OfflineQueue {
100
136
  }
101
137
  this.items.length = 0;
102
138
  this.items.push(...kept);
139
+ this.notifySize();
103
140
  return drained;
104
141
  }
105
142
  /**
@@ -113,6 +150,7 @@ class OfflineQueue {
113
150
  return;
114
151
  }
115
152
  this.items.unshift(...items);
153
+ this.notifySize();
116
154
  }
117
155
  clear() {
118
156
  for (const item of this.items) {
@@ -121,7 +159,12 @@ class OfflineQueue {
121
159
  item.reject(error);
122
160
  }
123
161
  this.items.length = 0;
162
+ this.notifySize();
163
+ }
164
+ /** Notify the size observer (the client's pending-sync count) after any change. */
165
+ notifySize() {
166
+ this.onSizeChange?.(this.items.length);
124
167
  }
125
168
  }
126
169
 
127
- export { OfflineQueue, nextId, reportPersistenceError };
170
+ export { OfflineQueue as O, isStaleVersion as i, nextId as n, reportPersistenceError as r };
@@ -1,4 +1,4 @@
1
- import { F as FunctionReference, L as LunoraClient, A as ArgsOf, R as ReturnOf, P as Preloaded } from "./lunora-client.d-DGvyuJ_p.js";
1
+ import { F as FunctionReference, L as LunoraClient, A as ArgsOf, R as ReturnOf, P as Preloaded } from "./lunora-client.d-BYkEjCEJ.js";
2
2
  /**
3
3
  * Run a query once on the server (during SSR) and capture its result in a
4
4
  * serializable {@link Preloaded} token. Embed the token in the rendered HTML and
@@ -1,4 +1,4 @@
1
- import { F as FunctionReference, L as LunoraClient, A as ArgsOf, R as ReturnOf, P as Preloaded } from "./lunora-client.d-DGvyuJ_p.mjs";
1
+ import { F as FunctionReference, L as LunoraClient, A as ArgsOf, R as ReturnOf, P as Preloaded } from "./lunora-client.d-BYkEjCEJ.mjs";
2
2
  /**
3
3
  * Run a query once on the server (during SSR) and capture its result in a
4
4
  * serializable {@link Preloaded} token. Embed the token in the rendered HTML and
@@ -0,0 +1,68 @@
1
+ const compareKeys = (a, b) => {
2
+ if (a < b) {
3
+ return -1;
4
+ }
5
+ return a > b ? 1 : 0;
6
+ };
7
+ const stableStringify = (value) => {
8
+ if (value === void 0) {
9
+ return "null";
10
+ }
11
+ if (typeof value === "bigint") {
12
+ throw new TypeError("stableStringify: cannot use a bigint in a cache key (query/subscription/shape args) — pass it as a string");
13
+ }
14
+ if (value === null || typeof value !== "object") {
15
+ return JSON.stringify(value);
16
+ }
17
+ if (Array.isArray(value)) {
18
+ return `[${value.map((item) => stableStringify(item)).join(",")}]`;
19
+ }
20
+ const proto = Object.getPrototypeOf(value);
21
+ if (proto !== null && proto !== Object.prototype) {
22
+ const name = value.constructor?.name ?? "value";
23
+ throw new TypeError(
24
+ `stableStringify: cannot use a ${name} in a cache key (query/subscription/shape args) — only plain objects, arrays, and JSON primitives are supported`
25
+ );
26
+ }
27
+ const record = value;
28
+ const keys = Object.keys(record).toSorted(compareKeys);
29
+ const parts = [];
30
+ for (const key of keys) {
31
+ const raw = record[key];
32
+ if (raw === void 0) {
33
+ continue;
34
+ }
35
+ parts.push(`${JSON.stringify(key)}:${stableStringify(raw)}`);
36
+ }
37
+ return `{${parts.join(",")}}`;
38
+ };
39
+
40
+ class SubscriptionRegistry {
41
+ static key(functionPath, args, shardKey) {
42
+ return `${functionPath}::${stableStringify(args)}::${shardKey ?? ""}`;
43
+ }
44
+ byKey = /* @__PURE__ */ new Map();
45
+ byId = /* @__PURE__ */ new Map();
46
+ get(key) {
47
+ return this.byKey.get(key);
48
+ }
49
+ getById(id) {
50
+ return this.byId.get(id);
51
+ }
52
+ add(state) {
53
+ this.byKey.set(SubscriptionRegistry.key(state.fn.__lunoraRef, state.args, state.shardKey), state);
54
+ this.byId.set(state.id, state);
55
+ }
56
+ remove(state) {
57
+ const key = SubscriptionRegistry.key(state.fn.__lunoraRef, state.args, state.shardKey);
58
+ if (this.byKey.get(key) === state) {
59
+ this.byKey.delete(key);
60
+ }
61
+ this.byId.delete(state.id);
62
+ }
63
+ all() {
64
+ return [...this.byKey.values()];
65
+ }
66
+ }
67
+
68
+ export { SubscriptionRegistry as S, stableStringify as s };
@@ -1,5 +1,5 @@
1
- import { S as SubscriptionError, F as FunctionReference, A as ArgsOf, R as ReturnOf, L as LunoraClient, a as Unsubscribe } from "../packem_shared/lunora-client.d-DGvyuJ_p.mjs";
2
- export type { b as SubscriptionErrorCallback } from "../packem_shared/lunora-client.d-DGvyuJ_p.mjs";
1
+ import { S as SubscriptionError, F as FunctionReference, A as ArgsOf, R as ReturnOf, L as LunoraClient, a as Unsubscribe } from "../packem_shared/lunora-client.d-BYkEjCEJ.mjs";
2
+ export type { b as SubscriptionErrorCallback } from "../packem_shared/lunora-client.d-BYkEjCEJ.mjs";
3
3
  import '@lunora/runtime';
4
4
  /**
5
5
  * The sentinel a framework adapter resolves its reactive args to when it wants
@@ -1,5 +1,5 @@
1
- import { S as SubscriptionError, F as FunctionReference, A as ArgsOf, R as ReturnOf, L as LunoraClient, a as Unsubscribe } from "../packem_shared/lunora-client.d-DGvyuJ_p.js";
2
- export type { b as SubscriptionErrorCallback } from "../packem_shared/lunora-client.d-DGvyuJ_p.js";
1
+ import { S as SubscriptionError, F as FunctionReference, A as ArgsOf, R as ReturnOf, L as LunoraClient, a as Unsubscribe } from "../packem_shared/lunora-client.d-BYkEjCEJ.js";
2
+ export type { b as SubscriptionErrorCallback } from "../packem_shared/lunora-client.d-BYkEjCEJ.js";
3
3
  import '@lunora/runtime';
4
4
  /**
5
5
  * The sentinel a framework adapter resolves its reactive args to when it wants
@@ -1,6 +1,6 @@
1
- import { P as Preloaded, L as LunoraClient } from "../packem_shared/lunora-client.d-DGvyuJ_p.mjs";
2
- export type { A as ArgsOf, F as FunctionReference, R as ReturnOf } from "../packem_shared/lunora-client.d-DGvyuJ_p.mjs";
3
- export { p as preloadQuery, a as preloadedQueryResult } from "../packem_shared/preload.d-dSaRMuhL.mjs";
1
+ import { P as Preloaded, L as LunoraClient } from "../packem_shared/lunora-client.d-BYkEjCEJ.mjs";
2
+ export type { A as ArgsOf, F as FunctionReference, R as ReturnOf } from "../packem_shared/lunora-client.d-BYkEjCEJ.mjs";
3
+ export { p as preloadQuery, a as preloadedQueryResult } from "../packem_shared/preload.d-DrfuisCE.mjs";
4
4
  import '@lunora/runtime';
5
5
  /**
6
6
  * Structural shape of a better-auth `getSession` call's resolved value.
@@ -1,6 +1,6 @@
1
- import { P as Preloaded, L as LunoraClient } from "../packem_shared/lunora-client.d-DGvyuJ_p.js";
2
- export type { A as ArgsOf, F as FunctionReference, R as ReturnOf } from "../packem_shared/lunora-client.d-DGvyuJ_p.js";
3
- export { p as preloadQuery, a as preloadedQueryResult } from "../packem_shared/preload.d-BoDmFqSG.js";
1
+ import { P as Preloaded, L as LunoraClient } from "../packem_shared/lunora-client.d-BYkEjCEJ.js";
2
+ export type { A as ArgsOf, F as FunctionReference, R as ReturnOf } from "../packem_shared/lunora-client.d-BYkEjCEJ.js";
3
+ export { p as preloadQuery, a as preloadedQueryResult } from "../packem_shared/preload.d-B-vyHnml.js";
4
4
  import '@lunora/runtime';
5
5
  /**
6
6
  * Structural shape of a better-auth `getSession` call's resolved value.
@@ -1,4 +1,4 @@
1
1
  export { getServerSession } from '../packem_shared/getServerSession-8jXewqxd.mjs';
2
2
  export { deserializePreloaded, serializePreloaded } from '../packem_shared/deserializePreloaded-C0eJTY_W.mjs';
3
- export { createServerClient } from '../packem_shared/createServerClient-BjZc3gD8.mjs';
3
+ export { createServerClient } from '../packem_shared/createServerClient-DF-3mLmb.mjs';
4
4
  export { preloadQuery, preloadedQueryResult } from '../packem_shared/preloadQuery-lobFkD2Z.mjs';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/client",
3
- "version": "1.0.0-alpha.2",
3
+ "version": "1.0.0-alpha.21",
4
4
  "description": "Lunora browser SDK: WebSocket transport, optimistic updates, and an offline mutation queue",
5
5
  "keywords": [
6
6
  "cloudflare",
@@ -25,7 +25,7 @@
25
25
  "directory": "packages/client"
26
26
  },
27
27
  "files": [
28
- "dist",
28
+ "./dist",
29
29
  "README.md",
30
30
  "LICENSE.md",
31
31
  "__assets__"
@@ -61,6 +61,9 @@
61
61
  "publishConfig": {
62
62
  "access": "public"
63
63
  },
64
+ "dependencies": {
65
+ "@lunora/errors": "1.0.0-alpha.4"
66
+ },
64
67
  "engines": {
65
68
  "node": "^22.15.0 || >=24.11.0"
66
69
  }
@@ -1,4 +0,0 @@
1
- const CONFLICT_ERROR_CODE = "CONFLICT";
2
- const isConflictError = (error) => error instanceof Error && error.code === CONFLICT_ERROR_CODE;
3
-
4
- export { CONFLICT_ERROR_CODE, isConflictError };
@@ -1,26 +0,0 @@
1
- class SubscriptionRegistry {
2
- static key(functionPath, args, shardKey) {
3
- return `${functionPath}::${JSON.stringify(args)}::${shardKey ?? ""}`;
4
- }
5
- byKey = /* @__PURE__ */ new Map();
6
- byId = /* @__PURE__ */ new Map();
7
- get(key) {
8
- return this.byKey.get(key);
9
- }
10
- getById(id) {
11
- return this.byId.get(id);
12
- }
13
- add(state) {
14
- this.byKey.set(SubscriptionRegistry.key(state.fn.__lunoraRef, state.args, state.shardKey), state);
15
- this.byId.set(state.id, state);
16
- }
17
- remove(state) {
18
- this.byKey.delete(SubscriptionRegistry.key(state.fn.__lunoraRef, state.args, state.shardKey));
19
- this.byId.delete(state.id);
20
- }
21
- all() {
22
- return [...this.byKey.values()];
23
- }
24
- }
25
-
26
- export { SubscriptionRegistry };
@@ -1,105 +0,0 @@
1
- const createInMemoryPersistence = () => {
2
- const entries = /* @__PURE__ */ new Map();
3
- const clone = (mutation) => {
4
- return {
5
- args: { ...mutation.args },
6
- functionPath: mutation.functionPath,
7
- id: mutation.id,
8
- identity: mutation.identity,
9
- shardKey: mutation.shardKey
10
- };
11
- };
12
- return {
13
- append: (mutation) => {
14
- entries.set(mutation.id, clone(mutation));
15
- return Promise.resolve();
16
- },
17
- clear: () => {
18
- entries.clear();
19
- return Promise.resolve();
20
- },
21
- load: () => Promise.resolve([...entries.values()].map((mutation) => clone(mutation))),
22
- remove: (id) => {
23
- entries.delete(id);
24
- return Promise.resolve();
25
- }
26
- };
27
- };
28
- const DEFAULT_DATABASE = "lunora";
29
- const DEFAULT_STORE = "offline-mutations";
30
- const ID_INDEX = "by_id";
31
- const promisifyRequest = (request) => new Promise((resolve, reject) => {
32
- request.addEventListener("success", () => {
33
- resolve(request.result);
34
- });
35
- request.addEventListener("error", () => {
36
- reject(request.error ?? new Error("IndexedDB request failed"));
37
- });
38
- });
39
- const createIndexedDbPersistence = (options = {}) => {
40
- const factory = options.indexedDB ?? (typeof indexedDB === "undefined" ? void 0 : indexedDB);
41
- if (!factory) {
42
- throw new Error("createIndexedDbPersistence: no IndexedDB available — pass `indexedDB` or use createInMemoryPersistence()");
43
- }
44
- const databaseName = options.databaseName ?? DEFAULT_DATABASE;
45
- const storeName = options.storeName ?? DEFAULT_STORE;
46
- let databasePromise;
47
- const openDatabase = () => {
48
- if (databasePromise) {
49
- return databasePromise;
50
- }
51
- databasePromise = new Promise((resolve, reject) => {
52
- const request = factory.open(databaseName, 1);
53
- request.addEventListener("upgradeneeded", () => {
54
- const database = request.result;
55
- if (!database.objectStoreNames.contains(storeName)) {
56
- const store = database.createObjectStore(storeName, { autoIncrement: true });
57
- store.createIndex(ID_INDEX, "id", { unique: true });
58
- }
59
- });
60
- request.addEventListener("success", () => {
61
- resolve(request.result);
62
- });
63
- request.addEventListener("error", () => {
64
- reject(request.error ?? new Error("IndexedDB open failed"));
65
- });
66
- });
67
- return databasePromise;
68
- };
69
- const withStore = async (mode, run) => {
70
- const database = await openDatabase();
71
- const transaction = database.transaction(storeName, mode);
72
- const result = await run(transaction.objectStore(storeName));
73
- await new Promise((resolve, reject) => {
74
- transaction.addEventListener("complete", () => {
75
- resolve();
76
- });
77
- transaction.addEventListener("error", () => {
78
- reject(transaction.error ?? new Error("IndexedDB transaction failed"));
79
- });
80
- transaction.addEventListener("abort", () => {
81
- reject(transaction.error ?? new Error("IndexedDB transaction aborted"));
82
- });
83
- });
84
- return result;
85
- };
86
- return {
87
- append: async (mutation) => {
88
- await withStore("readwrite", (store) => promisifyRequest(store.add(mutation)));
89
- },
90
- clear: async () => {
91
- await withStore("readwrite", (store) => promisifyRequest(store.clear()));
92
- },
93
- load: async () => withStore("readonly", (store) => promisifyRequest(store.getAll())),
94
- remove: async (id) => {
95
- await withStore("readwrite", async (store) => {
96
- const key = await promisifyRequest(store.index(ID_INDEX).getKey(id));
97
- if (key !== void 0) {
98
- await promisifyRequest(store.delete(key));
99
- }
100
- });
101
- }
102
- };
103
- };
104
-
105
- export { createInMemoryPersistence, createIndexedDbPersistence };
@@ -1,36 +0,0 @@
1
- const createLocalStore = (subscriptions, shardKey, write, stableStringify) => {
2
- const rollbacks = [];
3
- const findState = (functionRef, argsKey) => {
4
- for (const state of subscriptions.all()) {
5
- if (state.fn.__lunoraRef === functionRef && state.shardKey === shardKey && state.argsKey === argsKey) {
6
- return state;
7
- }
8
- }
9
- return void 0;
10
- };
11
- const store = {
12
- getAllQueries: (function_) => {
13
- const matches = [];
14
- for (const state of subscriptions.all()) {
15
- if (state.fn.__lunoraRef === function_.__lunoraRef && state.shardKey === shardKey) {
16
- matches.push({ args: state.args, value: state.lastValue });
17
- }
18
- }
19
- return matches;
20
- },
21
- getQuery: (function_, args) => {
22
- const state = findState(function_.__lunoraRef, stableStringify(args ?? {}));
23
- return state?.lastValue;
24
- },
25
- setQuery: (function_, args, value) => {
26
- const state = findState(function_.__lunoraRef, stableStringify(args ?? {}));
27
- if (!state) {
28
- return;
29
- }
30
- rollbacks.push(write(state, value));
31
- }
32
- };
33
- return { rollbacks, store };
34
- };
35
-
36
- export { createLocalStore };