@delali/sirannon-db 0.3.4-next.62 → 0.3.4
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/README.md +79 -56
- package/dist/backup/index.d.ts +44 -45
- package/dist/backup/index.mjs +7 -7
- package/dist/backup-scheduler/index.d.ts +42 -41
- package/dist/backup-scheduler/index.mjs +4 -4
- package/dist/baseline-Co3iIsN_.d.ts +19 -0
- package/dist/change-tracker-DqCdeEyx.d.ts +128 -0
- package/dist/{chunk-VOSJBZ6Q.mjs → chunk-2ES3DT4A.mjs} +71 -33
- package/dist/{chunk-6CMCO7S4.mjs → chunk-2O3PP4CF.mjs} +1 -1
- package/dist/{chunk-E7Q7HE46.mjs → chunk-3BZLZ47R.mjs} +26 -26
- package/dist/chunk-4KASJKKK.mjs +2501 -0
- package/dist/{chunk-MYNI2WFS.mjs → chunk-52XW3Z76.mjs} +40 -8
- package/dist/chunk-55YRLSM2.mjs +57 -0
- package/dist/{chunk-KDAHYFW7.mjs → chunk-6AOJCNRU.mjs} +5 -5
- package/dist/{chunk-KXTYR2OQ.mjs → chunk-A3PHCO6W.mjs} +39 -111
- package/dist/chunk-ADNWEHGZ.mjs +299 -0
- package/dist/{chunk-2QLXDHAP.mjs → chunk-BLMRNSUG.mjs} +11 -2
- package/dist/chunk-BVMZJOVM.mjs +5 -0
- package/dist/{chunk-IWGIYDMZ.mjs → chunk-BZWDPF4G.mjs} +1 -1
- package/dist/{chunk-YVF2ZJTE.mjs → chunk-CZL4D3BM.mjs} +1 -1
- package/dist/{chunk-7SC2TZAS.mjs → chunk-DX4WGJL7.mjs} +2 -2
- package/dist/{chunk-45MKELYA.mjs → chunk-ESTZZPT5.mjs} +132 -13
- package/dist/{chunk-QP5L5DFJ.mjs → chunk-FJPR2CJX.mjs} +3 -3
- package/dist/chunk-HR7K3GZW.mjs +36 -0
- package/dist/{chunk-LXCGPZPA.mjs → chunk-IEKIYO4V.mjs} +1 -1
- package/dist/{chunk-TUD5CJ76.mjs → chunk-KB4OLJAD.mjs} +26 -19
- package/dist/{chunk-PBRXXISQ.mjs → chunk-KINDFBQP.mjs} +3 -2
- package/dist/{chunk-E6IMX7RS.mjs → chunk-KJLNYOOL.mjs} +2 -2
- package/dist/{chunk-BTTFW4Z4.mjs → chunk-KU32OT4H.mjs} +2 -10
- package/dist/{chunk-JQWGHSEH.mjs → chunk-NVSQ2T5Q.mjs} +71 -70
- package/dist/{chunk-W4EUTJNT.mjs → chunk-NZGSDALX.mjs} +94 -96
- package/dist/{chunk-U445A3C4.mjs → chunk-QGVRRKB2.mjs} +1 -1
- package/dist/{chunk-GYCEJNU4.mjs → chunk-R7GBXNTO.mjs} +1 -1
- package/dist/{chunk-7C36BCSN.mjs → chunk-VDN6FHU4.mjs} +1 -1
- package/dist/{chunk-6IYS4WLH.mjs → chunk-VO3ZWA2G.mjs} +142 -115
- package/dist/chunk-WOMADPM4.mjs +12 -0
- package/dist/{chunk-OUSWVNWT.mjs → chunk-YCMB6GR4.mjs} +1 -1
- package/dist/{chunk-YNTDOFTN.mjs → chunk-YW7IKQ7T.mjs} +1 -1
- package/dist/{chunk-M2BEAH5I.mjs → chunk-ZN4HI5FK.mjs} +126 -72
- package/dist/{chunk-SBL6GN43.mjs → chunk-ZYWI45EN.mjs} +1 -1
- package/dist/client/index.d.ts +135 -137
- package/dist/client/index.mjs +177 -82
- package/dist/client/topology.d.ts +31 -31
- package/dist/client/topology.mjs +42 -23
- package/dist/client-base-BxTocU1x.d.ts +398 -0
- package/dist/client-streams-YKANXMWO.mjs +112 -0
- package/dist/codegen/cli.mjs +4 -4
- package/dist/codegen/index.d.ts +36 -29
- package/dist/codegen/index.mjs +4 -4
- package/dist/core/index.d.ts +130 -115
- package/dist/core/index.mjs +387 -297
- package/dist/core/writer-worker.mjs +3 -3
- package/dist/database-CGDsXW1K.d.ts +959 -0
- package/dist/database-backup-DcLhxoa1.d.ts +143 -0
- package/dist/driver/better-sqlite3.d.ts +11 -10
- package/dist/driver/better-sqlite3.mjs +11 -11
- package/dist/driver/bun.d.ts +9 -8
- package/dist/driver/expo.d.ts +6 -7
- package/dist/driver/node.d.ts +11 -10
- package/dist/driver/node.mjs +13 -11
- package/dist/driver/wa-sqlite.d.ts +6 -6
- package/dist/errors-CJUkQ-KL.d.ts +199 -0
- package/dist/file-migrations/index.d.ts +20 -18
- package/dist/file-migrations/index.mjs +3 -3
- package/dist/forward-rpc-VRWFZXHN.mjs +44 -0
- package/dist/operation-registry-CGhNT-Sn.d.ts +96 -0
- package/dist/primary-wins-CHNOYuN7.d.ts +81 -0
- package/dist/protocol-DFpNNsIT.d.ts +153 -0
- package/dist/query-types-5B02ixH0.d.ts +129 -0
- package/dist/react/index.d.ts +22 -22
- package/dist/replication/coordinator/etcd.d.ts +37 -31
- package/dist/replication/coordinator/etcd.mjs +296 -131
- package/dist/replication/index.d.ts +201 -161
- package/dist/replication/index.mjs +630 -529
- package/dist/restore-options-CH8mE-Tb.d.ts +84 -0
- package/dist/server/index.d.ts +201 -191
- package/dist/server/index.mjs +736 -619
- package/dist/server-options-aYtCkefX.d.ts +317 -0
- package/dist/server-streams-24XL4T43.mjs +254 -0
- package/dist/sirannon-J3EgPVD7.d.ts +179 -0
- package/dist/transport/grpc.d.ts +37 -24
- package/dist/transport/grpc.mjs +83 -3199
- package/dist/transport/memory.d.ts +25 -26
- package/dist/transport/memory.mjs +18 -9
- package/dist/types-BrAiDGOb.d.ts +505 -0
- package/dist/types-CEJG0eFS.d.ts +88 -0
- package/dist/types-Dc8oWlxj.d.ts +1580 -0
- package/dist/types-DqODKeOq.d.ts +123 -0
- package/dist/types-guEKSzMr.d.ts +367 -0
- package/package.json +7 -7
- package/dist/baseline-D93hcIEE.d.ts +0 -17
- package/dist/change-tracker-CjRFuZdB.d.ts +0 -144
- package/dist/client-base-Cmd9EVme.d.ts +0 -397
- package/dist/database-BLQvqjsw.d.ts +0 -917
- package/dist/database-backup-B7oszQ2n.d.ts +0 -141
- package/dist/errors-Dei4GdBb.d.ts +0 -196
- package/dist/operation-registry-oiPuSYI6.d.ts +0 -94
- package/dist/primary-wins-B0np8JS3.d.ts +0 -85
- package/dist/protocol-HI-nY8DD.d.ts +0 -152
- package/dist/query-types-p1liOP-b.d.ts +0 -127
- package/dist/restore-options-CksYsH1l.d.ts +0 -83
- package/dist/server-options-Bj5iJnjx.d.ts +0 -292
- package/dist/sirannon-BPxC5tC2.d.ts +0 -178
- package/dist/types-B0EhgDbr.d.ts +0 -340
- package/dist/types-CbjfRwrK.d.ts +0 -499
- package/dist/types-CjhxcjhA.d.ts +0 -123
- package/dist/types-DPSmjU54.d.ts +0 -1518
- package/dist/types-DTO4CYhW.d.ts +0 -88
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import { a as OperationRegistry, D as DatabaseOperations } from './operation-registry-CGhNT-Sn.js';
|
|
2
|
+
import { R as ReplicationBatch, C as ConflictResolver, A as ApplyResult } from './types-DqODKeOq.js';
|
|
3
|
+
import { N as NodeHealth, T as Transaction, H as ClusterStatusInfo } from './types-Dc8oWlxj.js';
|
|
4
|
+
import { P as Params, Q as QueryOptions, E as ExecuteResult } from './query-types-5B02ixH0.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Describes one migration that a database has applied, as the migrations table records it.
|
|
8
|
+
*
|
|
9
|
+
* @public
|
|
10
|
+
*/
|
|
11
|
+
interface AppliedMigrationRow {
|
|
12
|
+
/** The migration's version number. */
|
|
13
|
+
version: number;
|
|
14
|
+
/** The migration's name. */
|
|
15
|
+
name: string;
|
|
16
|
+
/** The hash of the migration's `up` SQL, or `null` when a function applied the migration or the row predates checksums. */
|
|
17
|
+
checksum: string | null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** The request details that the server passes to the authenticate hook.
|
|
21
|
+
* @public
|
|
22
|
+
*/
|
|
23
|
+
interface RequestContext {
|
|
24
|
+
/** The request headers, with every name in lower case. */
|
|
25
|
+
headers: Record<string, string>;
|
|
26
|
+
/** The HTTP method of the request, or of the WebSocket upgrade request. */
|
|
27
|
+
method: string;
|
|
28
|
+
/** The URL path of the request. */
|
|
29
|
+
path: string;
|
|
30
|
+
/** The identifier of the database that the route names. */
|
|
31
|
+
databaseId?: string;
|
|
32
|
+
/** The network address of the client that sent the request. */
|
|
33
|
+
remoteAddress: string;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Identifies the caller behind a request. Return the identity, which the
|
|
37
|
+
* server passes to the other hooks and uses to fill the `fromIdentity`
|
|
38
|
+
* arguments of registered operations, or throw a {@link RequestDeniedError} to
|
|
39
|
+
* reject the request with a status of your own.
|
|
40
|
+
*
|
|
41
|
+
* @public
|
|
42
|
+
*/
|
|
43
|
+
type AuthenticateHook<Identity = unknown> = (ctx: RequestContext) => Identity | undefined | Promise<Identity | undefined>;
|
|
44
|
+
/** Returns whether a caller may read the addresses of every node in the group.
|
|
45
|
+
* @public
|
|
46
|
+
*/
|
|
47
|
+
type ClusterStatusAuthorizer = (ctx: RequestContext) => boolean | Promise<boolean>;
|
|
48
|
+
/**
|
|
49
|
+
* The synchronous level during a bulk load. At 'off', SQLite documents that a
|
|
50
|
+
* power loss or an operating-system crash can corrupt the database, so 'off'
|
|
51
|
+
* suits only a load that the operator can re-run from scratch. At 'normal', a
|
|
52
|
+
* WAL-mode database stays safe from corruption, which suits a load into a
|
|
53
|
+
* database whose existing data the operator must keep.
|
|
54
|
+
*
|
|
55
|
+
* @public
|
|
56
|
+
*/
|
|
57
|
+
type BulkLoadDurability = 'off' | 'normal';
|
|
58
|
+
/** The settings for one bulk load.
|
|
59
|
+
* @public
|
|
60
|
+
*/
|
|
61
|
+
interface BulkLoadOptions {
|
|
62
|
+
/** The synchronous level during the load; the default is 'off'. */
|
|
63
|
+
durability?: BulkLoadDurability;
|
|
64
|
+
/**
|
|
65
|
+
* Whether this load ends with a WAL checkpoint; the default is true. Set it
|
|
66
|
+
* to false on every load except the last of a multi-batch import, so that the
|
|
67
|
+
* import pays for one fsyncing checkpoint at the end. Sirannon still restores
|
|
68
|
+
* the configured level after each load, so an abandoned import leaves the
|
|
69
|
+
* writer at that level.
|
|
70
|
+
*/
|
|
71
|
+
checkpoint?: boolean;
|
|
72
|
+
}
|
|
73
|
+
/** The totals for a bulk load, which Sirannon sums across rows so that a large load keeps no per-row results in memory.
|
|
74
|
+
* @public
|
|
75
|
+
*/
|
|
76
|
+
interface BulkLoadResult {
|
|
77
|
+
/** The number of parameter sets that the load applied. */
|
|
78
|
+
rowsLoaded: number;
|
|
79
|
+
/** The number of rows that the load inserted, updated, or deleted. */
|
|
80
|
+
changes: number;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* The object that the server executes one database's statements against. A
|
|
84
|
+
* local `Database` fits this interface, and so does a proxy that forwards
|
|
85
|
+
* statements to another node.
|
|
86
|
+
*
|
|
87
|
+
* @public
|
|
88
|
+
*/
|
|
89
|
+
interface ServerExecutionTarget {
|
|
90
|
+
/** Executes a read and returns the rows. */
|
|
91
|
+
query<T = Record<string, unknown>>(sql: string, params?: Params, options?: QueryOptions): Promise<T[]>;
|
|
92
|
+
/**
|
|
93
|
+
* Executes a read and returns rows already encoded for the wire, with
|
|
94
|
+
* safe-range integers as plain numbers and larger integers and BLOBs as
|
|
95
|
+
* tagged envelopes. When the target has this method, the server calls it and
|
|
96
|
+
* encodes nothing itself; otherwise the server calls
|
|
97
|
+
* {@link ServerExecutionTarget.query} and encodes the rows in a second pass.
|
|
98
|
+
*/
|
|
99
|
+
queryForWire?(sql: string, params?: Params, options?: QueryOptions): Promise<unknown[]>;
|
|
100
|
+
/** Executes one write and returns the change count and the row id of the last inserted row. */
|
|
101
|
+
execute(sql: string, params?: Params, options?: QueryOptions): Promise<ExecuteResult>;
|
|
102
|
+
/** Calls a function inside one transaction. */
|
|
103
|
+
transaction<T>(fn: (tx: Transaction) => Promise<T>, options?: QueryOptions): Promise<T>;
|
|
104
|
+
/**
|
|
105
|
+
* Executes a transaction whose statements are all known before it starts,
|
|
106
|
+
* which lets concurrent transactions share one commit. When the target has no
|
|
107
|
+
* such method, the server calls {@link ServerExecutionTarget.transaction} and
|
|
108
|
+
* executes the statements one at a time.
|
|
109
|
+
*/
|
|
110
|
+
executeTransaction?(statements: readonly {
|
|
111
|
+
sql: string;
|
|
112
|
+
params?: Params;
|
|
113
|
+
}[], options?: QueryOptions): Promise<ExecuteResult[]>;
|
|
114
|
+
/**
|
|
115
|
+
* Executes a bulk load. A target that proxies to a remote primary can omit
|
|
116
|
+
* this method, and the server then rejects bulk-load requests for that target
|
|
117
|
+
* with `BULK_LOAD_UNSUPPORTED`.
|
|
118
|
+
*/
|
|
119
|
+
bulkLoad?(sql: string, paramsBatch: Params[], options?: BulkLoadOptions): Promise<BulkLoadResult>;
|
|
120
|
+
/** Applies a batch of changes that a device pushed; when the target has no such method, the server rejects device pushes with `SYNC_UNSUPPORTED`. */
|
|
121
|
+
applyChanges?(batch: ReplicationBatch, resolver?: ConflictResolver | ((table: string) => ConflictResolver)): Promise<ApplyResult>;
|
|
122
|
+
/** Lists the migrations that this database has applied. */
|
|
123
|
+
appliedMigrations?(): Promise<AppliedMigrationRow[]>;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Returns the target that the server executes a database's statements
|
|
127
|
+
* against, or null or undefined, to which the server responds with
|
|
128
|
+
* `DATABASE_NOT_FOUND`.
|
|
129
|
+
*
|
|
130
|
+
* @public
|
|
131
|
+
*/
|
|
132
|
+
type ServerExecutionTargetResolver = (databaseId: string) => ServerExecutionTarget | null | undefined | Promise<ServerExecutionTarget | null | undefined>;
|
|
133
|
+
/** The options for the standalone HTTP and WebSocket server.
|
|
134
|
+
* @public
|
|
135
|
+
*/
|
|
136
|
+
interface ServerOptions<Identity = unknown> {
|
|
137
|
+
/** The address that the server binds to; the default is '127.0.0.1'. */
|
|
138
|
+
host?: string;
|
|
139
|
+
/** The port that the server binds to; the default is 9876. */
|
|
140
|
+
port?: number;
|
|
141
|
+
/** The cross-origin rules that the server applies to browser requests. */
|
|
142
|
+
cors?: boolean | CorsOptions;
|
|
143
|
+
/**
|
|
144
|
+
* The largest HTTP request body and WebSocket message, in bytes, which the
|
|
145
|
+
* server applies to both transports. The value must be a positive integer no
|
|
146
|
+
* larger than 4_294_967_295, the largest limit that uWebSockets.js stores in
|
|
147
|
+
* its unsigned 32-bit field; the server throws `INVALID_MAX_BODY_BYTES` for
|
|
148
|
+
* any other value. The default is 1_048_576 (1 MB), which caps the
|
|
149
|
+
* memory that the server spends on one request.
|
|
150
|
+
*/
|
|
151
|
+
maxBodyBytes?: number;
|
|
152
|
+
/**
|
|
153
|
+
* The most bytes that the server buffers for one WebSocket connection before
|
|
154
|
+
* it closes the connection, so that the client learns of the overflow and
|
|
155
|
+
* reconnects. The value must lie between `maxBodyBytes`, so that one frame
|
|
156
|
+
* fits, and 4_294_967_295; the server throws `INVALID_WS_BACKPRESSURE` for
|
|
157
|
+
* any other value. The default is the larger of 16 MB and `maxBodyBytes`.
|
|
158
|
+
*/
|
|
159
|
+
maxWebSocketBackpressureBytes?: number;
|
|
160
|
+
/**
|
|
161
|
+
* How long, in milliseconds, Sirannon keeps change events for WebSocket change
|
|
162
|
+
* subscriptions. The retention limits both the size of the change log on disk
|
|
163
|
+
* and how far back a reconnecting subscriber can resume. The default is
|
|
164
|
+
* 3_600_000, one hour.
|
|
165
|
+
*/
|
|
166
|
+
cdcRetentionMs?: number;
|
|
167
|
+
/**
|
|
168
|
+
* How long, in milliseconds, Sirannon keeps the changes that a device's sync
|
|
169
|
+
* cursor still needs before it drops the cursor; a database opened with its
|
|
170
|
+
* own `deviceCursorRetention` uses that value. The default is 2_592_000_000,
|
|
171
|
+
* 30 days.
|
|
172
|
+
*/
|
|
173
|
+
deviceCursorRetentionMs?: number;
|
|
174
|
+
/**
|
|
175
|
+
* The most changes that Sirannon keeps for one device's cursor before it
|
|
176
|
+
* drops the cursor; a database opened with its own `maxChangesHeldForDevice`
|
|
177
|
+
* uses that value. 0 sets no limit, and the default is 0.
|
|
178
|
+
*/
|
|
179
|
+
maxChangesHeldForDevice?: number;
|
|
180
|
+
/** The number of changes that a device may leave unacknowledged before the server pauses sending more; the default is 1_000. */
|
|
181
|
+
maxUnacknowledgedChanges?: number;
|
|
182
|
+
/** The server calls this before every database route and every WebSocket upgrade, to identify the caller. */
|
|
183
|
+
authenticate?: AuthenticateHook<Identity>;
|
|
184
|
+
/** The statements that callers may invoke by name on one database, keyed by database identifier; with neither this nor `sharedOperations` set, only the SQL routes serve reads and writes. */
|
|
185
|
+
operations?: OperationRegistry<Identity>;
|
|
186
|
+
/**
|
|
187
|
+
* The statements that callers may invoke by name on every database that this
|
|
188
|
+
* server serves, including a database that the registry opens after the
|
|
189
|
+
* server starts. When `operations` has an entry of the same name for that
|
|
190
|
+
* database, the server executes that entry.
|
|
191
|
+
*/
|
|
192
|
+
sharedOperations?: DatabaseOperations<Identity>;
|
|
193
|
+
/** Enables the five statement routes and their WebSocket messages, and WebSocket subscriptions to a table's changes on a registry without an `onBeforeSubscribe` hook; the default is false. */
|
|
194
|
+
acceptSql?: boolean;
|
|
195
|
+
/**
|
|
196
|
+
* Enables the route that rebuilds a database from its backups; the default is
|
|
197
|
+
* false. A restore replaces a database that is serving traffic, so the server
|
|
198
|
+
* keeps the route closed until you enable it here, and the server constructor
|
|
199
|
+
* throws `INVALID_BACKUP_RESTORE` when you enable it without an `authenticate`
|
|
200
|
+
* hook.
|
|
201
|
+
*/
|
|
202
|
+
acceptBackupRestore?: boolean;
|
|
203
|
+
/** Enables device sync, which covers the push, snapshot, and migration-list routes, device subscriptions, and acknowledgements; the default is false. The server constructor throws `INVALID_DEVICE_SYNC` when you enable it without an `authenticate` hook. */
|
|
204
|
+
acceptDeviceSync?: boolean;
|
|
205
|
+
/** Returns the target that the server executes a database's statements against; without it, the server uses the databases in its own registry. */
|
|
206
|
+
resolveExecutionTarget?: ServerExecutionTargetResolver;
|
|
207
|
+
/** Returns the replication figures that the readiness endpoint reports. */
|
|
208
|
+
getReplicationStatus?: () => ReplicationStatusInfo | null;
|
|
209
|
+
/** Returns the status that `GET /db/{id}/cluster` reports for one database. */
|
|
210
|
+
getClusterStatus?: (databaseId: string) => ClusterStatusInfo | null;
|
|
211
|
+
/** Returns whether a caller may read the addresses of every node in the group. */
|
|
212
|
+
authorizeClusterStatus?: ClusterStatusAuthorizer;
|
|
213
|
+
}
|
|
214
|
+
/** The replication figures that one node reports through its readiness endpoint.
|
|
215
|
+
* @public
|
|
216
|
+
*/
|
|
217
|
+
interface ReplicationStatusInfo {
|
|
218
|
+
/** The node's role in the replication group, which sets whether it accepts writes or serves reads. */
|
|
219
|
+
role: string;
|
|
220
|
+
/** Whether this node forwards writes to the primary. */
|
|
221
|
+
writeForwarding: boolean;
|
|
222
|
+
/** The number of peers that the node is connected to. */
|
|
223
|
+
peers: number;
|
|
224
|
+
/** The highest change-log position that this node has recorded locally. */
|
|
225
|
+
localSeq: bigint;
|
|
226
|
+
/** What the node can do now, and the reason for that state. */
|
|
227
|
+
health: NodeHealth;
|
|
228
|
+
/** The identifier of the node's replication group. */
|
|
229
|
+
replicationGroupId?: string;
|
|
230
|
+
/** The primary term that this node reports as current. */
|
|
231
|
+
primaryTerm?: bigint;
|
|
232
|
+
/** The identifier of the primary that this node reports as current. */
|
|
233
|
+
currentPrimary?: string;
|
|
234
|
+
/** Whether the node is connected to its cluster coordinator, and whether it holds write authority. */
|
|
235
|
+
coordinator?: {
|
|
236
|
+
connected: boolean;
|
|
237
|
+
authority: boolean;
|
|
238
|
+
};
|
|
239
|
+
/** The state of this node's controller loop for the group. */
|
|
240
|
+
controller?: {
|
|
241
|
+
state: 'disabled' | 'standby' | 'active' | 'lost';
|
|
242
|
+
};
|
|
243
|
+
/** The identifiers of the replicas that the group counts as in sync. */
|
|
244
|
+
inSyncReplicas?: string[];
|
|
245
|
+
/** The identifiers of the replicas that are behind the primary. */
|
|
246
|
+
laggingReplicas?: string[];
|
|
247
|
+
/** The node's progress through its first sync. */
|
|
248
|
+
syncState?: string;
|
|
249
|
+
}
|
|
250
|
+
/** The cross-origin (CORS) settings for the server.
|
|
251
|
+
* @public
|
|
252
|
+
*/
|
|
253
|
+
interface CorsOptions {
|
|
254
|
+
/** The origins that the server allows. */
|
|
255
|
+
origin?: string | string[];
|
|
256
|
+
/** The methods that the server allows. */
|
|
257
|
+
methods?: string[];
|
|
258
|
+
/** The request headers that the server allows. */
|
|
259
|
+
headers?: string[];
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* The options for the mountable WebSocket handler.
|
|
263
|
+
*
|
|
264
|
+
* @internal
|
|
265
|
+
*/
|
|
266
|
+
interface WSHandlerOptions<Identity = unknown> {
|
|
267
|
+
/** The largest message, in bytes; the default is 1_048_576 (1 MB). */
|
|
268
|
+
maxPayloadLength?: number;
|
|
269
|
+
/** The outbound bytes that a socket may buffer before the server pauses a device stream; the default is 16 MB. */
|
|
270
|
+
maxBackpressureBytes?: number;
|
|
271
|
+
/** The change-log retention for change subscriptions, in milliseconds; the default is 3_600_000. */
|
|
272
|
+
cdcRetentionMs?: number;
|
|
273
|
+
deviceCursorRetentionMs?: number;
|
|
274
|
+
maxChangesHeldForDevice?: number;
|
|
275
|
+
maxUnacknowledgedChanges?: number;
|
|
276
|
+
acceptSql?: boolean;
|
|
277
|
+
acceptDeviceSync?: boolean;
|
|
278
|
+
operations?: OperationRegistry<Identity>;
|
|
279
|
+
sharedOperations?: DatabaseOperations<Identity>;
|
|
280
|
+
resolveExecutionTarget?: ServerExecutionTargetResolver;
|
|
281
|
+
}
|
|
282
|
+
/** The options for the client SDK.
|
|
283
|
+
* @public
|
|
284
|
+
*/
|
|
285
|
+
interface ClientOptions {
|
|
286
|
+
/** The transport to use; the default is 'websocket'. */
|
|
287
|
+
transport?: 'websocket' | 'http';
|
|
288
|
+
/**
|
|
289
|
+
* Custom headers for HTTP requests, and for the WebSocket upgrade in a
|
|
290
|
+
* runtime whose WebSocket client can send handshake headers, as Node and Bun
|
|
291
|
+
* can and a browser cannot. In a runtime without that ability, constructing a
|
|
292
|
+
* WebSocket-transport client with headers and no
|
|
293
|
+
* {@link ClientOptions.webSocketProtocols} fails with `INVALID_ARGUMENT`. A
|
|
294
|
+
* browser client can pass both, and the client still sends the headers on
|
|
295
|
+
* every HTTP request.
|
|
296
|
+
*/
|
|
297
|
+
headers?: Record<string, string>;
|
|
298
|
+
/**
|
|
299
|
+
* The WebSocket subprotocols that the client offers during the handshake,
|
|
300
|
+
* which let a browser send a short-lived credential. The client also offers
|
|
301
|
+
* the `sirannon.v1` identifier, and the server selects that identifier, so the
|
|
302
|
+
* handshake response names `sirannon.v1` and leaves the credential out.
|
|
303
|
+
*/
|
|
304
|
+
webSocketProtocols?: string | string[];
|
|
305
|
+
/** Whether the client reconnects when the WebSocket disconnects; the default is true. */
|
|
306
|
+
autoReconnect?: boolean;
|
|
307
|
+
/** The delay before each reconnection attempt, in milliseconds; the default is 1000. */
|
|
308
|
+
reconnectInterval?: number;
|
|
309
|
+
/**
|
|
310
|
+
* The timeout for each request on the WebSocket transport, in milliseconds;
|
|
311
|
+
* the default is 30000, and 0 waits with no limit. Raise it for a bulk load or
|
|
312
|
+
* a batch large enough to take longer than the timeout.
|
|
313
|
+
*/
|
|
314
|
+
requestTimeout?: number;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
export type { AppliedMigrationRow as A, BulkLoadDurability as B, ClientOptions as C, RequestContext as R, ServerOptions as S, WSHandlerOptions as W, BulkLoadResult as a, AuthenticateHook as b, BulkLoadOptions as c, ClusterStatusAuthorizer as d, CorsOptions as e, ReplicationStatusInfo as f, ServerExecutionTarget as g, ServerExecutionTargetResolver as h };
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { SERVICE_NAME } from './chunk-BVMZJOVM.mjs';
|
|
2
|
+
import { ReplicationService, buildServerCreds } from './chunk-4KASJKKK.mjs';
|
|
3
|
+
import { registerPeer, removePeer } from './chunk-HR7K3GZW.mjs';
|
|
4
|
+
import { fromForwardRequest, fromBatchPayload, fromAckPayload, fromSyncRequestPayload, fromSyncBatchPayload, fromSyncCompletePayload, fromSyncAckPayload } from './chunk-ADNWEHGZ.mjs';
|
|
5
|
+
import { TransportError } from './chunk-KU32OT4H.mjs';
|
|
6
|
+
import './chunk-BLMRNSUG.mjs';
|
|
7
|
+
import { SirannonError } from './chunk-KINDFBQP.mjs';
|
|
8
|
+
import { Server, status } from '@grpc/grpc-js';
|
|
9
|
+
import { HealthImplementation } from 'grpc-health-check';
|
|
10
|
+
|
|
11
|
+
async function startServer(t) {
|
|
12
|
+
const server = new Server();
|
|
13
|
+
t.server = server;
|
|
14
|
+
const healthImpl = new HealthImplementation({
|
|
15
|
+
[SERVICE_NAME]: "SERVING",
|
|
16
|
+
"": "SERVING"
|
|
17
|
+
});
|
|
18
|
+
healthImpl.addToServer(server);
|
|
19
|
+
t.healthImpl = healthImpl;
|
|
20
|
+
server.addService(ReplicationService, {
|
|
21
|
+
replicate: (call) => {
|
|
22
|
+
handleServerReplicateStream(t, call);
|
|
23
|
+
},
|
|
24
|
+
sync: (call) => {
|
|
25
|
+
handleServerSyncStream(t, call);
|
|
26
|
+
},
|
|
27
|
+
forward: (call, callback) => {
|
|
28
|
+
handleForwardCall(t, call, callback);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
const host = t.options.host ?? "0.0.0.0";
|
|
32
|
+
const port = t.options.port ?? 0;
|
|
33
|
+
const bindAddress = `${host}:${port}`;
|
|
34
|
+
const serverCreds = buildServerCreds(t.options);
|
|
35
|
+
await new Promise((resolve, reject) => {
|
|
36
|
+
server.bindAsync(bindAddress, serverCreds, (err, assignedPort) => {
|
|
37
|
+
if (err) {
|
|
38
|
+
reject(new TransportError(`Failed to bind gRPC server: ${err.message}`));
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
t.boundPort = assignedPort;
|
|
42
|
+
resolve();
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
function handleServerReplicateStream(t, call) {
|
|
47
|
+
let peerId = null;
|
|
48
|
+
let helloSent = false;
|
|
49
|
+
call.on("data", (msg) => {
|
|
50
|
+
if (peerId === null) {
|
|
51
|
+
if (!msg.hello) {
|
|
52
|
+
call.destroy(new Error("First message must be Hello"));
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (!t.validateTlsIdentity(call, msg.hello.nodeId)) {
|
|
56
|
+
call.destroy(new Error(`TLS certificate CN does not match claimed nodeId '${msg.hello.nodeId}'`));
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
peerId = msg.hello.nodeId;
|
|
60
|
+
const peerRole = msg.hello.role;
|
|
61
|
+
const existing = t.serverPeerStreams.get(peerId);
|
|
62
|
+
if (existing?.replicateStream) {
|
|
63
|
+
existing.replicateStream.end();
|
|
64
|
+
}
|
|
65
|
+
const ps = t.serverPeerStreams.get(peerId) ?? {
|
|
66
|
+
replicateStream: null,
|
|
67
|
+
syncStream: null
|
|
68
|
+
};
|
|
69
|
+
ps.replicateStream = call;
|
|
70
|
+
t.serverPeerStreams.set(peerId, ps);
|
|
71
|
+
if (!helloSent) {
|
|
72
|
+
call.write({
|
|
73
|
+
hello: {
|
|
74
|
+
nodeId: t.localNodeId,
|
|
75
|
+
role: t.localRole,
|
|
76
|
+
groupId: t.localGroupId ?? "",
|
|
77
|
+
primaryTerm: t.localPrimaryTerm ?? 0n,
|
|
78
|
+
protocolVersion: t.localProtocolVersion ?? ""
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
helloSent = true;
|
|
82
|
+
}
|
|
83
|
+
registerPeer(t.connectedPeers, t.peerConnectedHandler, peerId, peerRole, {
|
|
84
|
+
groupId: msg.hello.groupId || void 0,
|
|
85
|
+
primaryTerm: msg.hello.primaryTerm === 0n ? void 0 : msg.hello.primaryTerm,
|
|
86
|
+
protocolVersion: msg.hello.protocolVersion || void 0
|
|
87
|
+
});
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
if (msg.batch) {
|
|
91
|
+
const batch = fromBatchPayload(msg.batch);
|
|
92
|
+
t.batchHandler?.(batch, peerId).catch(() => {
|
|
93
|
+
});
|
|
94
|
+
} else if (msg.ack) {
|
|
95
|
+
const ack = fromAckPayload(msg.ack);
|
|
96
|
+
t.ackHandler?.(ack, peerId);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
call.on("end", () => {
|
|
100
|
+
call.end();
|
|
101
|
+
if (peerId) {
|
|
102
|
+
const ps = t.serverPeerStreams.get(peerId);
|
|
103
|
+
if (ps?.replicateStream === call) {
|
|
104
|
+
ps.replicateStream = null;
|
|
105
|
+
}
|
|
106
|
+
if (!ps?.syncStream) {
|
|
107
|
+
removePeer(t.connectedPeers, t.serverPeerStreams, t.peerDisconnectedHandler, peerId);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
call.on("error", () => {
|
|
112
|
+
if (peerId) {
|
|
113
|
+
const ps = t.serverPeerStreams.get(peerId);
|
|
114
|
+
if (ps?.replicateStream === call) {
|
|
115
|
+
ps.replicateStream = null;
|
|
116
|
+
}
|
|
117
|
+
if (!ps?.syncStream) {
|
|
118
|
+
removePeer(t.connectedPeers, t.serverPeerStreams, t.peerDisconnectedHandler, peerId);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
function handleServerSyncStream(t, call) {
|
|
124
|
+
let peerId = null;
|
|
125
|
+
let helloSent = false;
|
|
126
|
+
call.on("data", (msg) => {
|
|
127
|
+
if (peerId === null) {
|
|
128
|
+
if (!msg.hello) {
|
|
129
|
+
call.destroy(new Error("First message must be Hello"));
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
if (!t.validateTlsIdentity(call, msg.hello.nodeId)) {
|
|
133
|
+
call.destroy(new Error(`TLS certificate CN does not match claimed nodeId '${msg.hello.nodeId}'`));
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
peerId = msg.hello.nodeId;
|
|
137
|
+
const peerRole = msg.hello.role;
|
|
138
|
+
const existing = t.serverPeerStreams.get(peerId);
|
|
139
|
+
if (existing?.syncStream) {
|
|
140
|
+
existing.syncStream.end();
|
|
141
|
+
}
|
|
142
|
+
const ps = t.serverPeerStreams.get(peerId) ?? {
|
|
143
|
+
replicateStream: null,
|
|
144
|
+
syncStream: null
|
|
145
|
+
};
|
|
146
|
+
ps.syncStream = call;
|
|
147
|
+
t.serverPeerStreams.set(peerId, ps);
|
|
148
|
+
if (!helloSent) {
|
|
149
|
+
call.write({
|
|
150
|
+
hello: {
|
|
151
|
+
nodeId: t.localNodeId,
|
|
152
|
+
role: t.localRole,
|
|
153
|
+
groupId: t.localGroupId ?? "",
|
|
154
|
+
primaryTerm: t.localPrimaryTerm ?? 0n,
|
|
155
|
+
protocolVersion: t.localProtocolVersion ?? ""
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
helloSent = true;
|
|
159
|
+
}
|
|
160
|
+
registerPeer(t.connectedPeers, t.peerConnectedHandler, peerId, peerRole, {
|
|
161
|
+
groupId: msg.hello.groupId || void 0,
|
|
162
|
+
primaryTerm: msg.hello.primaryTerm === 0n ? void 0 : msg.hello.primaryTerm,
|
|
163
|
+
protocolVersion: msg.hello.protocolVersion || void 0
|
|
164
|
+
});
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
if (msg.syncRequest) {
|
|
168
|
+
t.syncRequestHandler?.(fromSyncRequestPayload(msg.syncRequest), peerId).catch(() => {
|
|
169
|
+
});
|
|
170
|
+
} else if (msg.syncBatch) {
|
|
171
|
+
t.syncBatchHandler?.(fromSyncBatchPayload(msg.syncBatch), peerId).catch(() => {
|
|
172
|
+
});
|
|
173
|
+
} else if (msg.syncComplete) {
|
|
174
|
+
t.syncCompleteHandler?.(fromSyncCompletePayload(msg.syncComplete), peerId).catch(() => {
|
|
175
|
+
});
|
|
176
|
+
} else if (msg.syncAck) {
|
|
177
|
+
t.syncAckHandler?.(fromSyncAckPayload(msg.syncAck), peerId);
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
call.on("end", () => {
|
|
181
|
+
call.end();
|
|
182
|
+
if (peerId) {
|
|
183
|
+
const ps = t.serverPeerStreams.get(peerId);
|
|
184
|
+
if (ps?.syncStream === call) {
|
|
185
|
+
ps.syncStream = null;
|
|
186
|
+
}
|
|
187
|
+
if (!ps?.replicateStream) {
|
|
188
|
+
removePeer(t.connectedPeers, t.serverPeerStreams, t.peerDisconnectedHandler, peerId);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
call.on("error", () => {
|
|
193
|
+
if (peerId) {
|
|
194
|
+
const ps = t.serverPeerStreams.get(peerId);
|
|
195
|
+
if (ps?.syncStream === call) {
|
|
196
|
+
ps.syncStream = null;
|
|
197
|
+
}
|
|
198
|
+
if (!ps?.replicateStream) {
|
|
199
|
+
removePeer(t.connectedPeers, t.serverPeerStreams, t.peerDisconnectedHandler, peerId);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
function handleForwardCall(t, call, callback) {
|
|
205
|
+
if (!t.forwardHandler) {
|
|
206
|
+
callback({
|
|
207
|
+
code: status.UNIMPLEMENTED,
|
|
208
|
+
message: "Forward handler not registered"
|
|
209
|
+
});
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
const peerId = t.resolveForwardPeerId(call);
|
|
213
|
+
if (!peerId) {
|
|
214
|
+
callback({
|
|
215
|
+
code: status.UNAUTHENTICATED,
|
|
216
|
+
message: "Forward request from unidentified peer"
|
|
217
|
+
});
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
let appReq;
|
|
221
|
+
try {
|
|
222
|
+
appReq = fromForwardRequest(call.request);
|
|
223
|
+
} catch (err) {
|
|
224
|
+
callback({
|
|
225
|
+
code: status.INVALID_ARGUMENT,
|
|
226
|
+
message: err instanceof Error ? err.message : String(err)
|
|
227
|
+
});
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
t.forwardHandler(appReq, peerId).then((result) => {
|
|
231
|
+
callback(null, {
|
|
232
|
+
requestId: result.requestId,
|
|
233
|
+
results: result.results.map((r) => ({
|
|
234
|
+
changes: r.changes,
|
|
235
|
+
lastInsertRowId: BigInt(typeof r.lastInsertRowId === "string" ? r.lastInsertRowId : r.lastInsertRowId)
|
|
236
|
+
})),
|
|
237
|
+
error: "",
|
|
238
|
+
errorCode: "",
|
|
239
|
+
groupId: result.groupId ?? "",
|
|
240
|
+
primaryTerm: result.primaryTerm ?? 0n
|
|
241
|
+
});
|
|
242
|
+
}).catch((err) => {
|
|
243
|
+
callback(null, {
|
|
244
|
+
requestId: call.request.requestId,
|
|
245
|
+
results: [],
|
|
246
|
+
error: err.message,
|
|
247
|
+
errorCode: err instanceof SirannonError ? err.code : "",
|
|
248
|
+
groupId: call.request.groupId ?? "",
|
|
249
|
+
primaryTerm: call.request.primaryTerm ?? 0n
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export { handleForwardCall, handleServerReplicateStream, handleServerSyncStream, startServer };
|