firebird-wasm 0.7.0 → 0.9.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 (99) hide show
  1. package/CHANGELOG.md +266 -0
  2. package/README.md +31 -15
  3. package/dist/.tsbuildinfo.build +1 -1
  4. package/dist/browser/engine-transport.d.ts +2 -0
  5. package/dist/browser/engine-transport.d.ts.map +1 -1
  6. package/dist/browser/engine-transport.js +43 -11
  7. package/dist/browser/engine-transport.js.map +1 -1
  8. package/dist/browser/firebird-browser.d.ts.map +1 -1
  9. package/dist/browser/firebird-browser.js +59 -20
  10. package/dist/browser/firebird-browser.js.map +1 -1
  11. package/dist/browser/index.d.ts +3 -3
  12. package/dist/browser/index.d.ts.map +1 -1
  13. package/dist/browser/index.js +6 -3
  14. package/dist/browser/index.js.map +1 -1
  15. package/dist/browser/params.d.ts +8 -2
  16. package/dist/browser/params.d.ts.map +1 -1
  17. package/dist/browser/params.js +14 -5
  18. package/dist/browser/params.js.map +1 -1
  19. package/dist/browser/serve-engine.d.ts +30 -0
  20. package/dist/browser/serve-engine.d.ts.map +1 -0
  21. package/dist/browser/serve-engine.js +93 -0
  22. package/dist/browser/serve-engine.js.map +1 -0
  23. package/dist/browser/worker-entry.d.ts +8 -22
  24. package/dist/browser/worker-entry.d.ts.map +1 -1
  25. package/dist/browser/worker-entry.js +18 -79
  26. package/dist/browser/worker-entry.js.map +1 -1
  27. package/dist/electric/apply.d.ts +53 -0
  28. package/dist/electric/apply.d.ts.map +1 -0
  29. package/dist/electric/apply.js +88 -0
  30. package/dist/electric/apply.js.map +1 -0
  31. package/dist/electric/index.d.ts +133 -0
  32. package/dist/electric/index.d.ts.map +1 -0
  33. package/dist/electric/index.js +378 -0
  34. package/dist/electric/index.js.map +1 -0
  35. package/dist/electric/serial.d.ts +20 -0
  36. package/dist/electric/serial.d.ts.map +1 -0
  37. package/dist/electric/serial.js +29 -0
  38. package/dist/electric/serial.js.map +1 -0
  39. package/dist/electric/values.d.ts +65 -0
  40. package/dist/electric/values.d.ts.map +1 -0
  41. package/dist/electric/values.js +257 -0
  42. package/dist/electric/values.js.map +1 -0
  43. package/dist/electric/writes.d.ts +99 -0
  44. package/dist/electric/writes.d.ts.map +1 -0
  45. package/dist/electric/writes.js +282 -0
  46. package/dist/electric/writes.js.map +1 -0
  47. package/dist/electric-server/capture.d.ts +51 -0
  48. package/dist/electric-server/capture.d.ts.map +1 -0
  49. package/dist/electric-server/capture.js +224 -0
  50. package/dist/electric-server/capture.js.map +1 -0
  51. package/dist/electric-server/catalog.d.ts +61 -0
  52. package/dist/electric-server/catalog.d.ts.map +1 -0
  53. package/dist/electric-server/catalog.js +137 -0
  54. package/dist/electric-server/catalog.js.map +1 -0
  55. package/dist/electric-server/index.d.ts +85 -0
  56. package/dist/electric-server/index.d.ts.map +1 -0
  57. package/dist/electric-server/index.js +528 -0
  58. package/dist/electric-server/index.js.map +1 -0
  59. package/dist/electric-server/where.d.ts +30 -0
  60. package/dist/electric-server/where.d.ts.map +1 -0
  61. package/dist/electric-server/where.js +243 -0
  62. package/dist/electric-server/where.js.map +1 -0
  63. package/dist/firebird.d.ts +3 -1
  64. package/dist/firebird.d.ts.map +1 -1
  65. package/dist/firebird.js +259 -22
  66. package/dist/firebird.js.map +1 -1
  67. package/dist/frameworks/query-store.d.ts +99 -0
  68. package/dist/frameworks/query-store.d.ts.map +1 -0
  69. package/dist/frameworks/query-store.js +166 -0
  70. package/dist/frameworks/query-store.js.map +1 -0
  71. package/dist/index.d.ts +1 -1
  72. package/dist/index.d.ts.map +1 -1
  73. package/dist/index.js +2 -1
  74. package/dist/index.js.map +1 -1
  75. package/dist/react/index.d.ts +68 -0
  76. package/dist/react/index.d.ts.map +1 -0
  77. package/dist/react/index.js +92 -0
  78. package/dist/react/index.js.map +1 -0
  79. package/dist/repl/index.d.ts +76 -0
  80. package/dist/repl/index.d.ts.map +1 -0
  81. package/dist/repl/index.js +328 -0
  82. package/dist/repl/index.js.map +1 -0
  83. package/dist/types.d.ts +2 -1
  84. package/dist/types.d.ts.map +1 -1
  85. package/dist/version.d.ts +1 -1
  86. package/dist/version.js +1 -1
  87. package/dist/vue/index.d.ts +68 -0
  88. package/dist/vue/index.d.ts.map +1 -0
  89. package/dist/vue/index.js +107 -0
  90. package/dist/vue/index.js.map +1 -0
  91. package/dist/wasm/firebird-embedded-st.js +1 -1
  92. package/dist/wasm/firebird-embedded-st.wasm +0 -0
  93. package/dist/wasm/firebird-embedded.js +1 -1
  94. package/dist/wasm/firebird-embedded.wasm +0 -0
  95. package/dist/wasm-loader.d.ts +57 -7
  96. package/dist/wasm-loader.d.ts.map +1 -1
  97. package/dist/wasm-loader.js +106 -21
  98. package/dist/wasm-loader.js.map +1 -1
  99. package/package.json +50 -3
