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.
- package/CHANGELOG.md +266 -0
- package/README.md +31 -15
- package/dist/.tsbuildinfo.build +1 -1
- package/dist/browser/engine-transport.d.ts +2 -0
- package/dist/browser/engine-transport.d.ts.map +1 -1
- package/dist/browser/engine-transport.js +43 -11
- package/dist/browser/engine-transport.js.map +1 -1
- package/dist/browser/firebird-browser.d.ts.map +1 -1
- package/dist/browser/firebird-browser.js +59 -20
- package/dist/browser/firebird-browser.js.map +1 -1
- package/dist/browser/index.d.ts +3 -3
- package/dist/browser/index.d.ts.map +1 -1
- package/dist/browser/index.js +6 -3
- package/dist/browser/index.js.map +1 -1
- package/dist/browser/params.d.ts +8 -2
- package/dist/browser/params.d.ts.map +1 -1
- package/dist/browser/params.js +14 -5
- package/dist/browser/params.js.map +1 -1
- package/dist/browser/serve-engine.d.ts +30 -0
- package/dist/browser/serve-engine.d.ts.map +1 -0
- package/dist/browser/serve-engine.js +93 -0
- package/dist/browser/serve-engine.js.map +1 -0
- package/dist/browser/worker-entry.d.ts +8 -22
- package/dist/browser/worker-entry.d.ts.map +1 -1
- package/dist/browser/worker-entry.js +18 -79
- package/dist/browser/worker-entry.js.map +1 -1
- package/dist/electric/apply.d.ts +53 -0
- package/dist/electric/apply.d.ts.map +1 -0
- package/dist/electric/apply.js +88 -0
- package/dist/electric/apply.js.map +1 -0
- package/dist/electric/index.d.ts +133 -0
- package/dist/electric/index.d.ts.map +1 -0
- package/dist/electric/index.js +378 -0
- package/dist/electric/index.js.map +1 -0
- package/dist/electric/serial.d.ts +20 -0
- package/dist/electric/serial.d.ts.map +1 -0
- package/dist/electric/serial.js +29 -0
- package/dist/electric/serial.js.map +1 -0
- package/dist/electric/values.d.ts +65 -0
- package/dist/electric/values.d.ts.map +1 -0
- package/dist/electric/values.js +257 -0
- package/dist/electric/values.js.map +1 -0
- package/dist/electric/writes.d.ts +99 -0
- package/dist/electric/writes.d.ts.map +1 -0
- package/dist/electric/writes.js +282 -0
- package/dist/electric/writes.js.map +1 -0
- package/dist/electric-server/capture.d.ts +51 -0
- package/dist/electric-server/capture.d.ts.map +1 -0
- package/dist/electric-server/capture.js +224 -0
- package/dist/electric-server/capture.js.map +1 -0
- package/dist/electric-server/catalog.d.ts +61 -0
- package/dist/electric-server/catalog.d.ts.map +1 -0
- package/dist/electric-server/catalog.js +137 -0
- package/dist/electric-server/catalog.js.map +1 -0
- package/dist/electric-server/index.d.ts +85 -0
- package/dist/electric-server/index.d.ts.map +1 -0
- package/dist/electric-server/index.js +528 -0
- package/dist/electric-server/index.js.map +1 -0
- package/dist/electric-server/where.d.ts +30 -0
- package/dist/electric-server/where.d.ts.map +1 -0
- package/dist/electric-server/where.js +243 -0
- package/dist/electric-server/where.js.map +1 -0
- package/dist/firebird.d.ts +3 -1
- package/dist/firebird.d.ts.map +1 -1
- package/dist/firebird.js +259 -22
- package/dist/firebird.js.map +1 -1
- package/dist/frameworks/query-store.d.ts +99 -0
- package/dist/frameworks/query-store.d.ts.map +1 -0
- package/dist/frameworks/query-store.js +166 -0
- package/dist/frameworks/query-store.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/react/index.d.ts +68 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +92 -0
- package/dist/react/index.js.map +1 -0
- package/dist/repl/index.d.ts +76 -0
- package/dist/repl/index.d.ts.map +1 -0
- package/dist/repl/index.js +328 -0
- package/dist/repl/index.js.map +1 -0
- package/dist/types.d.ts +2 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/vue/index.d.ts +68 -0
- package/dist/vue/index.d.ts.map +1 -0
- package/dist/vue/index.js +107 -0
- package/dist/vue/index.js.map +1 -0
- package/dist/wasm/firebird-embedded-st.js +1 -1
- package/dist/wasm/firebird-embedded-st.wasm +0 -0
- package/dist/wasm/firebird-embedded.js +1 -1
- package/dist/wasm/firebird-embedded.wasm +0 -0
- package/dist/wasm-loader.d.ts +57 -7
- package/dist/wasm-loader.d.ts.map +1 -1
- package/dist/wasm-loader.js +106 -21
- package/dist/wasm-loader.js.map +1 -1
- 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
|
-
*
|
|
23
|
-
* need SharedArrayBuffer, which browsers withhold
|
|
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 =
|
|
31
|
+
exports.serveEngine = void 0;
|
|
27
32
|
const engine_transport_1 = require("./engine-transport");
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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":";AAAA
|
|
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"}
|