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.
- package/dist/cf-do/cursor-pull.d.ts +27 -0
- package/dist/cf-do/cursor-pull.d.ts.map +1 -0
- package/dist/cf-do/cursor-pull.js +78 -0
- package/dist/cf-do/cursor-pull.js.map +1 -0
- package/dist/pg-proxy-do-backend.d.ts +1 -0
- package/dist/pg-proxy-do-backend.d.ts.map +1 -1
- package/dist/pg-proxy-do-backend.js +68 -7
- package/dist/pg-proxy-do-backend.js.map +1 -1
- package/dist/sync-server/sync-server.d.ts +77 -0
- package/dist/sync-server/sync-server.d.ts.map +1 -0
- package/dist/sync-server/sync-server.js +385 -0
- package/dist/sync-server/sync-server.js.map +1 -0
- package/dist/worker/zero-sql-write-circuit.d.ts.map +1 -1
- package/dist/worker/zero-sql-write-circuit.js +10 -2
- package/dist/worker/zero-sql-write-circuit.js.map +1 -1
- package/package.json +2 -2
|
@@ -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
|
|
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
|
-
|
|
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
|
];
|