firebird-wasm 0.7.0 → 0.8.0

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 (72) hide show
  1. package/CHANGELOG.md +141 -0
  2. package/README.md +1 -1
  3. package/dist/.tsbuildinfo.build +1 -1
  4. package/dist/browser/engine-transport.d.ts.map +1 -1
  5. package/dist/browser/engine-transport.js +5 -8
  6. package/dist/browser/engine-transport.js.map +1 -1
  7. package/dist/browser/firebird-browser.d.ts.map +1 -1
  8. package/dist/browser/firebird-browser.js +59 -20
  9. package/dist/browser/firebird-browser.js.map +1 -1
  10. package/dist/browser/index.d.ts +2 -2
  11. package/dist/browser/index.d.ts.map +1 -1
  12. package/dist/browser/index.js +4 -2
  13. package/dist/browser/index.js.map +1 -1
  14. package/dist/browser/serve-engine.d.ts +30 -0
  15. package/dist/browser/serve-engine.d.ts.map +1 -0
  16. package/dist/browser/serve-engine.js +93 -0
  17. package/dist/browser/serve-engine.js.map +1 -0
  18. package/dist/browser/worker-entry.d.ts +8 -22
  19. package/dist/browser/worker-entry.d.ts.map +1 -1
  20. package/dist/browser/worker-entry.js +18 -79
  21. package/dist/browser/worker-entry.js.map +1 -1
  22. package/dist/electric/apply.d.ts +53 -0
  23. package/dist/electric/apply.d.ts.map +1 -0
  24. package/dist/electric/apply.js +88 -0
  25. package/dist/electric/apply.js.map +1 -0
  26. package/dist/electric/index.d.ts +83 -0
  27. package/dist/electric/index.d.ts.map +1 -0
  28. package/dist/electric/index.js +210 -0
  29. package/dist/electric/index.js.map +1 -0
  30. package/dist/electric/values.d.ts +65 -0
  31. package/dist/electric/values.d.ts.map +1 -0
  32. package/dist/electric/values.js +243 -0
  33. package/dist/electric/values.js.map +1 -0
  34. package/dist/electric-server/capture.d.ts +36 -0
  35. package/dist/electric-server/capture.d.ts.map +1 -0
  36. package/dist/electric-server/capture.js +148 -0
  37. package/dist/electric-server/capture.js.map +1 -0
  38. package/dist/electric-server/catalog.d.ts +59 -0
  39. package/dist/electric-server/catalog.d.ts.map +1 -0
  40. package/dist/electric-server/catalog.js +122 -0
  41. package/dist/electric-server/catalog.js.map +1 -0
  42. package/dist/electric-server/index.d.ts +58 -0
  43. package/dist/electric-server/index.d.ts.map +1 -0
  44. package/dist/electric-server/index.js +348 -0
  45. package/dist/electric-server/index.js.map +1 -0
  46. package/dist/firebird.d.ts +3 -1
  47. package/dist/firebird.d.ts.map +1 -1
  48. package/dist/firebird.js +250 -22
  49. package/dist/firebird.js.map +1 -1
  50. package/dist/frameworks/query-store.d.ts +99 -0
  51. package/dist/frameworks/query-store.d.ts.map +1 -0
  52. package/dist/frameworks/query-store.js +166 -0
  53. package/dist/frameworks/query-store.js.map +1 -0
  54. package/dist/react/index.d.ts +68 -0
  55. package/dist/react/index.d.ts.map +1 -0
  56. package/dist/react/index.js +92 -0
  57. package/dist/react/index.js.map +1 -0
  58. package/dist/repl/index.d.ts +76 -0
  59. package/dist/repl/index.d.ts.map +1 -0
  60. package/dist/repl/index.js +328 -0
  61. package/dist/repl/index.js.map +1 -0
  62. package/dist/types.d.ts +2 -1
  63. package/dist/types.d.ts.map +1 -1
  64. package/dist/version.d.ts +1 -1
  65. package/dist/version.js +1 -1
  66. package/dist/vue/index.d.ts +68 -0
  67. package/dist/vue/index.d.ts.map +1 -0
  68. package/dist/vue/index.js +107 -0
  69. package/dist/vue/index.js.map +1 -0
  70. package/dist/wasm/firebird-embedded-st.wasm +0 -0
  71. package/dist/wasm/firebird-embedded.wasm +0 -0
  72. package/package.json +50 -3
