@delali/sirannon-db 0.1.6 → 0.1.7

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 (54) hide show
  1. package/README.md +134 -19
  2. package/dist/backup-scheduler/index.d.ts +18 -3
  3. package/dist/backup-scheduler/index.mjs +2 -2
  4. package/dist/{change-tracker-CFTQ9TSn.d.ts → change-tracker-CbmaMO-N.d.ts} +12 -2
  5. package/dist/chunk-4IGMIJQK.mjs +318 -0
  6. package/dist/chunk-BNUTBHHH.mjs +22 -0
  7. package/dist/chunk-CJLYFDP5.mjs +26 -0
  8. package/dist/chunk-CW6S3WL5.mjs +222 -0
  9. package/dist/chunk-DJLX6CAE.mjs +20 -0
  10. package/dist/chunk-DVWQD3GF.mjs +49 -0
  11. package/dist/chunk-GEZUUIKV.mjs +268 -0
  12. package/dist/{chunk-UVMVN3OT.mjs → chunk-H5AB6NIR.mjs} +1 -1
  13. package/dist/{chunk-UTO3ZAFS.mjs → chunk-HHRMRFFR.mjs} +148 -28
  14. package/dist/chunk-TKGHYWQ6.mjs +35 -0
  15. package/dist/chunk-VLTICJOD.mjs +470 -0
  16. package/dist/{chunk-O7BHI3CF.mjs → chunk-YPYVQJ4C.mjs} +15 -1
  17. package/dist/client/index.d.ts +107 -15
  18. package/dist/client/index.mjs +348 -37
  19. package/dist/core/index.d.ts +70 -13
  20. package/dist/core/index.mjs +635 -192
  21. package/dist/core/writer-worker.d.ts +2 -0
  22. package/dist/core/writer-worker.mjs +107 -0
  23. package/dist/{database-BVY1GqE7.d.ts → database-DuGp0Rtr.d.ts} +51 -19
  24. package/dist/driver/better-sqlite3.d.ts +1 -1
  25. package/dist/driver/better-sqlite3.mjs +44 -6
  26. package/dist/driver/bun.mjs +35 -6
  27. package/dist/driver/expo.mjs +2 -2
  28. package/dist/driver/node.d.ts +1 -1
  29. package/dist/driver/node.mjs +48 -5
  30. package/dist/driver/wa-sqlite.d.ts +109 -0
  31. package/dist/driver/wa-sqlite.mjs +30 -2
  32. package/dist/{errors-C00ed08Q.d.ts → errors-5Nf5ZAEC.d.ts} +19 -1
  33. package/dist/file-migrations/index.d.ts +2 -3
  34. package/dist/file-migrations/index.mjs +1 -1
  35. package/dist/replication/coordinator/etcd.mjs +2 -2
  36. package/dist/replication/index.d.ts +7 -8
  37. package/dist/replication/index.mjs +79 -78
  38. package/dist/server/index.d.ts +111 -28
  39. package/dist/server/index.mjs +1016 -384
  40. package/dist/{sirannon-Cd-lK6T0.d.ts → sirannon-4SspRvP5.d.ts} +3 -3
  41. package/dist/transport/grpc.d.ts +3 -4
  42. package/dist/transport/grpc.mjs +2 -2
  43. package/dist/{types-Lc7ywFx7.d.ts → types-BsjobKbl.d.ts} +2 -2
  44. package/dist/{types-BeozgNPr.d.ts → types-D4p4UyDK.d.ts} +1 -1
  45. package/dist/types-D_hQW1hr.d.ts +494 -0
  46. package/package.json +3 -23
  47. package/dist/chunk-3MCMONVP.mjs +0 -115
  48. package/dist/chunk-74UN4DIE.mjs +0 -14
  49. package/dist/chunk-FB2U2Q3Y.mjs +0 -21
  50. package/dist/chunk-GS7T5YMI.mjs +0 -51
  51. package/dist/chunk-PXKAKK2V.mjs +0 -124
  52. package/dist/index-CLdNrcPz.d.ts +0 -16
  53. package/dist/types-BFSsG77t.d.ts +0 -29
  54. package/dist/types-D-74JiXb.d.ts +0 -265
