@spooky-sync/core 0.0.1-canary.157 → 0.0.1-canary.158

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/index.js CHANGED
@@ -4010,7 +4010,7 @@ var UpQueue = class {
4010
4010
  async enqueueFromDatabase(mutationId) {
4011
4011
  if (this.queue.some((e) => encodeUpEventId(e) === mutationId)) return;
4012
4012
  try {
4013
- const [records] = await this.local.query(`SELECT * FROM $mutation_id`, { mutation_id: parseRecordIdString(mutationId) });
4013
+ const [records] = await this.local.query(`SELECT * FROM $mutation_ids`, { mutation_ids: [parseRecordIdString(mutationId)] });
4014
4014
  const event = Array.isArray(records) && records[0] ? rowToUpEvent(records[0], this.logger) : null;
4015
4015
  if (event) this.addToQueue(event);
4016
4016
  } catch (error) {
@@ -5694,8 +5694,8 @@ async function walkOpfs(maxEntries = 2e3, maxDepth = 8) {
5694
5694
 
5695
5695
  //#endregion
5696
5696
  //#region src/modules/devtools/index.ts
5697
- const CORE_VERSION = "0.0.1-canary.157";
5698
- const WASM_VERSION = "0.0.1-canary.157";
5697
+ const CORE_VERSION = "0.0.1-canary.158";
5698
+ const WASM_VERSION = "0.0.1-canary.158";
5699
5699
  const SURREAL_VERSION = "3.0.3";
5700
5700
  var DevToolsService = class DevToolsService {
5701
5701
  eventsHistory = [];
@@ -8939,7 +8939,7 @@ var Sp00kyClient = class {
8939
8939
  return new TabsCoordinator({
8940
8940
  tabId,
8941
8941
  fingerprint: computeTabsFingerprint({
8942
- coreVersion: "0.0.1-canary.157",
8942
+ coreVersion: "0.0.1-canary.158",
8943
8943
  schemaHash: hash53(this.config.schemaSurql),
8944
8944
  endpoint: this.config.database.endpoint ?? "",
8945
8945
  namespace: this.config.database.namespace,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spooky-sync/core",
3
- "version": "0.0.1-canary.157",
3
+ "version": "0.0.1-canary.158",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "main": "./dist/index.js",
@@ -60,8 +60,8 @@
60
60
  }
61
61
  },
62
62
  "dependencies": {
63
- "@spooky-sync/query-builder": "0.0.1-canary.157",
64
- "@spooky-sync/ssp-wasm": "0.0.1-canary.157",
63
+ "@spooky-sync/query-builder": "0.0.1-canary.158",
64
+ "@spooky-sync/ssp-wasm": "0.0.1-canary.158",
65
65
  "@sqlite.org/sqlite-wasm": "3.53.0-build1",
66
66
  "@surrealdb/wasm": "^3.0.3",
67
67
  "fast-json-patch": "^3.1.1",
@@ -1,5 +1,6 @@
1
1
  import { describe, it, expect, vi } from 'vitest';
2
2
  import { UpQueue } from './queue-up';
3
+ import { translateSurql } from '../../../services/database/surql-translate';
3
4
 
4
5
  function makeLogger(): any {
5
6
  const noop = () => {};
@@ -21,7 +22,8 @@ const ROW = {
21
22
  describe('UpQueue.enqueueFromDatabase', () => {
22
23
  function makeQueue(rows: Record<string, unknown[]>) {
23
24
  const query = vi.fn(async (_sql: string, vars?: Record<string, unknown>) => {
24
- const id = String(vars?.mutation_id ?? '');
25
+ const ids = (vars?.mutation_ids as unknown[]) ?? [];
26
+ const id = String(ids[0] ?? '');
25
27
  return [rows[id] ?? []];
26
28
  });
27
29
  const local: any = { query };
@@ -48,4 +50,26 @@ describe('UpQueue.enqueueFromDatabase', () => {
48
50
  await queue.enqueueFromDatabase(ROW.id);
49
51
  expect(queue.size).toBe(0);
50
52
  });
53
+
54
+ // The mock above replaces `local.query` wholesale, so on its own it proves
55
+ // nothing about the SQLite engine actually being able to RUN the statement.
56
+ // That gap shipped a bug: the emitted `SELECT * FROM $mutation_id` passed a
57
+ // single RecordId where the engine's `selectByIds` lowering expects an array,
58
+ // so it threw, the surrounding catch swallowed it at `error` level, and every
59
+ // follower mutation was silently never pushed. Drive the real translator.
60
+ it('emits a statement the SQLite engine can actually translate', async () => {
61
+ const { queue, query } = makeQueue({ [ROW.id]: [ROW] });
62
+ await queue.enqueueFromDatabase(ROW.id);
63
+
64
+ const [sql, vars] = query.mock.calls[0] as [string, Record<string, unknown>];
65
+ const translated = translateSurql(sql, vars);
66
+ const op: any = translated.ops[0];
67
+
68
+ expect(op.kind).toBe('selectByIds');
69
+ // The engine does `ids.length` then `ids.map(...)`: a non-array silently
70
+ // skips the empty-guard and then throws.
71
+ expect(Array.isArray(op.ids)).toBe(true);
72
+ expect(op.ids).toHaveLength(1);
73
+ expect(() => (op.ids as unknown[]).map((x) => x)).not.toThrow();
74
+ });
51
75
  });
@@ -1,11 +1,7 @@
1
1
  import type { RecordId } from 'surrealdb';
2
2
  import type { LocalStore } from '../../../services/database/index';
3
- import type {
4
- SyncQueueEventSystem} from '../events/index';
5
- import {
6
- createSyncQueueEventSystem,
7
- SyncQueueEventTypes,
8
- } from '../events/index';
3
+ import type { SyncQueueEventSystem } from '../events/index';
4
+ import { createSyncQueueEventSystem, SyncQueueEventTypes } from '../events/index';
9
5
  import {
10
6
  parseRecordIdString,
11
7
  extractTablePart,
@@ -50,7 +46,10 @@ export class UpQueue {
50
46
  private queue: UpEvent[] = [];
51
47
  private _events: SyncQueueEventSystem;
52
48
  private logger: Logger;
53
- private debouncedMutations: Map<string, { timer: any; firstBeforeRecord?: Record<string, unknown> }>;
49
+ private debouncedMutations: Map<
50
+ string,
51
+ { timer: any; firstBeforeRecord?: Record<string, unknown> }
52
+ >;
54
53
 
55
54
  get events(): SyncQueueEventSystem {
56
55
  return this._events;
@@ -199,10 +198,19 @@ export class UpQueue {
199
198
  async enqueueFromDatabase(mutationId: string): Promise<void> {
200
199
  if (this.queue.some((e) => encodeUpEventId(e) === mutationId)) return;
201
200
  try {
202
- const [records] = await this.local.query<any>(`SELECT * FROM $mutation_id`, {
203
- mutation_id: parseRecordIdString(mutationId),
201
+ // ARRAY param, matching SyncEngine's `SELECT * FROM $idsToFetch`. A bare
202
+ // `FROM $singleRecordId` looks fine against SurrealDB but the SQLite
203
+ // engine lowers any `FROM $param` to `selectByIds` and calls `.map` on
204
+ // the param (surql-translate.ts, SqliteCacheEngine.selectByIds), so a
205
+ // single RecordId threw. The throw landed in the catch below, which logs
206
+ // at `error` — invisible to an app running `logLevel: 'fatal'` — so every
207
+ // forwarded mutation was silently dropped: the follower's optimistic
208
+ // write stuck locally, was never pushed, and the next down-sync reverted it.
209
+ const [records] = await this.local.query<any>(`SELECT * FROM $mutation_ids`, {
210
+ mutation_ids: [parseRecordIdString(mutationId)],
204
211
  });
205
- const event = Array.isArray(records) && records[0] ? rowToUpEvent(records[0], this.logger) : null;
212
+ const event =
213
+ Array.isArray(records) && records[0] ? rowToUpEvent(records[0], this.logger) : null;
206
214
  if (event) this.addToQueue(event);
207
215
  } catch (error) {
208
216
  this.logger.error(
@@ -266,7 +274,11 @@ function rowToUpEvent(r: any, logger: Logger): UpEvent | null {
266
274
  };
267
275
  default:
268
276
  logger.warn(
269
- { mutationType: r.mutationType, record: r, Category: 'sp00ky-client::UpQueue::rowToUpEvent' },
277
+ {
278
+ mutationType: r.mutationType,
279
+ record: r,
280
+ Category: 'sp00ky-client::UpQueue::rowToUpEvent',
281
+ },
270
282
  'Unknown mutation type'
271
283
  );
272
284
  return null;