@@ -19,89 +19,28 @@
19
19
  * serves filesystem requests too — persistence copies the database between
20
20
  * that filesystem and IndexedDB, and only this side can read it.
21
21
  *
22
- * The page must be cross-origin isolated (COOP/COEP): Emscripten's pthreads
23
- * need SharedArrayBuffer, which browsers withhold otherwise.
22
+ * With the threaded build the page must be cross-origin isolated (COOP/COEP):
23
+ * Emscripten's pthreads need SharedArrayBuffer, which browsers withhold
24
+ * otherwise. The build without threads needs no isolation.
25
+ *
26
+ * Loading this module starts serving, but only in a dedicated Worker; the
27
+ * library's index exports serveEngine from serve-engine.ts, which has no such
28
+ * side effect, so importing the library starts nothing.
24
29
  */
25
30
  Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.serveEngine = serveEngine;
31
+ exports.serveEngine = void 0;
27
32
  const engine_transport_1 = require("./engine-transport");
28
- /**
29
- * Every distinct ArrayBuffer behind a Uint8Array in a query result.
30
- *
31
- * Only query results are walked, and only one level into each row: a value is
32
- * either a blob or it is not, and scanning arbitrary structures on every reply
33
- * would cost more than the transfer saves.
34
- *
35
- * De-duplicated because transferring the same buffer twice throws
36
- * DataCloneError, and two rows can hold views on one buffer if the same blob
37
- * appeared twice.
38
- */
39
- function collectBlobBuffers(result) {
40
- if (typeof result !== 'object' || result === null)
41
- return [];
42
- const rows = result.rows;
43
- if (!Array.isArray(rows))
44
- return [];
45
- const buffers = new Set();
46
- for (const row of rows) {
47
- if (typeof row !== 'object' || row === null)
48
- continue;
49
- // Rows are arrays in rowMode 'array' and plain objects otherwise;
50
- // Object.values covers both.
51
- for (const value of Object.values(row)) {
52
- if (value instanceof Uint8Array)
53
- buffers.add(value.buffer);
54
- }
55
- }
56
- return [...buffers];
57
- }
58
- /**
59
- * Wire a transport up to a Worker scope.
60
- *
61
- * Exported so a host application can build its own Worker entry point — for
62
- * example to set `locateFile` before any message is handled.
63
- */
64
- function serveEngine(scope, transport) {
65
- const reply = (response) => {
66
- // Transfer the buffer for readFile rather than copying it: a database
67
- // image is measured in megabytes and this runs on every persist.
68
- //
69
- // The same applies to binary BLOBs coming back from a query, which arrive
70
- // as Uint8Arrays inside the rows. Structured cloning would copy every one
71
- // of them across the boundary — the cost a base64 string could never avoid
72
- // and the reason the side channel is worth having at all.
73
- const transfer = response.result instanceof Uint8Array
74
- ? [response.result.buffer]
75
- : collectBlobBuffers(response.result);
76
- scope.postMessage(response, transfer);
77
- };
78
- scope.addEventListener('message', (event) => {
79
- const { id, op, args } = event.data;
80
- void (async () => {
81
- try {
82
- // The op names are exactly the transport's method names, so this is a
83
- // direct dispatch rather than a switch that has to be kept in step.
84
- const fn = transport[op];
85
- if (typeof fn !== 'function') {
86
- throw new Error(`unknown engine operation: ${op}`);
87
- }
88
- const result = (await fn.apply(transport, args)) ?? null;
89
- reply({ id, ok: true, result: result });
90
- }
91
- catch (err) {
92
- reply({
93
- id,
94
- ok: false,
95
- error: err instanceof Error ? err.message : String(err),
96
- });
97
- }
98
- })();
99
- });
100
- }
101
- // Auto-start when loaded as a Worker script.
102
- if (typeof self !== 'undefined' && typeof self.postMessage === 'function') {
33
+ const serve_engine_1 = require("./serve-engine");
34
+ var serve_engine_2 = require("./serve-engine");
35
+ Object.defineProperty(exports, "serveEngine", { enumerable: true, get: function () { return serve_engine_2.serveEngine; } });
36
+ // Start serving when loaded as a dedicated Worker's script, and only then.
37
+ // `self.postMessage` alone is no test: a page's window has one too, and
38
+ // serving there would answer every window that can message the page.
39
+ const workerScope = globalThis.WorkerGlobalScope;
40
+ if (typeof workerScope === 'function' && self instanceof workerScope
41
+ && typeof self.postMessage === 'function') {
103
42
  try {
104
- serveEngine(self, new engine_transport_1.DirectTransport(self.FIREBIRD_WORKER_OPTIONS ?? {}));
43
+ (0, serve_engine_1.serveEngine)(self, new engine_transport_1.DirectTransport(self.FIREBIRD_WORKER_OPTIONS ?? {}));
105
44
  }
106
45
  catch (err) {
107
46
  // No request to answer yet, so report the bootstrap failure out of band;
@@ -1 +1 @@
1
- {"version":3,"file":"worker-entry.js","sourceRoot":"","sources":["../../src/browser/worker-entry.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"worker-entry.js","sourceRoot":"","sources":["../../src/browser/worker-entry.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;;;AAEH,yDAAqD;AAErD,iDAA6C;AAG7C,+CAA6C;AAApC,2GAAA,WAAW,OAAA;AAQpB,2EAA2E;AAC3E,wEAAwE;AACxE,qEAAqE;AACrE,MAAM,WAAW,GAAI,UAA8C,CAAC,iBAAiB,CAAC;AACtF,IAAI,OAAO,WAAW,KAAK,UAAU,IAAI,IAAI,YAAa,WAAgC;OACrF,OAAO,IAAI,CAAC,WAAW,KAAK,UAAU,EAAE,CAAC;IAC5C,IAAI,CAAC;QACH,IAAA,0BAAW,EAAC,IAAI,EAAE,IAAI,kCAAe,CAAC,IAAI,CAAC,uBAAuB,IAAI,EAAE,CAAC,CAAC,CAAC;IAC7E,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,yEAAyE;QACzE,6DAA6D;QAC7D,IAAI,CAAC,WAAW,CAAC;YACf,EAAE,EAAE,CAAC,CAAC;YACN,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;SACxD,CAAC,CAAC;IACL,CAAC;AACH,CAAC"}
@@ -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,133 @@
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
+ export { createWriteQueue, WriteRejected } from './writes';
34
+ export type { WriteQueue, WriteQueueOptions, LocalWrite, SendResult, WritesDatabase } from './writes';
35
+ import type { WriteQueue } from './writes';
36
+ /** What the sync needs of a database: `FirebirdBrowser` has it. */
37
+ export interface ElectricDatabase {
38
+ transaction<T>(fn: (tx: ElectricTransaction) => Promise<T>): Promise<T>;
39
+ }
40
+ /** Options of {@link syncShapeToTable}. */
41
+ export interface SyncShapeToTableOptions {
42
+ /**
43
+ * The shape, as `ShapeStream` takes it: `url` of Electric's `/v1/shape`
44
+ * (or a proxy in front of it), and `params` with at least `table`.
45
+ */
46
+ shape: ShapeOptions;
47
+ /** The Firebird table the rows go to, as written in Firebird SQL. */
48
+ table: string;
49
+ /**
50
+ * Where the sync position is kept, so that the next sync resumes rather
51
+ * than starting over; `null` starts over every time (and empties the
52
+ * table first). One shape per key, and one shape per table.
53
+ */
54
+ shapeKey: string | null;
55
+ /** The primary key, as Postgres column names. Default: the shape's own. */
56
+ primaryKey?: string[];
57
+ /** Firebird column names for Postgres columns, where the default mapping does not fit. */
58
+ columns?: Record<string, string>;
59
+ /**
60
+ * The event posted once per applied batch, delivered when it commits, so
61
+ * `useLiveQuery` and `db.live()` can follow the sync without a trigger.
62
+ * Default `electric_<shapeKey>`, or `electric_<table>` without a key.
63
+ */
64
+ event?: string | null;
65
+ /** Called when the sync stops on an error. Default: console.error. */
66
+ onError?: (error: Error) => void;
67
+ /** Local writes to keep over what the sync applies, and to drop once confirmed: {@link createWriteQueue}. */
68
+ writes?: WriteQueue;
69
+ /** For tests: the ShapeStream class to use. */
70
+ ShapeStream?: typeof ShapeStream;
71
+ }
72
+ /** The shape options a sync passes on to `ShapeStream`. */
73
+ export type ShapeOptions = Omit<ShapeStreamOptions, 'parser' | 'transformer' | 'columnMapper' | 'offset' | 'handle'>;
74
+ /** One shape of {@link syncShapesToTables}. */
75
+ export interface ShapeTableOptions {
76
+ /** The shape, as {@link SyncShapeToTableOptions.shape}. */
77
+ shape: ShapeOptions;
78
+ /** The Firebird table its rows go to. */
79
+ table: string;
80
+ /** The primary key, as Postgres column names. Default: the shape's own. */
81
+ primaryKey?: string[];
82
+ /** Firebird column names for Postgres columns. */
83
+ columns?: Record<string, string>;
84
+ }
85
+ /** Options of {@link syncShapesToTables}. */
86
+ export interface SyncShapesToTablesOptions {
87
+ /** The shapes, by a name of your choosing; one table each. */
88
+ shapes: Record<string, ShapeTableOptions>;
89
+ /**
90
+ * Where the positions are kept (one per shape, under `<key>:<name>`), so
91
+ * the next sync resumes; `null` starts over every time.
92
+ */
93
+ key: string | null;
94
+ /** The event posted once per committed batch. Default `electric_<key>`, or none without a key. */
95
+ event?: string | null;
96
+ /** Called when the sync stops on an error. Default: console.error. */
97
+ onError?: (error: Error) => void;
98
+ /** Local writes to keep over what the sync applies, and to drop once confirmed: {@link createWriteQueue}. */
99
+ writes?: WriteQueue;
100
+ /** For tests: the ShapeStream class to use. */
101
+ ShapeStream?: typeof ShapeStream;
102
+ }
103
+ /** A running sync. Returned by {@link syncShapeToTable} and {@link syncShapesToTables}. */
104
+ export interface ShapeSync {
105
+ /** Resolves when the first complete copy of the shape is in; rejects if the sync stops before. */
106
+ readonly upToDate: Promise<void>;
107
+ /** True once a batch marked up-to-date has been applied. */
108
+ readonly isUpToDate: boolean;
109
+ /** Why the sync stopped, if it stopped on an error. */
110
+ readonly error: Error | undefined;
111
+ /** Stop syncing. The rows and the position stay; a later sync resumes. */
112
+ unsubscribe(): Promise<void>;
113
+ }
114
+ /**
115
+ * Keep `options.table` in step with a shape: an initial copy, then every
116
+ * change, until {@link ShapeSync.unsubscribe}. The table must exist.
117
+ */
118
+ export declare function syncShapeToTable(db: ElectricDatabase, options: SyncShapeToTableOptions): Promise<ShapeSync>;
119
+ /**
120
+ * Keep several tables in step with several shapes, together: every batch is
121
+ * applied in one Firebird transaction with all the shapes' positions, so a
122
+ * Postgres transaction that wrote to two of them never shows in one table
123
+ * and not the other.
124
+ *
125
+ * A batch is applied once every shape has reached up-to-date after the last
126
+ * change it received. Electric marks each up-to-date with the last
127
+ * Postgres position it has seen (`global_last_seen_lsn`); where it does, the
128
+ * shapes must also be at the same one, and a shape behind is asked to
129
+ * refresh, so the batch holds the whole of each transaction it holds any of.
130
+ * The tables must exist.
131
+ */
132
+ export declare function syncShapesToTables(db: ElectricDatabase, options: SyncShapesToTablesOptions): Promise<ShapeSync>;
133
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/electric/index.ts"],"names":[],"mappings":"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epB"}