@@ -0,0 +1,470 @@
1
+ import { BackupError } from './chunk-YPYVQJ4C.mjs';
2
+ import { existsSync, mkdirSync, rmSync, readdirSync, lstatSync } from 'fs';
3
+ import { resolve, dirname, join } from 'path';
4
+
5
+ var BACKUP_FILE_PREFIX = "backup";
6
+ function hasControlCharacters(s) {
7
+ for (let i = 0; i < s.length; i++) {
8
+ const code = s.charCodeAt(i);
9
+ if (code <= 31) return true;
10
+ }
11
+ return false;
12
+ }
13
+ var BackupManager = class {
14
+ async backup(conn, destPath) {
15
+ if (hasControlCharacters(destPath)) {
16
+ throw new BackupError("Backup path contains invalid characters");
17
+ }
18
+ const segments = destPath.split(/[/\\]/);
19
+ if (segments.includes("..")) {
20
+ throw new BackupError("Backup path must not contain directory traversal segments");
21
+ }
22
+ const resolved = resolve(destPath);
23
+ const dir = dirname(resolved);
24
+ if (!existsSync(dir)) {
25
+ try {
26
+ mkdirSync(dir, { recursive: true });
27
+ } catch (err) {
28
+ throw new BackupError(
29
+ `Failed to create backup directory '${dir}': ${err instanceof Error ? err.message : String(err)}`
30
+ );
31
+ }
32
+ }
33
+ if (existsSync(resolved)) {
34
+ throw new BackupError(`Backup destination '${destPath}' already exists`);
35
+ }
36
+ const escaped = resolved.replace(/'/g, "''");
37
+ try {
38
+ await conn.exec(`VACUUM INTO '${escaped}'`);
39
+ } catch (err) {
40
+ try {
41
+ rmSync(resolved, { force: true });
42
+ } catch {
43
+ }
44
+ throw new BackupError(`Backup to '${destPath}' failed: ${err instanceof Error ? err.message : String(err)}`);
45
+ }
46
+ }
47
+ generateFilename() {
48
+ const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
49
+ return `${BACKUP_FILE_PREFIX}-${ts}.db`;
50
+ }
51
+ rotate(dir, maxFiles) {
52
+ if (maxFiles <= 0) return;
53
+ const resolved = resolve(dir);
54
+ if (!existsSync(resolved)) return;
55
+ let entries;
56
+ try {
57
+ entries = readdirSync(resolved).filter((f) => f.startsWith(`${BACKUP_FILE_PREFIX}-`) && f.endsWith(".db")).map((f) => {
58
+ const filePath = join(resolved, f);
59
+ return { path: filePath, mtimeMs: lstatSync(filePath).mtimeMs };
60
+ }).sort((a, b) => b.mtimeMs - a.mtimeMs);
61
+ } catch (err) {
62
+ throw new BackupError(
63
+ `Failed to list backup files in '${dir}': ${err instanceof Error ? err.message : String(err)}`
64
+ );
65
+ }
66
+ for (const entry of entries.slice(maxFiles)) {
67
+ try {
68
+ rmSync(entry.path, { force: true });
69
+ } catch {
70
+ }
71
+ }
72
+ }
73
+ };
74
+
75
+ // src/core/backup/cron.ts
76
+ var MONTH_NAMES = {
77
+ jan: 1,
78
+ feb: 2,
79
+ mar: 3,
80
+ apr: 4,
81
+ may: 5,
82
+ jun: 6,
83
+ jul: 7,
84
+ aug: 8,
85
+ sep: 9,
86
+ oct: 10,
87
+ nov: 11,
88
+ dec: 12
89
+ };
90
+ var WEEKDAY_NAMES = {
91
+ sun: 0,
92
+ mon: 1,
93
+ tue: 2,
94
+ wed: 3,
95
+ thu: 4,
96
+ fri: 5,
97
+ sat: 6
98
+ };
99
+ var SECOND = { name: "second", min: 0, max: 59 };
100
+ var MINUTE = { name: "minute", min: 0, max: 59 };
101
+ var HOUR = { name: "hour", min: 0, max: 23 };
102
+ var DAY_OF_MONTH = { name: "day-of-month", min: 1, max: 31 };
103
+ var MONTH = { name: "month", min: 1, max: 12, names: MONTH_NAMES };
104
+ var DAY_OF_WEEK = { name: "day-of-week", min: 0, max: 7, names: WEEKDAY_NAMES };
105
+ var NICKNAMES = {
106
+ "@yearly": "0 0 1 1 *",
107
+ "@annually": "0 0 1 1 *",
108
+ "@monthly": "0 0 1 * *",
109
+ "@weekly": "0 0 * * 0",
110
+ "@daily": "0 0 * * *",
111
+ "@midnight": "0 0 * * *",
112
+ "@hourly": "0 * * * *"
113
+ };
114
+ var UNSUPPORTED_SYNTAX = /[LW#?]/i;
115
+ var zoneFormatters = /* @__PURE__ */ new Map();
116
+ function zoneFormatter(timeZone) {
117
+ const cached = zoneFormatters.get(timeZone);
118
+ if (cached) {
119
+ return cached;
120
+ }
121
+ const formatter = new Intl.DateTimeFormat("en-US", {
122
+ timeZone,
123
+ hourCycle: "h23",
124
+ year: "numeric",
125
+ month: "numeric",
126
+ day: "numeric",
127
+ hour: "numeric",
128
+ minute: "numeric",
129
+ second: "numeric"
130
+ });
131
+ zoneFormatters.set(timeZone, formatter);
132
+ return formatter;
133
+ }
134
+ function assertValidTimeZone(timeZone) {
135
+ try {
136
+ zoneFormatter(timeZone);
137
+ } catch {
138
+ throw new Error(`unknown timezone '${timeZone}'`);
139
+ }
140
+ }
141
+ function wallClockParts(date, timeZone) {
142
+ if (timeZone === void 0) {
143
+ return {
144
+ year: date.getFullYear(),
145
+ month: date.getMonth() + 1,
146
+ dayOfMonth: date.getDate(),
147
+ hour: date.getHours(),
148
+ minute: date.getMinutes(),
149
+ second: date.getSeconds(),
150
+ dayOfWeek: date.getDay()
151
+ };
152
+ }
153
+ const fields = {};
154
+ for (const part of zoneFormatter(timeZone).formatToParts(date)) {
155
+ if (part.type !== "literal") {
156
+ fields[part.type] = Number(part.value);
157
+ }
158
+ }
159
+ const year = fields.year ?? 0;
160
+ const month = fields.month ?? 1;
161
+ const dayOfMonth = fields.day ?? 1;
162
+ const hour = fields.hour === 24 ? 0 : fields.hour ?? 0;
163
+ return {
164
+ year,
165
+ month,
166
+ dayOfMonth,
167
+ hour,
168
+ minute: fields.minute ?? 0,
169
+ second: fields.second ?? 0,
170
+ dayOfWeek: new Date(Date.UTC(year, month - 1, dayOfMonth)).getUTCDay()
171
+ };
172
+ }
173
+ function resolveValue(token, spec) {
174
+ let value;
175
+ if (/^\d+$/.test(token)) {
176
+ value = Number(token);
177
+ } else if (spec.names) {
178
+ const named = spec.names[token.toLowerCase()];
179
+ if (named === void 0) {
180
+ throw new Error(`unknown name '${token}' in ${spec.name} field`);
181
+ }
182
+ value = named;
183
+ } else {
184
+ throw new Error(`invalid value '${token}' in ${spec.name} field`);
185
+ }
186
+ if (value < spec.min || value > spec.max) {
187
+ throw new Error(`value ${value} out of range ${spec.min}-${spec.max} in ${spec.name} field`);
188
+ }
189
+ return value;
190
+ }
191
+ function parseListItem(item, spec, out) {
192
+ const slashParts = item.split("/");
193
+ if (slashParts.length > 2) {
194
+ throw new Error(`invalid step syntax '${item}' in ${spec.name} field`);
195
+ }
196
+ const [base, stepToken] = slashParts;
197
+ let step = 1;
198
+ if (stepToken !== void 0) {
199
+ if (!/^\d+$/.test(stepToken)) {
200
+ throw new Error(`invalid step '${stepToken}' in ${spec.name} field`);
201
+ }
202
+ step = Number(stepToken);
203
+ if (step < 1) {
204
+ throw new Error(`step must be at least 1 in ${spec.name} field`);
205
+ }
206
+ }
207
+ let start;
208
+ let end;
209
+ if (base === "*") {
210
+ start = spec.min;
211
+ end = spec.max;
212
+ } else if (base.includes("-")) {
213
+ const rangeParts = base.split("-");
214
+ if (rangeParts.length !== 2) {
215
+ throw new Error(`invalid range '${base}' in ${spec.name} field`);
216
+ }
217
+ start = resolveValue(rangeParts[0], spec);
218
+ end = resolveValue(rangeParts[1], spec);
219
+ } else {
220
+ start = resolveValue(base, spec);
221
+ end = stepToken !== void 0 ? spec.max : start;
222
+ }
223
+ if (start > end) {
224
+ throw new Error(`range start ${start} is after end ${end} in ${spec.name} field`);
225
+ }
226
+ for (let value = start; value <= end; value += step) {
227
+ out.add(spec === DAY_OF_WEEK ? value % 7 : value);
228
+ }
229
+ }
230
+ function parseField(raw, spec) {
231
+ if (raw === "") {
232
+ throw new Error(`empty ${spec.name} field`);
233
+ }
234
+ if (UNSUPPORTED_SYNTAX.test(raw)) {
235
+ throw new Error(`unsupported syntax '${raw}' in ${spec.name} field`);
236
+ }
237
+ const values = /* @__PURE__ */ new Set();
238
+ for (const item of raw.split(",")) {
239
+ parseListItem(item, spec, values);
240
+ }
241
+ return values;
242
+ }
243
+ function isFullRange(values, spec) {
244
+ const span = spec === DAY_OF_WEEK ? 7 : spec.max - spec.min + 1;
245
+ return values.size >= span;
246
+ }
247
+ function expandNickname(expression) {
248
+ const expanded = NICKNAMES[expression.toLowerCase()];
249
+ if (expanded === void 0) {
250
+ throw new Error(`unsupported nickname '${expression}'`);
251
+ }
252
+ return expanded;
253
+ }
254
+ var ParsedCron = class {
255
+ constructor(hasSeconds, seconds, minutes, hours, daysOfMonth, months, daysOfWeek, domRestricted, dowRestricted) {
256
+ this.hasSeconds = hasSeconds;
257
+ this.seconds = seconds;
258
+ this.minutes = minutes;
259
+ this.hours = hours;
260
+ this.daysOfMonth = daysOfMonth;
261
+ this.months = months;
262
+ this.daysOfWeek = daysOfWeek;
263
+ this.domRestricted = domRestricted;
264
+ this.dowRestricted = dowRestricted;
265
+ }
266
+ matches(parts) {
267
+ if (this.hasSeconds && !this.seconds.has(parts.second)) {
268
+ return false;
269
+ }
270
+ if (!this.minutes.has(parts.minute)) {
271
+ return false;
272
+ }
273
+ if (!this.hours.has(parts.hour)) {
274
+ return false;
275
+ }
276
+ if (!this.months.has(parts.month)) {
277
+ return false;
278
+ }
279
+ const domMatch = this.daysOfMonth.has(parts.dayOfMonth);
280
+ const dowMatch = this.daysOfWeek.has(parts.dayOfWeek);
281
+ if (this.domRestricted && this.dowRestricted) {
282
+ return domMatch || dowMatch;
283
+ }
284
+ return domMatch && dowMatch;
285
+ }
286
+ };
287
+ function parseCron(expression) {
288
+ const trimmed = expression.trim();
289
+ if (trimmed === "") {
290
+ throw new Error("cron expression is empty");
291
+ }
292
+ const normalized = trimmed.startsWith("@") ? expandNickname(trimmed) : trimmed;
293
+ const fields = normalized.split(/\s+/);
294
+ let hasSeconds;
295
+ let secondRaw;
296
+ let minuteRaw;
297
+ let hourRaw;
298
+ let domRaw;
299
+ let monthRaw;
300
+ let dowRaw;
301
+ if (fields.length === 6) {
302
+ hasSeconds = true;
303
+ [secondRaw, minuteRaw, hourRaw, domRaw, monthRaw, dowRaw] = fields;
304
+ } else if (fields.length === 5) {
305
+ hasSeconds = false;
306
+ secondRaw = "0";
307
+ [minuteRaw, hourRaw, domRaw, monthRaw, dowRaw] = fields;
308
+ } else {
309
+ throw new Error(`expected 5 or 6 fields but got ${fields.length}`);
310
+ }
311
+ const daysOfMonth = parseField(domRaw, DAY_OF_MONTH);
312
+ const daysOfWeek = parseField(dowRaw, DAY_OF_WEEK);
313
+ return new ParsedCron(
314
+ hasSeconds,
315
+ parseField(secondRaw, SECOND),
316
+ parseField(minuteRaw, MINUTE),
317
+ parseField(hourRaw, HOUR),
318
+ daysOfMonth,
319
+ parseField(monthRaw, MONTH),
320
+ daysOfWeek,
321
+ !isFullRange(daysOfMonth, DAY_OF_MONTH),
322
+ !isFullRange(daysOfWeek, DAY_OF_WEEK)
323
+ );
324
+ }
325
+
326
+ // src/core/backup/scheduler.ts
327
+ var DEFAULT_MAX_FILES = 5;
328
+ var MINUTE_RESOLUTION_MS = 6e4;
329
+ var SECOND_RESOLUTION_MS = 1e3;
330
+ var DST_LOOKBACK_MS = 3 * 60 * 60 * 1e3;
331
+ function toError(value) {
332
+ if (value instanceof Error) {
333
+ return value;
334
+ }
335
+ return new BackupError(typeof value === "string" ? value : "Scheduled backup failed");
336
+ }
337
+ function slotKey(parts, hasSeconds) {
338
+ const base = `${parts.year}-${parts.month}-${parts.dayOfMonth}-${parts.hour}-${parts.minute}`;
339
+ return hasSeconds ? `${base}-${parts.second}` : base;
340
+ }
341
+ function zoneOffsetMs(date, timeZone) {
342
+ const parts = wallClockParts(date, timeZone);
343
+ const wallAsUtc = Date.UTC(parts.year, parts.month - 1, parts.dayOfMonth, parts.hour, parts.minute, parts.second);
344
+ return wallAsUtc - Math.floor(date.getTime() / 1e3) * 1e3;
345
+ }
346
+ function isRepeatedWallClock(now, nowParts, timeZone, hasSeconds) {
347
+ const offsetNow = zoneOffsetMs(now, timeZone);
348
+ const offsetBefore = zoneOffsetMs(new Date(now.getTime() - DST_LOOKBACK_MS), timeZone);
349
+ if (offsetNow >= offsetBefore) {
350
+ return false;
351
+ }
352
+ const shift = offsetBefore - offsetNow;
353
+ const firstOccurrence = wallClockParts(new Date(now.getTime() - shift), timeZone);
354
+ return slotKey(firstOccurrence, hasSeconds) === slotKey(nowParts, hasSeconds);
355
+ }
356
+ var INITIAL_TICK_STATE = {
357
+ lastFiredSlot: "",
358
+ lastFiredEpoch: Number.NEGATIVE_INFINITY
359
+ };
360
+ function evaluateTick(now, cron, timeZone, state) {
361
+ const nowEpoch = now.getTime();
362
+ if (nowEpoch <= state.lastFiredEpoch) {
363
+ return { shouldFire: false, nextState: state };
364
+ }
365
+ const parts = wallClockParts(now, timeZone);
366
+ if (!cron.matches(parts)) {
367
+ return { shouldFire: false, nextState: state };
368
+ }
369
+ const slot = slotKey(parts, cron.hasSeconds);
370
+ if (slot === state.lastFiredSlot) {
371
+ return { shouldFire: false, nextState: state };
372
+ }
373
+ if (isRepeatedWallClock(now, parts, timeZone, cron.hasSeconds)) {
374
+ return { shouldFire: false, nextState: state };
375
+ }
376
+ return { shouldFire: true, nextState: { lastFiredSlot: slot, lastFiredEpoch: nowEpoch } };
377
+ }
378
+ function runDirect(op) {
379
+ return op();
380
+ }
381
+ var BackupScheduler = class {
382
+ manager;
383
+ constructor(manager) {
384
+ this.manager = manager ?? new BackupManager();
385
+ }
386
+ schedule(conn, options, runExclusive = runDirect) {
387
+ const { cron: cronExpr, destDir, maxFiles = DEFAULT_MAX_FILES, onError, timezone } = options;
388
+ let cron;
389
+ try {
390
+ cron = parseCron(cronExpr);
391
+ } catch (err) {
392
+ throw new BackupError(
393
+ `Invalid cron expression '${cronExpr}': ${err instanceof Error ? err.message : String(err)}`
394
+ );
395
+ }
396
+ if (timezone !== void 0) {
397
+ try {
398
+ assertValidTimeZone(timezone);
399
+ } catch (err) {
400
+ throw new BackupError(`Invalid timezone '${timezone}': ${err instanceof Error ? err.message : String(err)}`);
401
+ }
402
+ }
403
+ const resolvedDir = resolve(destDir);
404
+ if (!existsSync(resolvedDir)) {
405
+ try {
406
+ mkdirSync(resolvedDir, { recursive: true });
407
+ } catch (err) {
408
+ throw new BackupError(
409
+ `Failed to create backup directory '${destDir}': ${err instanceof Error ? err.message : String(err)}`
410
+ );
411
+ }
412
+ }
413
+ return this.run(conn, cron, resolvedDir, maxFiles, timezone, runExclusive, onError);
414
+ }
415
+ run(conn, cron, resolvedDir, maxFiles, timezone, runExclusive, onError) {
416
+ const tickMs = cron.hasSeconds ? SECOND_RESOLUTION_MS : MINUTE_RESOLUTION_MS;
417
+ let timer = null;
418
+ let stopped = false;
419
+ let running = false;
420
+ let tickState = INITIAL_TICK_STATE;
421
+ const runBackup = async () => {
422
+ try {
423
+ const destPath = join(resolvedDir, this.manager.generateFilename());
424
+ await runExclusive(() => this.manager.backup(conn, destPath));
425
+ this.manager.rotate(resolvedDir, maxFiles);
426
+ } catch (err) {
427
+ if (onError) {
428
+ try {
429
+ onError(toError(err));
430
+ } catch {
431
+ }
432
+ }
433
+ } finally {
434
+ running = false;
435
+ }
436
+ };
437
+ const scheduleNext = () => {
438
+ if (stopped) {
439
+ return;
440
+ }
441
+ const delay = tickMs - Date.now() % tickMs;
442
+ timer = setTimeout(onTick, delay);
443
+ timer.unref?.();
444
+ };
445
+ const onTick = () => {
446
+ if (stopped) {
447
+ return;
448
+ }
449
+ if (!running) {
450
+ const { shouldFire, nextState } = evaluateTick(/* @__PURE__ */ new Date(), cron, timezone, tickState);
451
+ if (shouldFire) {
452
+ tickState = nextState;
453
+ running = true;
454
+ void runBackup();
455
+ }
456
+ }
457
+ scheduleNext();
458
+ };
459
+ scheduleNext();
460
+ return () => {
461
+ stopped = true;
462
+ if (timer) {
463
+ clearTimeout(timer);
464
+ timer = null;
465
+ }
466
+ };
467
+ }
468
+ };
469
+
470
+ export { BackupManager, BackupScheduler };
@@ -59,6 +59,12 @@ var CDCError = class extends SirannonError {
59
59
  this.name = "CDCError";
60
60
  }
61
61
  };
62
+ var ForbiddenSqlError = class extends SirannonError {
63
+ constructor(message) {
64
+ super(message, "FORBIDDEN_SQL");
65
+ this.name = "ForbiddenSqlError";
66
+ }
67
+ };
62
68
  var BackupError = class extends SirannonError {
63
69
  constructor(message) {
64
70
  super(message, "BACKUP_ERROR");
@@ -77,6 +83,14 @@ var MaxDatabasesError = class extends SirannonError {
77
83
  this.name = "MaxDatabasesError";
78
84
  }
79
85
  };
86
+ var WriteOverloadError = class extends SirannonError {
87
+ constructor(limit, retryAfterMs) {
88
+ super(`Write rejected: ${limit} writes already pending`, "WRITE_OVERLOADED");
89
+ this.limit = limit;
90
+ this.retryAfterMs = retryAfterMs;
91
+ this.name = "WriteOverloadError";
92
+ }
93
+ };
80
94
  var ExtensionError = class extends SirannonError {
81
95
  constructor(path, cause) {
82
96
  super(
@@ -87,4 +101,4 @@ var ExtensionError = class extends SirannonError {
87
101
  }
88
102
  };
89
103
 
90
- export { BackupError, CDCError, ConnectionPoolError, DatabaseAlreadyExistsError, DatabaseNotFoundError, ExtensionError, HookDeniedError, MaxDatabasesError, MigrationError, QueryError, ReadOnlyError, SirannonError, TransactionError };
104
+ export { BackupError, CDCError, ConnectionPoolError, DatabaseAlreadyExistsError, DatabaseNotFoundError, ExtensionError, ForbiddenSqlError, HookDeniedError, MaxDatabasesError, MigrationError, QueryError, ReadOnlyError, SirannonError, TransactionError, WriteOverloadError };