orez 0.4.45 → 0.4.47

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.
@@ -0,0 +1,27 @@
1
+ export type CursorPullTables = Record<string, // EXACT _zero_changes.table_name identity, e.g. 'public.file'
2
+ {
3
+ clientName: string;
4
+ primaryKey: string[];
5
+ }>;
6
+ export type ChangeLogRow = {
7
+ tableName: string;
8
+ op: 'INSERT' | 'UPDATE' | 'DELETE';
9
+ rowData: Record<string, unknown> | null;
10
+ oldData: Record<string, unknown> | null;
11
+ };
12
+ export type CursorDiffOp = {
13
+ op: 'put';
14
+ tableName: string;
15
+ row: Record<string, unknown>;
16
+ } | {
17
+ op: 'del';
18
+ tableName: string;
19
+ pk: Record<string, unknown>;
20
+ };
21
+ export declare function cursorDiffPatch(opts: {
22
+ changes: ChangeLogRow[];
23
+ tables: CursorPullTables;
24
+ skip: (logTableName: string) => boolean;
25
+ readRow: (clientTableName: string, pk: Record<string, unknown>) => Record<string, unknown> | undefined;
26
+ }): CursorDiffOp[];
27
+ //# sourceMappingURL=cursor-pull.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor-pull.d.ts","sourceRoot":"","sources":["../../src/cf-do/cursor-pull.ts"],"names":[],"mappings":"AAoCA,MAAM,MAAM,gBAAgB,GAAG,MAAM,CACnC,MAAM,EAAE,8DAA8D;AACtE;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,EAAE,CAAA;CAAE,CAC7C,CAAA;AAID,MAAM,MAAM,YAAY,GAAG;IACzB,SAAS,EAAE,MAAM,CAAA;IACjB,EAAE,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAA;IAClC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;IACvC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;CACxC,CAAA;AAED,MAAM,MAAM,YAAY,GACpB;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GAC9D;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAA;AAgBjE,wBAAgB,eAAe,CAAC,IAAI,EAAE;IACpC,OAAO,EAAE,YAAY,EAAE,CAAA;IACvB,MAAM,EAAE,gBAAgB,CAAA;IAIxB,IAAI,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAA;IAGvC,OAAO,EAAE,CACP,eAAe,EAAE,MAAM,EACvB,EAAE,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KACxB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAA;CACzC,GAAG,YAAY,EAAE,CA2BjB"}
@@ -0,0 +1,78 @@
1
+ // cursor-pull: the phase-2 delta primitive over the PRODUCTION change log
2
+ // (plans/zero-server-rewrite.md "build plan for the production composition").
3
+ // maps `_zero_changes` rows since a client's cookie into put/del row patches
4
+ // for an http-pull diff response.
5
+ //
6
+ // semantics follow the reference core (src/sync-server/sync-server.ts + its
7
+ // delta suite) exactly: collect the pks TOUCHED since the cookie, dedup,
8
+ // then resolve each against LIVE table state inside the pull transaction —
9
+ // row exists → put (current values), row gone → del. old row images in the
10
+ // log are used only to find pks (DELETE + pk-changing UPDATE), never as
11
+ // patch values, so op-coalescing bugs and serializer fidelity questions
12
+ // can't exist.
13
+ //
14
+ // table identity is EXPLICIT (2026-07-09 design review, blocker 1):
15
+ // production `_zero_changes.table_name` is schema-qualified
16
+ // (`public.file`, `soot_0.clients` — pg-proxy trackingRequest emits
17
+ // `<schema>.<table>`), so `tables` is keyed by the EXACT log identity and
18
+ // carries the zero/client table name to emit. rows matching neither the
19
+ // synced spec nor the host's `skip` classifier THROW — a silently dropped
20
+ // synced change is permanent client divergence, the one failure mode this
21
+ // module must never have.
22
+ //
23
+ // completeness contract (review, high 4): the caller must pass EVERY change
24
+ // row with `watermark > cookie` up to the watermark it returns as the next
25
+ // cookie, read atomically with the live point-reads. if the host caps the
26
+ // change read, it must return the last INCLUDED watermark as the cookie —
27
+ // never the global current watermark — so the remainder ships on the next
28
+ // pull. pk mutability (review, high 5): the main pg-proxy tracking path
29
+ // records old_data=null for UPDATEs, so a pk-changing UPDATE through it
30
+ // only surfaces the NEW pk. published-table pks must be immutable (enforce
31
+ // host-side) unless the tracker learns to capture old pk images.
32
+ //
33
+ // the host (soot's project pull endpoint) owns everything else: cookie
34
+ // compare against the watermark, floor/epoch snapshot fallback, LMID rows,
35
+ // and wire value conversion (its toZeroRow).
36
+ // pks touched by a change: NEW row for INSERT/UPDATE, OLD row for
37
+ // UPDATE/DELETE (when the tracker provides old images, a pk-changing
38
+ // UPDATE touches both, deleting the old pk)
39
+ function pksOf(change, primaryKey) {
40
+ const out = [];
41
+ for (const source of [change.rowData, change.oldData]) {
42
+ if (!source)
43
+ continue;
44
+ const pk = {};
45
+ for (const col of primaryKey)
46
+ pk[col] = source[col];
47
+ out.push(pk);
48
+ }
49
+ return out;
50
+ }
51
+ export function cursorDiffPatch(opts) {
52
+ const { changes, tables, skip, readRow } = opts;
53
+ const touched = new Map();
54
+ for (const change of changes) {
55
+ const spec = tables[change.tableName];
56
+ if (!spec) {
57
+ if (skip(change.tableName))
58
+ continue;
59
+ throw new Error(`cursor-pull: change log row for unmapped table '${change.tableName}' — ` +
60
+ `synced tables must be in the spec and internal tables must be classified by skip(); ` +
61
+ `silently dropping it would permanently diverge clients`);
62
+ }
63
+ for (const pk of pksOf(change, spec.primaryKey)) {
64
+ const key = `${spec.clientName} ${JSON.stringify(spec.primaryKey.map((c) => pk[c]))}`;
65
+ touched.set(key, { clientName: spec.clientName, pk });
66
+ }
67
+ }
68
+ const patch = [];
69
+ for (const { clientName, pk } of touched.values()) {
70
+ const row = readRow(clientName, pk);
71
+ if (row === undefined)
72
+ patch.push({ op: 'del', tableName: clientName, pk });
73
+ else
74
+ patch.push({ op: 'put', tableName: clientName, row });
75
+ }
76
+ return patch;
77
+ }
78
+ //# sourceMappingURL=cursor-pull.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor-pull.js","sourceRoot":"","sources":["../../src/cf-do/cursor-pull.ts"],"names":[],"mappings":"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"}
@@ -129,6 +129,7 @@ export declare class DoBackend {
129
129
  private generatedIndexName;
130
130
  private uniqueIndexName;
131
131
  private sqliteIndexColumnsFromRows;
132
+ private promotedUniqueIndexKey;
132
133
  private tableIndexInfos;
133
134
  private publicationContainsTable;
134
135
  private publicationsForTable;
@@ -1 +1 @@
1
- {"version":3,"file":"pg-proxy-do-backend.d.ts","sourceRoot":"","sources":["../src/pg-proxy-do-backend.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pg-proxy-do-backend.d.ts","sourceRoot":"","sources":["../src/pg-proxy-do-backend.ts"],"names":[],"mappings":"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"}
@@ -6262,6 +6262,54 @@ export class DoBackend {
6262
6262
  columns[row.name] = row.desc === 1 ? 'DESC' : 'ASC';
6263
6263
  return columns;
6264
6264
  }
6265
+ // a table with no PRIMARY KEY but a full, non-partial UNIQUE index is keyed
6266
+ // by that index — the shape soot's DDL generator emits for composite drizzle
6267
+ // primaryKey() tables (CREATE TABLE without a PK + CREATE UNIQUE INDEX
6268
+ // <table>_pkey), because a PK cannot be retrofitted onto an existing sqlite
6269
+ // table. real pg conveys this key via replica identity; without promoting it
6270
+ // here, zero's initial sync builds a keyless replica spec and its change
6271
+ // processor throws "Cannot replicate table without a PRIMARY KEY or UNIQUE
6272
+ // INDEX" on the first UPDATE (2026-07-10 soot prod outage).
6273
+ //
6274
+ // the <table>_pkey name IS the generator's primary-key convention, so it is
6275
+ // trusted outright: legacy tables created before NOT NULL reached their DDL
6276
+ // have nullable physical columns (and possibly no durable metadata), yet the
6277
+ // index still represents the app-level composite PK — requiring NOT NULL
6278
+ // there would silently skip exactly the tables this promotion exists to
6279
+ // heal. any OTHER unique index must cover only NOT NULL columns (a unique
6280
+ // index over nullable columns permits duplicate NULL rows and is not a row
6281
+ // identity); among those, narrowest wins, name as the tiebreak.
6282
+ promotedUniqueIndexKey(sqliteTableName, columns, indexListRows, indexColumnsByName) {
6283
+ const columnNames = new Set(columns.map((column) => column.name));
6284
+ const notNull = new Set(columns
6285
+ .filter((column) => {
6286
+ const metadata = this.schemaMetadata.get(sqliteTableName)?.get(column.name);
6287
+ return metadata?.notNull || metadata?.primaryKey || column.notnull || column.pk;
6288
+ })
6289
+ .map((column) => column.name));
6290
+ const ref = this.tableRefForSqliteTable(sqliteTableName);
6291
+ const pkeyName = `${ref.tableName}_pkey`;
6292
+ const candidates = [];
6293
+ for (const row of indexListRows) {
6294
+ const name = String(row.name ?? '');
6295
+ if (!name || !Number(row.unique ?? 0) || Number(row.partial ?? 0))
6296
+ continue;
6297
+ const indexColumns = Object.keys(indexColumnsByName.get(name) ?? {});
6298
+ if (indexColumns.length === 0)
6299
+ continue;
6300
+ if (!indexColumns.every((column) => columnNames.has(column)))
6301
+ continue;
6302
+ if (name === pkeyName)
6303
+ return indexColumns;
6304
+ if (!indexColumns.every((column) => notNull.has(column)))
6305
+ continue;
6306
+ candidates.push({ name, columns: indexColumns });
6307
+ }
6308
+ if (candidates.length === 0)
6309
+ return [];
6310
+ candidates.sort((a, b) => a.columns.length - b.columns.length || a.name.localeCompare(b.name));
6311
+ return candidates[0].columns;
6312
+ }
6265
6313
  tableIndexInfos(table, columns, primaryKey, indexListRows, indexColumnsByName) {
6266
6314
  const usedNames = new Set();
6267
6315
  const seenSignatures = new Set();
@@ -6387,17 +6435,21 @@ export class DoBackend {
6387
6435
  const metadataPrimaryKey = columns
6388
6436
  .filter((column) => this.schemaMetadata.get(table.name)?.get(column.name)?.primaryKey)
6389
6437
  .map((column) => column.name);
6438
+ const sqlitePrimaryKey = columns
6439
+ .filter((column) => column.pk > 0)
6440
+ .sort((a, b) => a.pk - b.pk)
6441
+ .map((column) => column.name);
6442
+ const primaryKey = metadataPrimaryKey.length > 0
6443
+ ? metadataPrimaryKey
6444
+ : sqlitePrimaryKey.length > 0
6445
+ ? sqlitePrimaryKey
6446
+ : this.promotedUniqueIndexKey(table.name, columns, pragmaResults[i * 2 + 1]?.rows ?? [], indexColumnsByName);
6390
6447
  const info = {
6391
6448
  name: table.name,
6392
6449
  schema: ref.schema,
6393
6450
  tableName: ref.tableName,
6394
6451
  columns,
6395
- primaryKey: metadataPrimaryKey.length > 0
6396
- ? metadataPrimaryKey
6397
- : columns
6398
- .filter((column) => column.pk > 0)
6399
- .sort((a, b) => a.pk - b.pk)
6400
- .map((column) => column.name),
6452
+ primaryKey,
6401
6453
  indexes: [],
6402
6454
  };
6403
6455
  info.indexes = this.tableIndexInfos(ref, columns, info.primaryKey, pragmaResults[i * 2 + 1]?.rows ?? [], indexColumnsByName);
@@ -6606,7 +6658,16 @@ export class DoBackend {
6606
6658
  elemPgTypeClass: metadata?.elemTyptype ?? null,
6607
6659
  typeOID: metadata?.typeOid ?? pgTypeOid(dataType),
6608
6660
  characterMaximumLength: metadata?.characterMaximumLength ?? null,
6609
- notNull: Boolean(metadata?.notNull || metadata?.primaryKey || column.notnull || column.pk),
6661
+ // primary-key columns are never-null by definition; legacy
6662
+ // tables whose key was promoted from a <table>_pkey unique
6663
+ // index carry nullable physical columns, and zero's
6664
+ // view-syncer refuses to sync a table whose key columns are
6665
+ // nullable on the replica (checkClientSchema).
6666
+ notNull: Boolean(metadata?.notNull ||
6667
+ metadata?.primaryKey ||
6668
+ column.notnull ||
6669
+ column.pk ||
6670
+ info.primaryKey.includes(column.name)),
6610
6671
  dflt: null,
6611
6672
  },
6612
6673
  ];