@farm.js/sync 0.1.0-beta.2 → 0.1.0-beta.4

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.
package/dist/client.d.ts CHANGED
@@ -100,7 +100,12 @@ type SyncPersistence = {
100
100
  };
101
101
  /** Replace the default browser store, for example with an IndexedDB one. */
102
102
  export declare function setSyncPersistence(store: SyncPersistence | null): void;
103
- /** Remove every persisted row. Call on logout so a shared device stays clean. */
103
+ /**
104
+ * Forget every synced row, on disk and in memory. Call it when the session
105
+ * changes (sign out, sign in as someone else, switch organization) so the next
106
+ * account never sees the previous one's rows. Each model reloads in full on its
107
+ * next use, scoped to the session current at that point.
108
+ */
104
109
  export declare function clearSyncedRows(): void;
105
110
  export declare function hydrateModel(model: string): void;
106
111
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
package/dist/client.js CHANGED
@@ -30,11 +30,31 @@ function requireRuntime() {
30
30
  }
31
31
  throw new Error("The sync runtime is not started. Add `sync({ ... })` to the plugins in farm.config.ts.");
32
32
  }
33
+ const scheduledReloads = new Set();
34
+ /**
35
+ * A store cleared by clearSyncedRows() reloads on its next use, under whichever
36
+ * session is current by then. Deferred so the reload's status change never
37
+ * fires while a component is rendering, and so loadModel's own lookup of the
38
+ * store does not schedule it again.
39
+ */
40
+ function scheduleReload(model, store) {
41
+ if (scheduledReloads.has(model))
42
+ return;
43
+ scheduledReloads.add(model);
44
+ queueMicrotask(() => {
45
+ if (store.status === "idle")
46
+ void loadModel(model);
47
+ scheduledReloads.delete(model);
48
+ });
49
+ }
33
50
  export function getSyncStore(model) {
34
51
  const state = requireRuntime();
35
52
  let store = state.stores.get(model);
36
- if (store)
53
+ if (store) {
54
+ if (store.status === "idle" && isBrowser())
55
+ scheduleReload(model, store);
37
56
  return store;
57
+ }
38
58
  const descriptor = state.config.models[model];
39
59
  if (!descriptor) {
40
60
  if (!isBrowser())
@@ -68,10 +88,13 @@ async function request(body) {
68
88
  /** Fetch rows for a model, incrementally when the schema supports a cursor. */
69
89
  export async function loadModel(model) {
70
90
  const store = getSyncStore(model);
91
+ const generation = store.generation;
71
92
  if (store.status === "idle")
72
93
  store.setStatus("loading");
73
94
  try {
74
95
  const result = await request({ model, operation: "list", since: store.cursor });
96
+ if (store.generation !== generation)
97
+ return;
75
98
  store.applyServerRows(result.rows ?? [], {
76
99
  full: result.full !== false,
77
100
  cursor: result.cursor ?? null,
@@ -80,6 +103,8 @@ export async function loadModel(model) {
80
103
  persistModel(model);
81
104
  }
82
105
  catch (cause) {
106
+ if (store.generation !== generation)
107
+ return;
83
108
  store.setStatus("error", cause instanceof Error ? cause : new Error(String(cause)));
84
109
  }
85
110
  }
@@ -147,18 +172,24 @@ function runMutation(model, operation, input, optimistic) {
147
172
  : { type: "upsert", key: optimistic.key, row: optimistic.row };
148
173
  store.addLayer(layer);
149
174
  store.trackPending(1);
175
+ const generation = store.generation;
150
176
  let handleState = "pending";
151
177
  const isPersisted = (async () => {
152
178
  try {
153
179
  const result = await dispatchWrite(store, { model, operation, input }, state.options);
154
- store.commitLayer(layer, operation === "delete" ? null : result);
155
180
  handleState = "completed";
181
+ // The store was cleared for a new session while this was in flight.
182
+ if (store.generation !== generation)
183
+ return result;
184
+ store.commitLayer(layer, operation === "delete" ? null : result);
156
185
  persistModel(model);
157
186
  return result;
158
187
  }
159
188
  catch (error) {
160
- store.removeLayer(layer);
161
189
  handleState = "failed";
190
+ if (store.generation !== generation)
191
+ throw error;
192
+ store.removeLayer(layer);
162
193
  store.recordFailure({
163
194
  operation,
164
195
  input,
@@ -217,6 +248,7 @@ export function runSyncAction(model, action, invoke, input, explicitPatch) {
217
248
  if (layer)
218
249
  store.addLayer(layer);
219
250
  store.trackPending(1);
251
+ const generation = store.generation;
220
252
  let handleState = "pending";
221
253
  const isPersisted = (async () => {
222
254
  try {
@@ -231,6 +263,10 @@ export function runSyncAction(model, action, invoke, input, explicitPatch) {
231
263
  }
232
264
  const result = await invoke(input);
233
265
  const rows = Array.isArray(result) ? result : [result];
266
+ if (store.generation !== generation) {
267
+ handleState = "completed";
268
+ return (rows[0] ?? null);
269
+ }
234
270
  let committed = false;
235
271
  for (const row of rows) {
236
272
  if (row && typeof row === "object" && row[keyField] !== undefined) {
@@ -254,9 +290,11 @@ export function runSyncAction(model, action, invoke, input, explicitPatch) {
254
290
  return (rows[0] ?? null);
255
291
  }
256
292
  catch (error) {
293
+ handleState = "failed";
294
+ if (store.generation !== generation)
295
+ throw error;
257
296
  if (layer)
258
297
  store.removeLayer(layer);
259
- handleState = "failed";
260
298
  store.recordFailure({
261
299
  operation: action,
262
300
  input,
@@ -406,9 +444,16 @@ function getPersistence() {
406
444
  export function setSyncPersistence(store) {
407
445
  persistence = store;
408
446
  }
409
- /** Remove every persisted row. Call on logout so a shared device stays clean. */
447
+ /**
448
+ * Forget every synced row, on disk and in memory. Call it when the session
449
+ * changes (sign out, sign in as someone else, switch organization) so the next
450
+ * account never sees the previous one's rows. Each model reloads in full on its
451
+ * next use, scoped to the session current at that point.
452
+ */
410
453
  export function clearSyncedRows() {
411
454
  getPersistence()?.clear();
455
+ for (const store of runtime?.stores.values() ?? [])
456
+ store.reset();
412
457
  }
413
458
  function persistModel(model) {
414
459
  const store = runtime?.stores.get(model);
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,EAAE,oBAAoB,EAAE,kBAAkB,EAAsB,MAAM,aAAa,CAAC;AAC3F,OAAO,EACL,iBAAiB,EACjB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EAErB,KAAK,SAAS,EACf,MAAM,YAAY,CAAC;AAEpB,MAAM,MAAM,iBAAiB,GAAG;IAC9B,wEAAwE;IACxE,MAAM,EAAE,UAAU,CAAC;IACnB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,OAAO,GAAG,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;IACtD,+DAA+D;IAC/D,KAAK,CAAC,EAAE,SAAS,GAAG,KAAK,CAAC;IAC1B,mFAAmF;IACnF,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IACzC,kEAAkE;IAClE,UAAU,CAAC,EAAE,SAAS,cAAc,EAAE,CAAC;IACvC,kCAAkC;IAClC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gEAAgE;IAChE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;OAGG;IACH,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,CAC3B,GAAG,EAAE,qBAAqB,KACvB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;AAE9E,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,YAAY,EACV,UAAU,EACV,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,SAAS,EACT,gBAAgB,GACjB,MAAM,YAAY,CAAC;AAIpB,wBAAgB,IAAI,CAAC,OAAO,EAAE,iBAAiB;;;;;;;;;;;yDAgJ9C;AAuLD,mCAAmC;AACnC,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,CAAC;AACvE,YAAY,EAAE,iBAAiB,EAAE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,EAAE,oBAAoB,EAAE,kBAAkB,EAAsB,MAAM,aAAa,CAAC;AAC3F,OAAO,EACL,iBAAiB,EACjB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EAErB,KAAK,SAAS,EACf,MAAM,YAAY,CAAC;AAEpB,MAAM,MAAM,iBAAiB,GAAG;IAC9B,wEAAwE;IACxE,MAAM,EAAE,UAAU,CAAC;IACnB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,OAAO,GAAG,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;IACtD,+DAA+D;IAC/D,KAAK,CAAC,EAAE,SAAS,GAAG,KAAK,CAAC;IAC1B,mFAAmF;IACnF,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IACzC,kEAAkE;IAClE,UAAU,CAAC,EAAE,SAAS,cAAc,EAAE,CAAC;IACvC,kCAAkC;IAClC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gEAAgE;IAChE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;OAGG;IACH,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,CAC3B,GAAG,EAAE,qBAAqB,KACvB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;AAE9E,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,YAAY,EACV,UAAU,EACV,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,SAAS,EACT,gBAAgB,GACjB,MAAM,YAAY,CAAC;AAIpB,wBAAgB,IAAI,CAAC,OAAO,EAAE,iBAAiB;;;;;;;;;;;yDAgJ9C;AAqMD,mCAAmC;AACnC,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,CAAC;AACvE,YAAY,EAAE,iBAAiB,EAAE,CAAC"}
package/dist/index.js CHANGED
@@ -125,6 +125,15 @@ async function handleSyncRequest(request, bodyOverride, options) {
125
125
  if (request.method !== "POST") {
126
126
  return jsonResponse(405, { error: { code: "invalid_input", message: "Sync uses POST." } });
127
127
  }
128
+ // The development server invokes runtime hooks with a Request that carries
129
+ // headers but no body stream, and an upstream consumer can leave the body
130
+ // unreadable. Neither is a client error: fall through so the Node adapter,
131
+ // which can read the underlying stream, serves the request instead of this
132
+ // hook answering 400 for a perfectly valid POST. The Node adapter re-enters
133
+ // this handler with bodyOverride, so the origin check below still guards
134
+ // every served request exactly once.
135
+ if (bodyOverride === undefined && request.body === null)
136
+ return undefined;
128
137
  // Sync operations ride the caller's cookies, and a cross-site page can send
129
138
  // this POST as a simple request with no preflight. Reject foreign origins
130
139
  // before touching the body, mirroring the server-action origin contract.
@@ -141,7 +150,12 @@ async function handleSyncRequest(request, bodyOverride, options) {
141
150
  try {
142
151
  body = bodyOverride ?? (await request.clone().json());
143
152
  }
144
- catch {
153
+ catch (error) {
154
+ if (bodyOverride === undefined && error?.name !== "SyntaxError") {
155
+ // clone() throws when the body was already consumed elsewhere; that is
156
+ // the runtime's shape, not the client's JSON.
157
+ return undefined;
158
+ }
145
159
  return jsonResponse(400, {
146
160
  error: { code: "invalid_input", message: "Request body must be JSON." },
147
161
  });
@@ -1 +1 @@
1
- {"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.ts"],"names":[],"mappings":"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"}
package/dist/react.js CHANGED
@@ -9,10 +9,6 @@ function resolveModelName(model) {
9
9
  export function useLiveQuery(model, predicate, options = {}) {
10
10
  const name = resolveModelName(model);
11
11
  const store = getSyncStore(name);
12
- const predicateRef = useRef(predicate);
13
- predicateRef.current = predicate;
14
- const optionsRef = useRef(options);
15
- optionsRef.current = options;
16
12
  const subscribe = useCallback((listener) => store.subscribe(listener), [store]);
17
13
  // Rows live in the browser, so the server has none. Both sides must agree on
18
14
  // that first snapshot or hydration mismatches; the real rows arrive on the
@@ -28,28 +24,30 @@ export function useLiveQuery(model, predicate, options = {}) {
28
24
  return;
29
25
  hydrateModel(name);
30
26
  }, [name, options.enabled]);
31
- // Recompute only when the underlying rows change; the predicate is read from
32
- // a ref so an inline arrow does not invalidate the memo every render.
27
+ // The memo depends on the real predicate and options, not refs: a predicate
28
+ // that closes over changed state (row => row.status === filter) must produce
29
+ // fresh rows on the next render, not stay frozen until some row mutates. An
30
+ // inline arrow recomputes every render, which is the correct trade; a caller
31
+ // with a large collection keeps memoization by passing a stable predicate.
33
32
  const visible = useMemo(() => {
34
- const current = predicateRef.current;
35
- const opts = optionsRef.current;
36
- let result = current ? rows.filter((row) => current(row)) : rows;
37
- if (opts.orderBy) {
38
- const direction = opts.direction === "desc" ? -1 : 1;
33
+ let result = predicate ? rows.filter((row) => predicate(row)) : rows;
34
+ if (options.orderBy) {
35
+ const direction = options.direction === "desc" ? -1 : 1;
36
+ const orderBy = options.orderBy;
39
37
  result = [...result].sort((a, b) => {
40
- const left = opts.orderBy(a);
41
- const right = opts.orderBy(b);
38
+ const left = orderBy(a);
39
+ const right = orderBy(b);
42
40
  if (left === right)
43
41
  return 0;
44
42
  return (left < right ? -1 : 1) * direction;
45
43
  });
46
44
  }
47
- if (opts.offset || opts.limit !== undefined) {
48
- const start = opts.offset ?? 0;
49
- result = result.slice(start, opts.limit === undefined ? undefined : start + opts.limit);
45
+ if (options.offset || options.limit !== undefined) {
46
+ const start = options.offset ?? 0;
47
+ result = result.slice(start, options.limit === undefined ? undefined : start + options.limit);
50
48
  }
51
49
  return result;
52
- }, [rows]);
50
+ }, [rows, predicate, options.orderBy, options.direction, options.offset, options.limit]);
53
51
  const client = useMemo(() => getSyncModelClient(name), [name]);
54
52
  return {
55
53
  rows: visible,
package/dist/store.d.ts CHANGED
@@ -54,6 +54,12 @@ export declare class SyncModelStore {
54
54
  /** Rolled-back writes awaiting the user. Replaced, never mutated, so a
55
55
  * useSyncExternalStore snapshot of it stays referentially stable. */
56
56
  failures: readonly SyncWriteFailure[];
57
+ /**
58
+ * Bumped by reset(). A load or write that started before a reset compares
59
+ * against it and drops its result, so a response for the previous session
60
+ * cannot land in the store after it was cleared.
61
+ */
62
+ generation: number;
57
63
  private nextFailureId;
58
64
  constructor(name: string, descriptor: SyncModelDescriptor);
59
65
  subscribe(listener: () => void): () => void;
@@ -71,6 +77,12 @@ export declare class SyncModelStore {
71
77
  /** Fold a confirmed server row in and drop the layer that predicted it. */
72
78
  commitLayer(layer: OptimisticLayer, confirmed: SyncRow | null): void;
73
79
  mergeRow(key: unknown, changes: SyncRow): SyncRow;
80
+ /**
81
+ * Forget everything this store holds for a session change: confirmed rows,
82
+ * optimistic layers, recorded failures, and the incremental cursor. The next
83
+ * load is a full one, scoped to whoever is signed in by then.
84
+ */
85
+ reset(): void;
74
86
  /** Rows worth persisting: confirmed only, never optimistic guesses. */
75
87
  confirmedRows(): SyncRow[];
76
88
  hydrate(rows: SyncRow[], cursor: string | null): void;
@@ -1 +1 @@
1
- {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
package/dist/store.js CHANGED
@@ -19,6 +19,12 @@ export class SyncModelStore {
19
19
  /** Rolled-back writes awaiting the user. Replaced, never mutated, so a
20
20
  * useSyncExternalStore snapshot of it stays referentially stable. */
21
21
  failures = [];
22
+ /**
23
+ * Bumped by reset(). A load or write that started before a reset compares
24
+ * against it and drops its result, so a response for the previous session
25
+ * cannot land in the store after it was cleared.
26
+ */
27
+ generation = 0;
22
28
  nextFailureId = 1;
23
29
  constructor(name, descriptor) {
24
30
  this.name = name;
@@ -98,6 +104,21 @@ export class SyncModelStore {
98
104
  const current = this.get(key) ?? { [this.descriptor.key]: key };
99
105
  return { ...current, ...changes };
100
106
  }
107
+ /**
108
+ * Forget everything this store holds for a session change: confirmed rows,
109
+ * optimistic layers, recorded failures, and the incremental cursor. The next
110
+ * load is a full one, scoped to whoever is signed in by then.
111
+ */
112
+ reset() {
113
+ this.generation += 1;
114
+ this.confirmed.clear();
115
+ this.layers = [];
116
+ this.failures = [];
117
+ this.cursor = null;
118
+ this.status = "idle";
119
+ this.error = null;
120
+ this.invalidate();
121
+ }
101
122
  /** Rows worth persisting: confirmed only, never optimistic guesses. */
102
123
  confirmedRows() {
103
124
  return Array.from(this.confirmed.values());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farm.js/sync",
3
- "version": "0.1.0-beta.2",
3
+ "version": "0.1.0-beta.4",
4
4
  "description": "Local-first collections for Farm.js: schema-driven reads, optimistic writes, offline queueing",
5
5
  "keywords": [
6
6
  "farm.js",
@@ -40,15 +40,17 @@
40
40
  "access": "public"
41
41
  },
42
42
  "dependencies": {
43
- "@farm.js/core": "0.1.0-beta.104"
43
+ "@farm.js/core": "0.1.0-beta.109"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@farming-labs/orm": "0.0.62",
47
47
  "@farming-labs/orm-runtime": "0.0.62",
48
48
  "@types/node": "^20.10.5",
49
49
  "@types/react": "^19.0.0",
50
+ "@types/react-dom": "^19.0.0",
50
51
  "jsdom": "^25.0.0",
51
52
  "react": "^19.0.0",
53
+ "react-dom": "^19.0.0",
52
54
  "typescript": "^5.3.3",
53
55
  "unstorage": "^1.16.1",
54
56
  "vitest": "^3.2.7"