@@ -0,0 +1,53 @@
1
+ /**
2
+ * apply.ts – one batch of Electric's shape log, applied in one transaction.
3
+ *
4
+ * Electric's rule is to apply operations only at `up-to-date`, as a whole:
5
+ * the store then always matches some state Postgres was in. The caller runs
6
+ * this inside a Firebird transaction that also records the sync position, so
7
+ * the rows and the position commit together or not at all.
8
+ */
9
+ import { firebirdColumn } from './values';
10
+ import type { ElectricSchema } from './values';
11
+ /** What applying needs of a transaction: `FirebirdBrowserTransaction` has it. */
12
+ export interface ElectricTransaction {
13
+ exec(sql: string, params?: unknown[]): Promise<{
14
+ affectedRows: number;
15
+ }>;
16
+ execBatch(sql: string, rows: unknown[][]): Promise<{
17
+ affectedRows: number;
18
+ }>;
19
+ query(sql: string, params?: unknown[]): Promise<{
20
+ rows: Array<Record<string, unknown>>;
21
+ }>;
22
+ }
23
+ /** An operation, or the reset a `must-refetch` asks for, in log order. */
24
+ export type LogEntry = {
25
+ kind: 'insert' | 'update' | 'delete';
26
+ value: Record<string, unknown>;
27
+ } | {
28
+ kind: 'reset';
29
+ };
30
+ /** Where the rows go. */
31
+ export interface ApplyTarget {
32
+ /** The Firebird table, quoted. */
33
+ table: string;
34
+ /** Primary key columns, Postgres names, in key order. */
35
+ primaryKey: string[];
36
+ /** The Firebird column for a Postgres column, quoted. */
37
+ column: (postgresName: string) => string;
38
+ schema: ElectricSchema;
39
+ }
40
+ /** The default column mapping. */
41
+ export declare const defaultColumn: typeof firebirdColumn;
42
+ /**
43
+ * Apply `entries` through `tx`.
44
+ *
45
+ * - an insert is `UPDATE OR INSERT … MATCHING (key)`, so one seen twice (a
46
+ * replayed batch, a resync over rows already there) does no harm; a run of
47
+ * inserts with the same columns goes through one prepared batch;
48
+ * - an update carries the key and the changed columns, and sets those;
49
+ * - a delete carries the key;
50
+ * - a reset empties the table, for the snapshot that follows to refill it.
51
+ */
52
+ export declare function applyEntries(tx: ElectricTransaction, target: ApplyTarget, entries: LogEntry[]): Promise<void>;
53
+ //# sourceMappingURL=apply.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../../src/electric/apply.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,cAAc,EAAc,MAAM,UAAU,CAAC;AACtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE/C,iFAAiF;AACjF,MAAM,WAAW,mBAAmB;IAClC,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;QAAE,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACzE,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,GAAG,OAAO,CAAC;QAAE,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC7E,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;KAAE,CAAC,CAAC;CAC3F;AAED,0EAA0E;AAC1E,MAAM,MAAM,QAAQ,GAChB;IAAE,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GACxE;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC;AAEtB,yBAAyB;AACzB,MAAM,WAAW,WAAW;IAC1B,kCAAkC;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,yDAAyD;IACzD,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,yDAAyD;IACzD,MAAM,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,MAAM,CAAC;IACzC,MAAM,EAAE,cAAc,CAAC;CACxB;AAED,kCAAkC;AAClC,eAAO,MAAM,aAAa,uBAAiB,CAAC;AAE5C;;;;;;;;;GASG;AACH,wBAAsB,YAAY,CAChC,EAAE,EAAE,mBAAmB,EACvB,MAAM,EAAE,WAAW,EACnB,OAAO,EAAE,QAAQ,EAAE,GAClB,OAAO,CAAC,IAAI,CAAC,CAqDf"}
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ /**
3
+ * apply.ts – one batch of Electric's shape log, applied in one transaction.
4
+ *
5
+ * Electric's rule is to apply operations only at `up-to-date`, as a whole:
6
+ * the store then always matches some state Postgres was in. The caller runs
7
+ * this inside a Firebird transaction that also records the sync position, so
8
+ * the rows and the position commit together or not at all.
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.defaultColumn = void 0;
12
+ exports.applyEntries = applyEntries;
13
+ const values_1 = require("./values");
14
+ /** The default column mapping. */
15
+ exports.defaultColumn = values_1.firebirdColumn;
16
+ /**
17
+ * Apply `entries` through `tx`.
18
+ *
19
+ * - an insert is `UPDATE OR INSERT … MATCHING (key)`, so one seen twice (a
20
+ * replayed batch, a resync over rows already there) does no harm; a run of
21
+ * inserts with the same columns goes through one prepared batch;
22
+ * - an update carries the key and the changed columns, and sets those;
23
+ * - a delete carries the key;
24
+ * - a reset empties the table, for the snapshot that follows to refill it.
25
+ */
26
+ async function applyEntries(tx, target, entries) {
27
+ const key = target.primaryKey;
28
+ const keyWhere = key.map((c) => `${target.column(c)} = ?`).join(' AND ');
29
+ const keyValues = (value) => key.map((c) => {
30
+ if (!(c in value)) {
31
+ throw new Error(`A message for ${target.table} has no value for its key column ${c}`);
32
+ }
33
+ return convert(target, c, value[c]);
34
+ });
35
+ let pending = null;
36
+ const flush = async () => {
37
+ if (!pending)
38
+ return;
39
+ const { columns, rows } = pending;
40
+ pending = null;
41
+ const sql = `UPDATE OR INSERT INTO ${target.table} (${columns.map(target.column).join(', ')}) ` +
42
+ `VALUES (${columns.map(() => '?').join(', ')}) ` +
43
+ `MATCHING (${key.map(target.column).join(', ')})`;
44
+ if (rows.length === 1)
45
+ await tx.exec(sql, rows[0]);
46
+ else
47
+ await tx.execBatch(sql, rows);
48
+ };
49
+ for (const entry of entries) {
50
+ if (entry.kind === 'insert') {
51
+ const columns = Object.keys(entry.value);
52
+ const row = columns.map((c) => convert(target, c, entry.value[c]));
53
+ if (pending && sameColumns(pending.columns, columns)) {
54
+ pending.rows.push(row);
55
+ }
56
+ else {
57
+ await flush();
58
+ pending = { columns, rows: [row] };
59
+ }
60
+ continue;
61
+ }
62
+ await flush();
63
+ if (entry.kind === 'reset') {
64
+ await tx.exec(`DELETE FROM ${target.table}`);
65
+ }
66
+ else if (entry.kind === 'update') {
67
+ const changed = Object.keys(entry.value).filter((c) => !key.includes(c));
68
+ if (changed.length === 0)
69
+ continue;
70
+ await tx.exec(`UPDATE ${target.table} SET ${changed.map((c) => `${target.column(c)} = ?`).join(', ')} WHERE ${keyWhere}`, [...changed.map((c) => convert(target, c, entry.value[c])), ...keyValues(entry.value)]);
71
+ }
72
+ else {
73
+ await tx.exec(`DELETE FROM ${target.table} WHERE ${keyWhere}`, keyValues(entry.value));
74
+ }
75
+ }
76
+ await flush();
77
+ }
78
+ function convert(target, column, value) {
79
+ const info = target.schema[column];
80
+ if (!info) {
81
+ throw new Error(`Column ${column} is not in the shape's schema`);
82
+ }
83
+ return (0, values_1.toFirebird)(value, column, info);
84
+ }
85
+ function sameColumns(a, b) {
86
+ return a.length === b.length && a.every((c, i) => c === b[i]);
87
+ }
88
+ //# sourceMappingURL=apply.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply.js","sourceRoot":"","sources":["../../src/electric/apply.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * firebird-wasm/electric – a Postgres table, through Electric, into Firebird.
3
+ *
4
+ * ```ts
5
+ * import { syncShapeToTable } from 'firebird-wasm/electric';
6
+ *
7
+ * await db.exec('CREATE TABLE items (id INTEGER PRIMARY KEY, title VARCHAR(100))');
8
+ * const sync = await syncShapeToTable(db, {
9
+ * shape: { url: 'https://electric.example.com/v1/shape', params: { table: 'items' } },
10
+ * table: 'items',
11
+ * shapeKey: 'items',
12
+ * });
13
+ * await sync.upToDate; // the first complete copy is in
14
+ * await sync.unsubscribe();
15
+ * ```
16
+ *
17
+ * Read-only: Electric streams changes out of Postgres and takes no writes,
18
+ * which go through the app's own API and come back down the shape. Each
19
+ * batch Electric marks `up-to-date` is applied in one Firebird transaction
20
+ * with the sync position, so a reload resumes exactly where the database is.
21
+ * See docs/plans/electric-sync.md for the design and docs/integration.md for
22
+ * use.
23
+ *
24
+ * `@electric-sql/client` is an optional peer dependency: install it to use
25
+ * this module.
26
+ */
27
+ import { ShapeStream } from '@electric-sql/client';
28
+ import type { ShapeStreamOptions } from '@electric-sql/client';
29
+ import type { ElectricTransaction } from './apply';
30
+ export { tableFromSchema, checkSchema, toFirebird, firebirdColumn, firebirdName } from './values';
31
+ export type { ElectricSchema, ElectricColumn } from './values';
32
+ export type { ElectricTransaction } from './apply';
33
+ /** What the sync needs of a database: `FirebirdBrowser` has it. */
34
+ export interface ElectricDatabase {
35
+ transaction<T>(fn: (tx: ElectricTransaction) => Promise<T>): Promise<T>;
36
+ }
37
+ /** Options of {@link syncShapeToTable}. */
38
+ export interface SyncShapeToTableOptions {
39
+ /**
40
+ * The shape, as `ShapeStream` takes it: `url` of Electric's `/v1/shape`
41
+ * (or a proxy in front of it), and `params` with at least `table`.
42
+ */
43
+ shape: Omit<ShapeStreamOptions, 'parser' | 'transformer' | 'columnMapper' | 'offset' | 'handle'>;
44
+ /** The Firebird table the rows go to, as written in Firebird SQL. */
45
+ table: string;
46
+ /**
47
+ * Where the sync position is kept, so that the next sync resumes rather
48
+ * than starting over; `null` starts over every time (and empties the
49
+ * table first). One shape per key, and one shape per table.
50
+ */
51
+ shapeKey: string | null;
52
+ /** The primary key, as Postgres column names. Default: the shape's own. */
53
+ primaryKey?: string[];
54
+ /** Firebird column names for Postgres columns, where the default mapping does not fit. */
55
+ columns?: Record<string, string>;
56
+ /**
57
+ * The event posted once per applied batch, delivered when it commits, so
58
+ * `useLiveQuery` and `db.live()` can follow the sync without a trigger.
59
+ * Default `electric_<shapeKey>`, or `electric_<table>` without a key.
60
+ */
61
+ event?: string | null;
62
+ /** Called when the sync stops on an error. Default: console.error. */
63
+ onError?: (error: Error) => void;
64
+ /** For tests: the ShapeStream class to use. */
65
+ ShapeStream?: typeof ShapeStream;
66
+ }
67
+ /** A running sync. Returned by {@link syncShapeToTable}. */
68
+ export interface ShapeSync {
69
+ /** Resolves when the first complete copy of the shape is in; rejects if the sync stops before. */
70
+ readonly upToDate: Promise<void>;
71
+ /** True once a batch marked up-to-date has been applied. */
72
+ readonly isUpToDate: boolean;
73
+ /** Why the sync stopped, if it stopped on an error. */
74
+ readonly error: Error | undefined;
75
+ /** Stop syncing. The rows and the position stay; a later sync resumes. */
76
+ unsubscribe(): Promise<void>;
77
+ }
78
+ /**
79
+ * Keep `options.table` in step with a shape: an initial copy, then every
80
+ * change, until {@link ShapeSync.unsubscribe}. The table must exist.
81
+ */
82
+ export declare function syncShapeToTable(db: ElectricDatabase, options: SyncShapeToTableOptions): Promise<ShapeSync>;
83
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/electric/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,WAAW,EAAqC,MAAM,sBAAsB,CAAC;AACtF,OAAO,KAAK,EAA+B,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE5F,OAAO,KAAK,EAAE,mBAAmB,EAAY,MAAM,SAAS,CAAC;AAI7D,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,UAAU,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAClG,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC/D,YAAY,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAEnD,mEAAmE;AACnE,MAAM,WAAW,gBAAgB;IAC/B,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,mBAAmB,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CACzE;AAED,2CAA2C;AAC3C,MAAM,WAAW,uBAAuB;IACtC;;;OAGG;IACH,KAAK,EAAE,IAAI,CAAC,kBAAkB,EAAE,QAAQ,GAAG,aAAa,GAAG,cAAc,GAAG,QAAQ,GAAG,QAAQ,CAAC,CAAC;IACjG,qEAAqE;IACrE,KAAK,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,4EAA4E;IAC5E,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,0FAA0F;IAC1F,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,uEAAuE;IACvE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,+CAA+C;IAC/C,WAAW,CAAC,EAAE,OAAO,WAAW,CAAC;CAClC;AAED,6DAA6D;AAC7D,MAAM,WAAW,SAAS;IACxB,kGAAkG;IAClG,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,4DAA4D;IAC5D,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,uDAAuD;IACvD,QAAQ,CAAC,KAAK,EAAE,KAAK,GAAG,SAAS,CAAC;IAClC,2EAA2E;IAC3E,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAUD;;;GAGG;AACH,wBAAsB,gBAAgB,CACpC,EAAE,EAAE,gBAAgB,EACpB,OAAO,EAAE,uBAAuB,GAC/B,OAAO,CAAC,SAAS,CAAC,CAqKpB"}
@@ -0,0 +1,210 @@
1
+ "use strict";
2
+ /**
3
+ * firebird-wasm/electric – a Postgres table, through Electric, into Firebird.
4
+ *
5
+ * ```ts
6
+ * import { syncShapeToTable } from 'firebird-wasm/electric';
7
+ *
8
+ * await db.exec('CREATE TABLE items (id INTEGER PRIMARY KEY, title VARCHAR(100))');
9
+ * const sync = await syncShapeToTable(db, {
10
+ * shape: { url: 'https://electric.example.com/v1/shape', params: { table: 'items' } },
11
+ * table: 'items',
12
+ * shapeKey: 'items',
13
+ * });
14
+ * await sync.upToDate; // the first complete copy is in
15
+ * await sync.unsubscribe();
16
+ * ```
17
+ *
18
+ * Read-only: Electric streams changes out of Postgres and takes no writes,
19
+ * which go through the app's own API and come back down the shape. Each
20
+ * batch Electric marks `up-to-date` is applied in one Firebird transaction
21
+ * with the sync position, so a reload resumes exactly where the database is.
22
+ * See docs/plans/electric-sync.md for the design and docs/integration.md for
23
+ * use.
24
+ *
25
+ * `@electric-sql/client` is an optional peer dependency: install it to use
26
+ * this module.
27
+ */
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.firebirdName = exports.firebirdColumn = exports.toFirebird = exports.checkSchema = exports.tableFromSchema = void 0;
30
+ exports.syncShapeToTable = syncShapeToTable;
31
+ const client_1 = require("@electric-sql/client");
32
+ const apply_1 = require("./apply");
33
+ const values_1 = require("./values");
34
+ var values_2 = require("./values");
35
+ Object.defineProperty(exports, "tableFromSchema", { enumerable: true, get: function () { return values_2.tableFromSchema; } });
36
+ Object.defineProperty(exports, "checkSchema", { enumerable: true, get: function () { return values_2.checkSchema; } });
37
+ Object.defineProperty(exports, "toFirebird", { enumerable: true, get: function () { return values_2.toFirebird; } });
38
+ Object.defineProperty(exports, "firebirdColumn", { enumerable: true, get: function () { return values_2.firebirdColumn; } });
39
+ Object.defineProperty(exports, "firebirdName", { enumerable: true, get: function () { return values_2.firebirdName; } });
40
+ const METADATA = '"ELECTRIC_SHAPES"';
41
+ /** Electric's own parsing, turned off for the types it parses: values stay Postgres text. */
42
+ const IDENTITY = (value) => value;
43
+ const KEEP_TEXT = Object.fromEntries(['int2', 'int4', 'int8', 'float4', 'float8', 'bool', 'json', 'jsonb'].map((t) => [t, IDENTITY]));
44
+ /**
45
+ * Keep `options.table` in step with a shape: an initial copy, then every
46
+ * change, until {@link ShapeSync.unsubscribe}. The table must exist.
47
+ */
48
+ async function syncShapeToTable(db, options) {
49
+ const table = (0, values_1.firebirdName)(options.table);
50
+ const column = (name) => options.columns?.[name] !== undefined ? (0, values_1.firebirdName)(options.columns[name]) : (0, apply_1.defaultColumn)(name);
51
+ const eventName = options.event === undefined
52
+ ? `electric_${options.shapeKey ?? options.table}`
53
+ : options.event;
54
+ const report = options.onError ?? ((e) => console.error('[firebird-wasm/electric] sync stopped', e));
55
+ // Where a previous sync stopped.
56
+ let position;
57
+ // Its own transaction: Firebird applies DDL at commit, so a table made in
58
+ // a transaction cannot be read in that same transaction.
59
+ await db.transaction((tx) => ensureMetadata(tx));
60
+ await db.transaction(async (tx) => {
61
+ if (options.shapeKey !== null) {
62
+ const { rows } = await tx.query(`SELECT SHAPE_HANDLE, SHAPE_OFFSET, SHAPE_SCHEMA FROM ${METADATA} WHERE SHAPE_KEY = ?`, [options.shapeKey]);
63
+ const row = rows[0];
64
+ if (row && row['SHAPE_HANDLE'] && row['SHAPE_OFFSET']) {
65
+ position = {
66
+ handle: String(row['SHAPE_HANDLE']),
67
+ offset: String(row['SHAPE_OFFSET']),
68
+ schema: JSON.parse(String(row['SHAPE_SCHEMA'] ?? '{}')),
69
+ };
70
+ }
71
+ }
72
+ });
73
+ let schema = position?.schema;
74
+ let checked = false;
75
+ // Operations received since the last up-to-date; applied together at the next.
76
+ let pending = options.shapeKey === null || !position ? [{ kind: 'reset' }] : [];
77
+ let upToDate = false;
78
+ let failure;
79
+ let stopped = false;
80
+ let applying = Promise.resolve();
81
+ let resolveReady;
82
+ let rejectReady;
83
+ const ready = new Promise((resolve, reject) => {
84
+ resolveReady = resolve;
85
+ rejectReady = reject;
86
+ });
87
+ ready.catch(() => undefined);
88
+ const abort = new AbortController();
89
+ const userSignal = options.shape.signal;
90
+ if (userSignal)
91
+ userSignal.addEventListener('abort', () => abort.abort(), { once: true });
92
+ // The schema arrives as a header, which the client does not hand on.
93
+ const baseFetch = options.shape.fetchClient ?? ((...args) => fetch(...args));
94
+ const fetchClient = async (input, init) => {
95
+ const response = await baseFetch(input, init);
96
+ const header = response.headers.get('electric-schema');
97
+ if (header) {
98
+ schema = JSON.parse(header);
99
+ checked = false;
100
+ }
101
+ return response;
102
+ };
103
+ const Stream = options.ShapeStream ?? client_1.ShapeStream;
104
+ const stream = new Stream({
105
+ ...options.shape,
106
+ signal: abort.signal,
107
+ fetchClient,
108
+ parser: KEEP_TEXT,
109
+ ...(position ? { handle: position.handle, offset: position.offset } : {}),
110
+ onError: (error) => {
111
+ // The client has already retried what it retries; this is final.
112
+ void stop(error);
113
+ },
114
+ });
115
+ const stop = async (error) => {
116
+ if (stopped)
117
+ return;
118
+ stopped = true;
119
+ abort.abort();
120
+ stream.unsubscribeAll();
121
+ if (error) {
122
+ failure = error;
123
+ rejectReady(error);
124
+ report(error);
125
+ }
126
+ };
127
+ const apply = async (entries) => {
128
+ if (!schema)
129
+ throw new Error('Electric sent rows before a schema; the sync cannot map them');
130
+ if (!checked) {
131
+ (0, values_1.checkSchema)(schema);
132
+ checked = true;
133
+ }
134
+ const target = {
135
+ table,
136
+ column,
137
+ schema,
138
+ primaryKey: options.primaryKey ?? (0, values_1.primaryKeyOf)(schema),
139
+ };
140
+ if (target.primaryKey.length === 0) {
141
+ throw new Error(`The shape of ${options.table} has no primary key; pass primaryKey`);
142
+ }
143
+ const handle = stream.shapeHandle;
144
+ const offset = String(stream.lastOffset);
145
+ const schemaText = JSON.stringify(schema);
146
+ await db.transaction(async (tx) => {
147
+ await (0, apply_1.applyEntries)(tx, target, entries);
148
+ if (options.shapeKey !== null && handle) {
149
+ await tx.exec(`UPDATE OR INSERT INTO ${METADATA} (SHAPE_KEY, SHAPE_HANDLE, SHAPE_OFFSET, SHAPE_SCHEMA) ` +
150
+ 'VALUES (?, ?, ?, ?) MATCHING (SHAPE_KEY)', [options.shapeKey, handle, offset, schemaText]);
151
+ }
152
+ if (eventName) {
153
+ await tx.exec('EXECUTE BLOCK (event_name VARCHAR(127) = ?) AS BEGIN POST_EVENT :event_name; END', [eventName]);
154
+ }
155
+ });
156
+ };
157
+ stream.subscribe(async (messages) => {
158
+ if (stopped)
159
+ return;
160
+ for (const message of messages) {
161
+ if ((0, client_1.isChangeMessage)(message)) {
162
+ pending.push({ kind: message.headers.operation, value: message.value });
163
+ }
164
+ else if ((0, client_1.isControlMessage)(message) && message.headers.control === 'must-refetch') {
165
+ // What came before belongs to a shape that no longer exists.
166
+ pending = [{ kind: 'reset' }];
167
+ }
168
+ else if ((0, client_1.isControlMessage)(message) && message.headers.control === 'up-to-date') {
169
+ const entries = pending;
170
+ pending = [];
171
+ applying = applying.then(() => apply(entries));
172
+ try {
173
+ await applying;
174
+ }
175
+ catch (err) {
176
+ await stop(err instanceof Error ? err : new Error(String(err)));
177
+ return;
178
+ }
179
+ if (!upToDate) {
180
+ upToDate = true;
181
+ resolveReady();
182
+ }
183
+ }
184
+ }
185
+ });
186
+ return {
187
+ upToDate: ready,
188
+ get isUpToDate() {
189
+ return upToDate;
190
+ },
191
+ get error() {
192
+ return failure;
193
+ },
194
+ unsubscribe: async () => {
195
+ await stop();
196
+ await applying.catch(() => undefined);
197
+ rejectReady(new Error('The sync was stopped before it was up to date'));
198
+ },
199
+ };
200
+ }
201
+ /** The table holding each shape's position, made on first use. */
202
+ async function ensureMetadata(tx) {
203
+ const { rows } = await tx.query("SELECT 1 AS X FROM RDB$RELATIONS WHERE RDB$RELATION_NAME = 'ELECTRIC_SHAPES'");
204
+ if (rows.length > 0)
205
+ return;
206
+ await tx.exec(`CREATE TABLE ${METADATA} (` +
207
+ 'SHAPE_KEY VARCHAR(255) NOT NULL PRIMARY KEY, ' +
208
+ 'SHAPE_HANDLE VARCHAR(255), SHAPE_OFFSET VARCHAR(64), SHAPE_SCHEMA BLOB SUB_TYPE TEXT)');
209
+ }
210
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/electric/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * values.ts – Postgres values, as Electric sends them, into Firebird.
3
+ *
4
+ * Electric sends every value as Postgres's text output and describes the
5
+ * columns once, in the `electric-schema` response header. This turns one
6
+ * into a parameter Firebird converts exactly, and refuses what it does not
7
+ * map by name rather than storing something that looks right and is not.
8
+ *
9
+ * Observed from Electric 1.8 (not all of it in the documentation): a boolean
10
+ * is `"true"` in the initial snapshot and `"f"` in a live update; a
11
+ * `timestamptz` arrives in UTC as `2024-05-01 10:00:00+00`; arrays are marked
12
+ * `dims`, and the primary key `pk_index`.
13
+ */
14
+ /** One column of `electric-schema`. */
15
+ export interface ElectricColumn {
16
+ type: string;
17
+ dims?: number;
18
+ pk_index?: number;
19
+ not_null?: boolean;
20
+ max_length?: number;
21
+ length?: number;
22
+ precision?: number;
23
+ scale?: number;
24
+ }
25
+ /** The `electric-schema` header, parsed: Postgres column name to its type. */
26
+ export type ElectricSchema = Record<string, ElectricColumn>;
27
+ /**
28
+ * Check that every column of `schema` can be stored, before any row is.
29
+ * @throws naming each column and type that cannot.
30
+ */
31
+ export declare function checkSchema(schema: ElectricSchema): void;
32
+ /**
33
+ * One value from Electric as a Firebird parameter.
34
+ *
35
+ * `value` is Electric's text, `null`, or for an array column the array the
36
+ * client parsed (of texts), which is stored as JSON.
37
+ */
38
+ export declare function toFirebird(value: unknown, column: string, info: ElectricColumn): unknown;
39
+ /**
40
+ * The Firebird identifier for a Postgres column, quoted.
41
+ *
42
+ * Postgres folds unquoted names to lower case and Firebird to upper, so a
43
+ * plain lower-case Postgres name maps to the upper-case Firebird name that
44
+ * the same unquoted word finds (`title` → `"TITLE"`). Anything else was
45
+ * quoted in Postgres to exist and stays exactly as it is (`"camelCase"`).
46
+ */
47
+ export declare function firebirdColumn(postgresName: string): string;
48
+ /**
49
+ * A table or column name as written in Firebird SQL, quoted: `items` and
50
+ * `ITEMS` are `"ITEMS"`; a name given in double quotes keeps its case.
51
+ */
52
+ export declare function firebirdName(name: string): string;
53
+ /**
54
+ * `CREATE TABLE` for a shape's columns, to read and adjust before running.
55
+ *
56
+ * Unbounded text becomes `BLOB SUB_TYPE TEXT`, which cannot be part of a key
57
+ * or an index; a text primary key gets `VARCHAR(255)` instead, and is worth
58
+ * a look. Nothing here runs the statement: what types a table should have
59
+ * is the app's call, and a later change to the Postgres table does not
60
+ * migrate this one.
61
+ */
62
+ export declare function tableFromSchema(table: string, schema: ElectricSchema): string;
63
+ /** The shape's primary key, as Postgres column names in key order. */
64
+ export declare function primaryKeyOf(schema: ElectricSchema): string[];
65
+ //# sourceMappingURL=values.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"values.d.ts","sourceRoot":"","sources":["../../src/electric/values.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,uCAAuC;AACvC,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,8EAA8E;AAC9E,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;AA2G5D;;;GAGG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,CAaxD;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,GAAG,OAAO,CAQxF;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAG3D;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKjD;AAwCD;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,MAAM,CAe7E;AAED,sEAAsE;AACtE,wBAAgB,YAAY,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,EAAE,CAK7D"}