@voltro/data-transfer 0.85.0 → 0.86.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/dist/index.js CHANGED
@@ -1,2673 +1,28 @@
1
- import { Cause as e, Data as t, Effect as n, Exit as r, Metric as i, Option as a, Schedule as o, Schema as s, Stream as c } from "effect";
2
- import { DATA_TRANSFERS_TABLE as l, REPLACE_IN_PROGRESS_TABLE as u, STAGING_TABLE_PREFIX as d, _voltroDataTransfersTable as f, classifyConstraintViolation as p, derivedRowId as m, ensureTableRegistered as h, eq as g, extractDbCause as _, getTable as v, inSet as y, isNotNull as b, or as x, recordsTable as S, registerTable as ee, streamTable as C, suspendWriteRecorders as te, suspendWriteRecordersEffect as ne, unregisterTable as re } from "@voltro/database";
3
- import { fingerprintSchema as ie } from "@voltro/database/sql";
4
- import { createCipheriv as ae, createDecipheriv as oe, createHash as w, createHmac as T, randomBytes as E, randomUUID as se, scrypt as ce } from "node:crypto";
5
- import { copyFile as le, createReadStream as ue, createWriteStream as de, existsSync as fe, rename as D, statSync as O, unlinkSync as k } from "node:fs";
6
- import { STORAGE_REFS_TABLE as pe, fromNodeReadable as me, getObjectStream as A, putObjectStream as he, toNodeReadable as j } from "@voltro/plugin-storage";
7
- import * as M from "node:zlib";
8
- import { PassThrough as ge, Readable as _e } from "node:stream";
9
- import { SqlClient as ve } from "effect/sql";
10
- import { mkdir as N, readdir as ye, stat as be } from "node:fs/promises";
11
- import { basename as P, delimiter as F, dirname as xe, join as I, resolve as L, sep as Se } from "node:path";
12
- import { promisify as Ce } from "node:util";
13
- import { spawn as we } from "node:child_process";
14
- //#region src/consistency.ts
15
- var Te = (e) => {
16
- switch (e) {
17
- case "postgres": return ["SET TRANSACTION ISOLATION LEVEL REPEATABLE READ"];
18
- case "mysql":
19
- case "mariadb": return ["SET TRANSACTION ISOLATION LEVEL REPEATABLE READ"];
20
- case "mssql": return ["SET TRANSACTION ISOLATION LEVEL SNAPSHOT"];
21
- case "sqlite":
22
- case "turso": return [];
23
- default: return [];
24
- }
25
- }, Ee = {
26
- email: { fake: "email" },
27
- fullName: { fake: "fullName" },
28
- firstName: { fake: "firstName" },
29
- lastName: { fake: "lastName" },
30
- username: { fake: "username" },
31
- phone: { fake: "phone" },
32
- address: { fake: "address" },
33
- company: { fake: "company" },
34
- url: { fake: "url" },
35
- ip: { fake: "ip" },
36
- creditCard: { fake: "creditCard" },
37
- date: "dateShift",
38
- secret: "null",
39
- freeText: "redact"
40
- }, De = (e) => {
41
- let t = {};
42
- for (let n of e.tables) {
43
- let e = {};
44
- for (let t of n.columns) e[t.name] = {
45
- ...t.sensitive ? { sensitive: t.sensitive } : {},
46
- ...t.safe ? { safe: !0 } : {},
47
- columnType: t.type
48
- };
49
- t[n.name] = e;
50
- }
51
- return t;
52
- }, Oe = (e, t, n, r) => {
53
- let i = r.columns?.[`${e}.${t}`];
54
- if (i) return i;
55
- if (n?.sensitive) {
56
- let e = n.sensitive.class;
57
- return r.classes?.[e] ?? Ee[e] ?? "redact";
58
- }
59
- return n?.safe ? r.onSafe ?? "keep" : n?.columnType === "id" || n?.columnType === "reference" ? "keep" : r.onUnclassified ?? "error";
60
- }, ke = (e, t) => T("sha256", e).update(t == null ? "∅" : String(t)).digest(), R = (e, t) => e.readUInt32BE(t * 4 % (e.length - 4)), Ae = [
61
- "Ada",
62
- "Alan",
63
- "Grace",
64
- "Linus",
65
- "Ida",
66
- "Ken",
67
- "Barbara",
68
- "Dennis",
69
- "Radia",
70
- "Guido",
71
- "Margaret",
72
- "Bjarne"
73
- ], je = [
74
- "Lovelace",
75
- "Turing",
76
- "Hopper",
77
- "Torvalds",
78
- "Rhodes",
79
- "Thompson",
80
- "Liskov",
81
- "Ritchie",
82
- "Perlman",
83
- "Rossum",
84
- "Hamilton",
85
- "Stroustrup"
86
- ], Me = [
87
- "Maple",
88
- "Oak",
89
- "Cedar",
90
- "Pine",
91
- "Elm",
92
- "Birch",
93
- "Willow",
94
- "Ash"
95
- ], Ne = [
96
- "Initech",
97
- "Hooli",
98
- "Umbrella",
99
- "Acme",
100
- "Globex",
101
- "Soylent",
102
- "Stark",
103
- "Wayne"
104
- ], z = (e, t, n) => e[R(t, n) % e.length], Pe = (e, t, n) => {
105
- let r = ke(n, `${e}:${String(t)}`), i = R(r, 1) % 1e4;
106
- switch (e) {
107
- case "email": return `${z(Ae, r, 0).toLowerCase()}.${z(je, r, 2).toLowerCase()}${i}@example.com`;
108
- case "fullName": return `${z(Ae, r, 0)} ${z(je, r, 1)}`;
109
- case "firstName": return z(Ae, r, 0);
110
- case "lastName": return z(je, r, 1);
111
- case "username": return `${z(Ae, r, 0).toLowerCase()}${i}`;
112
- case "phone": return `+1${String(R(r, 0) % 1e10).padStart(10, "0")}`;
113
- case "address": return `${R(r, 0) % 9899 + 100} ${z(Me, r, 1)} St`;
114
- case "company": return z(Ne, r, 0);
115
- case "url": return `https://example.com/${r.toString("hex").slice(0, 12)}`;
116
- case "ip": return `${R(r, 0) % 256}.${R(r, 1) % 256}.${R(r, 2) % 256}.${R(r, 3) % 254 + 1}`;
117
- case "creditCard": return Fe(`4${r.toString("hex").replace(/\D/g, "").padEnd(14, "0").slice(0, 14)}`);
118
- default: return `${e}_${r.toString("hex").slice(0, 8)}`;
119
- }
120
- }, Fe = (e) => {
121
- let t = e.slice(0, 15).split("").map(Number), n = 0;
122
- t.forEach((e, t) => {
123
- let r = e;
124
- (15 - t) % 2 == 1 && (r *= 2, r > 9 && (r -= 9)), n += r;
125
- });
126
- let r = (10 - n % 10) % 10;
127
- return e.slice(0, 15) + String(r);
128
- }, Ie = 864e5, Le = (e, t) => {
129
- let n = e instanceof Date ? e.getTime() : Date.parse(String(e));
130
- if (Number.isNaN(n)) return e;
131
- let r = R(ke(t, "date-shift-offset"), 0) % 730 - 365, i = new Date(n + r * Ie);
132
- return e instanceof Date ? i : i.toISOString();
133
- }, B = (e, t) => {
134
- if (t.value === null || t.value === void 0) return null;
135
- if (e === "error") throw Error(`masking: unresolved 'error' action for ${t.table}.${t.column}`);
136
- if (e === "keep") return t.value;
137
- if (e === "null") return null;
138
- if (e === "redact") return t.columnType === "text" || typeof t.value == "string" ? "[redacted]" : null;
139
- if (e === "hash") return ke(t.seed, t.value).toString("hex").slice(0, 16);
140
- if (e === "dateShift") return Le(t.value, t.seed);
141
- if (typeof e == "object" && "fake" in e) return Pe(e.fake, t.value, t.seed);
142
- if (typeof e == "object" && "custom" in e) return e.custom(t);
143
- if (typeof e == "object" && "json" in e) return Re(e, t);
144
- throw Error(`masking: '${t.table}.${t.column}' resolved to an action this build does not understand (${JSON.stringify(e)}). Refusing to copy the value through. Valid actions: 'keep' | 'null' | 'redact' | 'hash' | 'dateShift' | { fake: '<class>' } | { custom: fn } | { json: { fields, rest? } }.`);
145
- }, Re = (e, t) => {
146
- let { fields: n, rest: r = "redact" } = e.json, i = typeof t.value == "string" ? ze(t.value) : {
147
- ok: !0,
148
- value: t.value
149
- };
150
- if (!i.ok) return B(r, {
151
- ...t,
152
- columnType: "text"
153
- });
154
- let a = (e, i) => {
155
- if (e == null) return null;
156
- if (Array.isArray(e)) return e.map((e, t) => a(e, `${i}[${t}]`));
157
- if (typeof e == "object") {
158
- let a = {};
159
- for (let [o, s] of Object.entries(e)) {
160
- let e = n[o] ?? r, c = `${i}.${o}`;
161
- a[o] = typeof e == "object" && "json" in e ? Re(e, {
162
- ...t,
163
- value: s,
164
- column: c,
165
- columnType: "json"
166
- }) : B(e, {
167
- ...t,
168
- value: s,
169
- column: c,
170
- columnType: typeof s == "string" ? "text" : "json"
171
- });
172
- }
173
- return a;
174
- }
175
- return B(r, {
176
- ...t,
177
- value: e,
178
- column: i,
179
- columnType: typeof e == "string" ? "text" : "json"
180
- });
181
- }, o = a(i.value, t.column);
182
- return typeof t.value == "string" ? JSON.stringify(o) : o;
183
- }, ze = (e) => {
184
- try {
185
- return {
186
- ok: !0,
187
- value: JSON.parse(e)
188
- };
189
- } catch {
190
- return { ok: !1 };
191
- }
192
- }, Be = (e) => {
193
- if (typeof e == "string") return e === "keep" || e === "null" || e === "redact" || e === "hash" || e === "dateShift" || e === "error";
194
- if (typeof e != "object" || !e) return !1;
195
- if ("fake" in e) return typeof e.fake == "string";
196
- if ("custom" in e) return typeof e.custom == "function";
197
- if ("json" in e) {
198
- let t = e.json;
199
- if (typeof t != "object" || !t) return !1;
200
- let { fields: n, rest: r } = t;
201
- return typeof n != "object" || !n ? !1 : Object.values(n).every(Be) && (r === void 0 || Be(r));
202
- }
203
- return !1;
204
- }, Ve = (e, t, n) => {
205
- let r = {}, i = [], a = [];
206
- for (let o of e) for (let e of o.columns) {
207
- let s = `${o.name}.${e.name}`, c = Oe(o.name, e.name, t[o.name]?.[e.name], n);
208
- if (c === "error") {
209
- i.push(s);
210
- continue;
211
- }
212
- if (!Be(c)) {
213
- a.push({
214
- column: s,
215
- action: c
216
- });
217
- continue;
218
- }
219
- r[s] = c;
220
- }
221
- return {
222
- actions: r,
223
- unclassified: i,
224
- invalid: a
225
- };
226
- }, He = [
227
- {
228
- name: "email",
229
- re: /[\w.+-]+@[\w-]+\.[\w.-]+/
230
- },
231
- {
232
- name: "phone",
233
- re: /(?:\+?\d[\d\s().-]{8,}\d)/
234
- },
235
- {
236
- name: "ipv4",
237
- re: /\b(?:\d{1,3}\.){3}\d{1,3}\b/
238
- },
239
- {
240
- name: "creditCard",
241
- re: /\b(?:\d[ -]?){13,16}\b/
242
- }
243
- ], Ue = (e, t, n) => {
244
- let r = [], i = /* @__PURE__ */ new Set();
245
- for (let [a, o] of Object.entries(n)) {
246
- if (!a.startsWith(`${t}.`) || o !== "keep") continue;
247
- let n = a.slice(t.length + 1);
248
- for (let a of e) {
249
- let e = a[n];
250
- if (typeof e != "string" || e.length === 0) continue;
251
- let o = He.find((t) => t.re.test(e));
252
- if (o && !i.has(n)) {
253
- i.add(n), r.push({
254
- table: t,
255
- column: n,
256
- pattern: o.name,
257
- sample: e.slice(0, 40)
258
- });
259
- break;
260
- }
261
- }
262
- }
263
- return r;
264
- }, We = (e, t, n, r, i) => {
265
- let a = {};
266
- for (let [o, s] of Object.entries(e)) a[o] = B(n[`${t}.${o}`] ?? "keep", {
267
- value: s,
268
- table: t,
269
- column: o,
270
- seed: r,
271
- ...i?.[o] ? { columnType: i[o] } : {}
272
- });
273
- return a;
274
- }, Ge = /* @__PURE__ */ new Map([
275
- ["_voltro_inspect_create_receipts", "inspect creation commands accepted by THIS deployment — a foreign receipt would replay or suppress a creation the target never accepted"],
276
- ["_voltro_isr_cache", "pages rendered by THIS deployment’s web processes — a foreign row would serve another environment’s HTML here"],
277
- ["_voltro_web_replicas", "heartbeats of the web processes running HERE — a foreign row advertises replicas that do not exist in the target"],
278
- ["_voltro_aggregate_meta", "materialized refresh generations computed HERE — a foreign row would claim that the target refreshed its own source data"],
279
- ["_voltro_aggregate_rows", "materialized rows tied to this deployment’s refresh generation — the target recomputes them from its own data"],
280
- ["_voltro_timeline", "captured source mutations from this deployment — foreign rows invent local timeline history"],
281
- ["_voltro_timeline_meta", "capture epoch and source cursor of this database — foreign metadata claims continuity with another database"],
282
- ["_voltro_timeline_leases", "active capture readers of this database — foreign leases claim readers that do not exist here"],
283
- ["_voltro_timeline_slots", "bounded capture slots belonging to this source epoch — foreign slots invent local mutation history"],
284
- ["_voltro_plugin_migrations", "plugin migration steps applied to this database — a foreign ledger would skip steps that the target has not run"],
285
- ["_voltro_migration_plans", "the schema fingerprint THIS deployment applied — a foreign row makes the next boot refuse"],
286
- ["_voltro_migrations", "which file-based migrations ran HERE — a foreign row marks unrun migrations as done"],
287
- ["_voltro_plugin_migrations", "which plugin migration steps ran HERE — a foreign row marks a plugin's unrun steps as done"],
288
- ["_voltro_schema_owner", "which app owns THIS database — a foreign row hands the target's schema to the source app, so the target's own app refuses to boot on it"],
289
- ["_voltro_deploys", "the deploy THIS database is in the middle of, with the baseline to undo it from — a foreign row makes the next apply undo a deploy the target never started"],
290
- ["_voltro_devices", "devices paired with THIS deployment — token hashes; a foreign row makes a device paired elsewhere authenticate here"],
291
- ["_voltro_seeds", "which seeds ran HERE — a foreign row suppresses seeding the target still needs"],
292
- ["_voltro_cdc_offsets", "binlog/WAL positions of the SOURCE server — meaningless and unsafe against another one"],
293
- ["_voltro_notification_push_subscriptions", "browser push endpoints bound to THIS deployment's VAPID key — the browser's applicationServerKey is derived from `VOLTRO_VAPID_PRIVATE_KEY`, so a push service refuses a delivery signed by another one. A foreign row makes the target attempt a delivery that must fail, and the auto-prune then deletes a subscription that was valid where it came from"],
294
- ["_voltro_schedule_claims", "live leases held by THIS deployment's replicas — a foreign row hands out a lease nobody holds"],
295
- ["_voltro_change_claims", "the record that ONE of THIS deployment's replicas already handled a given change — a foreign row asserts that an effect fired here when it fired somewhere else, so the subscriber or reaction it gates stands down for a notification nobody sent"],
296
- ["_voltro_replica_observations", "what THIS deployment's replicas last published about themselves — a foreign row invents a replica that does not exist here, and a fleet answer would then report it as present or, once it ages, as a replica that has stopped answering"],
297
- ["_voltro_data_transfers", "which bundles moved through HERE, in or out — a foreign row claims a transfer this deployment never ran"],
298
- ["_voltro_replace_in_progress", "the marker a destructive import HERE left behind — a foreign row refuses a boot over a healthy database"],
299
- ["_voltro_wakeups", "pending wakeups addressed to THIS deployment's runners"],
300
- ["_voltro_workflow_watermarks", "how far a consumer has read THIS deployment's event journal — a foreign position skips events"],
301
- ["_voltro_workflow_pending", "starts waiting to be drained HERE — a foreign row makes the target RUN a workflow somebody queued elsewhere"],
302
- ["_voltro_workflow_admissions", "the in-flight accounting a concurrency gate decides on — a foreign row makes the target refuse or admit wrongly"],
303
- ["_voltro_workflow_pauses", "which workflows an operator paused HERE — a foreign row silently pauses them in the target"],
304
- ["_voltro_workflow_event_deliveries", "which (event, trigger) pairs were already delivered HERE — a foreign row SUPPRESSES a delivery the target owes"],
305
- ["_voltro_traces", "spans recorded HERE — a foreign span claims a request this deployment never served"],
306
- ["_voltro_undo_log", "the before-images of mutations made HERE — a foreign row offers to undo a write into rows the target does not have"],
307
- ["_voltro_outbox", "messages waiting to be SENT by this deployment — a foreign row makes the target deliver something queued elsewhere"],
308
- ["_voltro_outbox_attempts", "the delivery attempts this deployment made — they belong to its outbox rows and to no other"],
309
- ["_voltro_ai_inferences", "offloaded inference work queued for THIS deployment's runners — a foreign row makes the target run somebody else's"],
310
- ["_voltro_ai_budget", "the live spend RESERVATION counter a budget refuses against — a foreign row makes the target refuse calls it should allow"],
311
- ["_voltro_ai_usage", "what this deployment spent on model calls — a foreign row overstates a bill nobody here ran up"],
312
- ["_voltro_budget_holds", "in-flight holds against a running deployment's budget — the same accounting `_voltro_workflow_admissions` keeps"],
313
- ["_voltro_ai_budget_reservations", "reservations held against THIS deployment's live spend counter — a foreign row makes the target refuse calls it should allow, exactly as `_voltro_ai_budget` does"],
314
- ["_voltro_storage_grants", "short-lived upload/download grants issued HERE — a foreign grant authorises nothing in the target"],
315
- ["_voltro_storage_usage", "how much storage this deployment accounts for — a foreign row makes a quota decision on somebody else's bytes"],
316
- ["_voltro_storage_operations", "the puts and deletes in flight against THIS provider, by physical key — a foreign row makes the target finish or undo an operation on a key it does not have"],
317
- ["_voltro_storage_object_heads", "which physical generation of each content-addressed object is live HERE — a foreign row points the target at a key it does not own"],
318
- ["_voltro_storage_object_claims", "which callers HERE still hold a shared object — a foreign row keeps the target retaining a key for a claimant it never had, or releases one it does"],
319
- ["_voltro_storage_put_intents", "the ref a reservation HERE will become — a foreign row makes the target confirm a write it never received"],
320
- ["_voltro_storage_operation_locks", "the per-tenant serialisation rows the operation transactions lock — a foreign row is a lock nobody here holds"],
321
- ["_voltro_idempotency", "the request keys this deployment has already answered — a foreign key SUPPRESSES a request the target never served"],
322
- ["_voltro_webhook_deliveries", "the delivery attempts this deployment made to a subscriber — a foreign row claims a call the target never placed"],
323
- ["_voltro_schedule_runs", "every schedule firing that happened HERE, replica ids included — a foreign row is history the target did not live"],
324
- ["_voltro_schedule_backfill_requests", "backfills queued for THIS deployment's runners — a foreign row makes the target re-run a range somebody asked another deployment for"],
325
- ["_voltro_tenant_namespaces", "which physical namespace each tenant lives in HERE — a foreign row points a tenant at a namespace the target does not have"],
326
- ["_voltro_workflow_execution_placements", "which physical store holds each execution HERE — a foreign row sends the target looking in a database it does not own"],
327
- ["_voltro_workflow_pool_permits", "concurrency permits held against a running deployment's budget — the same accounting `_voltro_workflow_admissions` keeps"],
328
- ["_voltro_workflow_admission_locks", "the locks an admission decision is being made under RIGHT NOW — a foreign row blocks or releases a decision the target is not making"],
329
- ["_voltro_workflow_submissions", "starts submitted HERE and not yet admitted — a foreign row makes the target admit a start somebody handed to another deployment"],
330
- ["_voltro_workflow_start_receipts", "the caller-facing identity of a start placed HERE, resolved through this deployment's placements — a foreign receipt names a run the target never accepted"],
331
- ["_voltro_workflow_control_requests", "cancel/resume traffic issued against executions HERE — a foreign row claims an operation nobody asked the target to perform"],
332
- ["_voltro_workflow_queued_cancellations", "cancellations waiting on a run that is queued HERE — a foreign row cancels something the target has not queued"],
333
- ["_voltro_workflow_restart_withdrawals", "withdrawal fences for restart requests issued HERE — a foreign row would block a target restart that its operator never withdrew"],
334
- ["_voltro_workflow_restart_requests", "restarts pending against this deployment's generations — a foreign row restarts a generation the target never entered"],
335
- ["_voltro_workflow_retry_requests", "retry intents an operator raised against runs HERE, keyed by their own request id — a foreign row binds the target to an intent nobody raised against it, and lets a later observation read somebody else's execution"],
336
- ["_voltro_workflow_parent_closures", "which parents are still waiting on children in flight HERE — a foreign row holds a parent open against a child the target is not running"],
337
- ["_voltro_workflow_failure_reports", "failures being handed off to a runner HERE — a foreign row makes the target take delivery of somebody else's failure"],
338
- ["_voltro_cdc_log", "the change rows this deployment captured from its own database — a foreign row replays a change that never happened here"],
339
- ["_voltro_notification_deliveries", "the notification attempts this deployment made — a foreign row claims a send the target never performed"],
340
- ["_voltro_notification_held", "notifications waiting to be SENT from here — a foreign row makes the target deliver something queued elsewhere"],
341
- ["_voltro_webhook_event_stats", "per-deployment delivery counters a backoff decision reads — foreign counts steer the target's retries"],
342
- ["_voltro_webhook_rate_windows", "the live rate-limit windows this deployment is inside — a foreign window throttles or releases wrongly"],
343
- ["cluster_runners", "the workflow runners of THIS deployment and their addresses — a foreign row routes shards to a runner that does not exist here"],
344
- ["cluster_locks", "the shard leases THIS deployment's runners hold — a foreign row holds a shard for nobody, and its work stalls"],
345
- ["cluster_messages", "workflow messages in flight HERE — a foreign row makes the target run work that was started, and may be running, elsewhere"],
346
- ["cluster_replies", "replies to those messages — a foreign row answers a request the target never sent"],
347
- ["cluster_migrations", "which engine migrations ran HERE — a foreign row marks the target's engine tables migrated when they are not"]
348
- ]), Ke = /* @__PURE__ */ new Map([
349
- ["_voltro_comment_thread_events", "the status history of the app's comment threads — belongs with those threads"],
350
- ["_voltro_agent_threads", "conversations the app's users had — app data"],
351
- ["_voltro_agent_messages", "the messages in those conversations — app data"],
352
- ["_voltro_prompts", "prompt definitions the app authored — app data"],
353
- ["_voltro_api_keys", "keys the app issued (hashed) — app data, and a restore without them locks callers out"],
354
- ["_voltro_tenant_selections", "the tenant each user chose to act in — app data, verified against memberships on every request"],
355
- ["_voltro_kv", "the app's own key/value rows — app data by construction"],
356
- ["_voltro_approvals", "human decisions on workflow runs — they belong with the runs"],
357
- ["_voltro_connections", "third-party connections the app's users authorised. Portable as ROWS; note the secrets are encrypted with the project's key, so a target without that key holds ciphertext it cannot read"],
358
- ["_voltro_connection_grants", "who may use which connection — belongs with the connections"],
359
- ["_voltro_storage_blobs", "the stored objects' metadata — the assets travel with the bundle"],
360
- ["_voltro_storage_refs", "which rows reference which blob — belongs with the blobs"],
361
- ["_voltro_webhook_targets", "the endpoints subscribers registered through the app — app data"],
362
- ["_voltro_workflow_runs", "what the app's workflows did — history the app owns"],
363
- ["_voltro_workflow_run_steps", "the steps of those runs — belongs with the runs"],
364
- ["_voltro_workflow_run_events", "the events those runs emitted — belongs with the runs"],
365
- ["_voltro_workflow_events", "the app's event journal — app data (the target keeps its OWN watermarks, which are local)"],
366
- ["_voltro_workflow_start_contexts", "the context a start was made with — belongs with the runs"],
367
- ["_voltro_events", "the app's domain events — app data"],
368
- ["_voltro_ai_flows", "the flow definitions the app authored — app data"],
369
- ["_voltro_ai_flow_runs", "what those flows did — history the app owns, like a workflow run"],
370
- ["_voltro_notification_inbox", "what the app's users received — app data, and a restore without it loses their inbox"],
371
- ["_voltro_notification_preferences", "the per-user delivery settings the app's users chose — app data"],
372
- ["_voltro_notification_quiet_hours", "the per-user quiet hours the app's users chose — app data"],
373
- ["_voltro_notification_topic_subscriptions", "which topics the app's users subscribed to — app data"],
374
- ["_voltro_rebac_tuples", "the relationship tuples the app's authorisation decisions are made from — app data"],
375
- ["_voltro_scim_users", "identities an external directory provisioned into the app — app data"],
376
- ["_voltro_scim_groups", "groups an external directory provisioned into the app — app data"],
377
- ["_voltro_row_history", "the app's own row-version history — the point of keeping versions is that they survive a move"],
378
- ["_voltro_crdt_versions", "the version history of the app's collaborative documents (named snapshots, archived lineages) — it restores the document rows that travel with it"],
379
- ["_voltro_audit_log", "the app's audit trail — compliance data the app owns, not deployment bookkeeping"],
380
- ["_voltro_feature_flags", "the flags the app defined — app configuration"],
381
- ["_voltro_feature_flag_audit", "who changed which flag — belongs with the flags"],
382
- ["_voltro_comments", "the comments the app's users wrote — app data"],
383
- ["_voltro_comment_threads", "the threads those comments hang on — app data"],
384
- ["_voltro_comment_reactions", "the reactions the app's users left — app data"],
385
- ["_voltro_comment_reads", "which comments each user has read — app data, and losing it re-marks everything unread"],
386
- ["_voltro_comment_thread_events", "the status changes the app's users made on a thread — app data"]
387
- ]), V = (e) => Ge.has(e), qe = (e) => Ge.get(e), Je = (e, t) => {
388
- if (e.length === 0) return;
389
- let n = e.map((e) => ` ${e} — ${qe(e) ?? "describes this deployment"}`);
390
- return `${t} ${e.length} table(s) that describe a deployment rather than its data:\n${n.join("\n")}\n A row from another environment is not stale there, it is a false statement about this one.`;
391
- }, H = class extends s.TaggedError()("BundleError", {
392
- reason: s.String,
393
- path: s.optional(s.String)
394
- }) {
395
- get message() {
396
- return this.path === void 0 ? this.reason : `${this.reason} (${this.path})`;
397
- }
398
- }, Ye = class extends s.TaggedError()("CodecError", {
399
- table: s.String,
400
- reason: s.String
401
- }) {}, Xe = class extends s.TaggedError()("IntegrityError", {
402
- what: s.String,
403
- expected: s.String,
404
- actual: s.String
405
- }) {
406
- get message() {
407
- return `${this.what}: expected ${this.expected}, got ${this.actual}`;
408
- }
409
- }, Ze = class extends t.TaggedError("CompressionError") {}, Qe = class extends s.TaggedError()("ScopeError", { reason: s.String }) {
410
- get message() {
411
- return this.reason;
412
- }
413
- }, $e = class extends s.TaggedError()("RowsRefusedError", {
414
- rows: s.Array(s.Struct({
415
- table: s.String,
416
- id: s.String,
417
- reason: s.String,
418
- derived: s.Boolean
419
- })),
420
- primaryCount: s.Number,
421
- totalCount: s.Number,
422
- byTable: s.Array(s.Struct({
423
- table: s.String,
424
- refused: s.Number,
425
- primary: s.Number
426
- }))
427
- }) {}, et = class extends s.TaggedError()("SchemaDriftError", {
428
- bundleFingerprint: s.String,
429
- targetFingerprint: s.String,
430
- diff: s.Array(s.String)
431
- }) {
432
- get message() {
433
- return `bundle schema ${this.bundleFingerprint} does not match target ${this.targetFingerprint}: ${this.diff.join("; ")}`;
434
- }
435
- }, U = class extends t.TaggedError("NativeToolError") {
436
- get message() {
437
- let e = this.stderr?.trim(), t = this.hint?.trim();
438
- return `${this.tool}: ${this.reason}${e ? `\n${e}` : ""}${t ? `\n\nhint: ${t}` : ""}`;
439
- }
440
- }, tt = class extends s.TaggedError()("MaskingError", {
441
- unclassified: s.Array(s.String),
442
- invalidActions: s.optional(s.Array(s.String))
443
- }) {
444
- get message() {
445
- let e = [];
446
- return this.unclassified.length > 0 && e.push(`unclassified columns: ${this.unclassified.join(", ")}`), this.invalidActions !== void 0 && this.invalidActions.length > 0 && e.push(`invalidActions: ${this.invalidActions.join(", ")}`), `masking refused — ${e.join("; ") || "no column may be exported"}`;
447
- }
448
- }, nt = class extends s.TaggedError()("ImportModeError", {
449
- mode: s.String,
450
- reason: s.String
451
- }) {
452
- get message() {
453
- return `import mode '${this.mode}' refused: ${this.reason}`;
454
- }
455
- }, rt = class extends s.TaggedError()("CrossDialectError", {
456
- sourceDialect: s.String,
457
- targetDialect: s.String,
458
- blocking: s.Array(s.String)
459
- }) {}, it = (e, t) => e.tables.find((e) => e.name === t)?.columns.find((e) => e.type === "id")?.name ?? "id", at = (e, t, r) => n.gen(function* () {
460
- let i = r.maxDepth ?? 12, a = new Set(r.pinned ?? []), o = /* @__PURE__ */ new Map(), s = /* @__PURE__ */ new Map(), l = /* @__PURE__ */ new Map(), u = /* @__PURE__ */ new Map();
461
- for (let e of t.tables) u.has(e.name) || u.set(e.name, e);
462
- let d = /* @__PURE__ */ new Map();
463
- for (let [e, t] of u) d.set(e, {
464
- pk: t.columns.find((e) => e.type === "id")?.name ?? "id",
465
- fks: t.columns.filter((e) => e.references !== void 0).map((e) => ({
466
- col: e.name,
467
- refTable: e.references.table
468
- }))
469
- });
470
- let f = (e) => d.get(e)?.pk ?? "id", p = (e) => {
471
- let t = s.get(e) ?? /* @__PURE__ */ new Set();
472
- return s.set(e, t), t;
473
- }, m = (e, t) => {
474
- if (t == null || a.has(e) || s.get(e)?.has(t)) return;
475
- let n = l.get(e) ?? /* @__PURE__ */ new Set();
476
- n.add(t), l.set(e, n);
477
- }, h = (e, t, n) => {
478
- let r = p(e), i = a.has(e) ? void 0 : o.get(e) ?? (() => {
479
- let t = /* @__PURE__ */ new Set();
480
- return o.set(e, t), t;
481
- })();
482
- return (e) => {
483
- r.add(e[t]), i?.add(e[t]);
484
- for (let t of n) m(t.refTable, e[t.col]);
485
- };
486
- }, g = (t, r) => {
487
- let i = h(t, f(t), d.get(t)?.fks ?? []);
488
- return c.runForEach(C(e, {
489
- table: t,
490
- pk: f(t),
491
- ...r === void 0 ? {} : { where: r }
492
- }), (e) => n.sync(() => i(e)));
493
- }, _ = n.gen(function* () {
494
- for (let e = 0; e < i && l.size > 0; e++) {
495
- let e = [...l.entries()];
496
- l.clear();
497
- for (let [t, n] of e) {
498
- let e = [...n].filter((e) => !s.get(t)?.has(e));
499
- if (e.length !== 0) for (let n = 0; n < e.length; n += 500) yield* g(t, y(f(t), e.slice(n, n + 500)));
500
- }
501
- }
502
- });
503
- for (let [e, t] of Object.entries(r.seeds)) yield* g(e, t);
504
- if (yield* _, r.children) {
505
- let e = new Set(r.children === !0 ? [...s.keys()] : r.children.roots.filter((e) => s.has(e))), a = /* @__PURE__ */ new Map();
506
- for (let r = 0; r < i; r++) {
507
- let r = !1, i = [];
508
- for (let o of t.tables) {
509
- let t = o.name, c = o.columns.map((e) => ({
510
- col: e.name,
511
- refTable: e.references?.table
512
- })).filter((t) => !!t.refTable && e.has(t.refTable) && (s.get(t.refTable)?.size ?? 0) > 0);
513
- if (c.length === 0) continue;
514
- let l = s.get(t)?.size ?? 0, u = a.get(t);
515
- u === void 0 && (u = /* @__PURE__ */ new Map(), a.set(t, u));
516
- let d = [], f = 0, p = () => {
517
- let e = d;
518
- return d = [], f = 0, e.length === 0 ? n.void : g(t, e.length === 1 ? e[0] : x(...e));
519
- };
520
- for (let e of c) {
521
- let t = u.get(e.col);
522
- t === void 0 && (t = /* @__PURE__ */ new Set(), u.set(e.col, t));
523
- let n = [...s.get(e.refTable)].filter((e) => !t.has(e));
524
- for (let t = 0; t < n.length; t += 500) {
525
- let r = n.slice(t, t + 500);
526
- f + r.length > 500 && (yield* p()), d.push(y(e.col, r)), f += r.length;
527
- }
528
- for (let e of n) t.add(e);
529
- }
530
- yield* p(), (s.get(t)?.size ?? 0) > l && !e.has(t) && i.push(t), (s.get(t)?.size ?? 0) > l && (r = !0);
531
- }
532
- for (let t of i) e.add(t);
533
- if (yield* _, !r) break;
534
- }
535
- }
536
- return o;
537
- }), ot = (e, t) => ({
538
- tables: [...e.keys()],
539
- predicateFor: (n) => {
540
- let r = e.get(n);
541
- return r ? y(it(t, n), [...r]) : void 0;
542
- }
543
- }), st = "tenantId", ct = (e, t, r) => n.gen(function* () {
544
- let i = t.tables.map((e) => e.name), a = new Map(t.tables.map((e) => [e.name, e]));
545
- if (e.kind === "all") {
546
- let t = new Set(e.exclude ?? []);
547
- return {
548
- tables: i.filter((e) => !V(e) && !t.has(e)),
549
- predicateFor: () => void 0
550
- };
551
- }
552
- if (e.kind === "tables") {
553
- let t = e.tables ?? [], r = new Set(i), a = t.filter((e) => !r.has(e));
554
- if (a.length > 0) return yield* n.fail(new Qe({ reason: `scope { kind: 'tables' } names ${a.length} table(s) that are not in the schema: ${a.join(", ")}. Refusing to write a bundle that silently omits them. Known tables: ${i.length}.` }));
555
- let o = new Set(t);
556
- return {
557
- tables: i.filter((e) => o.has(e)),
558
- predicateFor: () => void 0
559
- };
560
- }
561
- let o = e.tenantId;
562
- if (o === void 0 || o === "") return yield* n.fail(new Qe({ reason: "scope { kind: 'tenant' } requires a tenantId — pass --tenant <id> (CLI) or scope.tenantId (API). Refusing to fall back to an unfiltered export." }));
563
- let s = r.tenantTables ? r.tenantTables.filter((e) => a.has(e)) : i.filter((e) => a.get(e)?.columns.some((e) => e.name === st) ?? !1);
564
- if (s.length === 0) return yield* n.fail(new Qe({ reason: r.tenantTables ? "tenant scope: none of the provided tenantTables exist in the schema — nothing to filter." : `tenant scope: no table carries a '${st}' column — nothing to filter. A tenant export of a schema without tenant-scoped tables would be the full database; refusing.` }));
565
- let c = yield* at(r.store, t, {
566
- seeds: Object.fromEntries(s.map((e) => [e, g(st, o)])),
567
- pinned: s,
568
- children: { roots: s }
569
- }), l = [...c.entries()].filter(([, e]) => e.size > 0).map(([e]) => e), u = new Set(s);
570
- return {
571
- tables: [...s, ...l],
572
- predicateFor: (e) => u.has(e) ? g(st, o) : c.has(e) ? y(it(t, e), [...c.get(e)]) : void 0
573
- };
574
- }), lt = (e, t) => ({
575
- list: t,
576
- open: (t) => A(e, t)
577
- }), ut = (e) => ({
578
- write: (t, n, r) => he(e, t, n, r),
579
- has: (t) => e.head(t).pipe(n.map((e) => e !== null))
580
- }), dt = (e, t) => n.tryPromise({
581
- try: async () => {
582
- let { createWriteStream: n } = await import("node:fs"), r = await import("node:fs/promises"), { createHash: i, randomUUID: a } = await import("node:crypto"), { pipeline: o } = await import("node:stream/promises"), { Transform: s } = await import("node:stream"), c = await import("node:path"), l = c.join(t, `.tmp-${a()}`), u = i("sha256"), d = 0, f = new s({ transform(e, t, n) {
583
- u.update(e), d += e.length, n(null, e);
584
- } });
585
- await o(await j(e.stream), f, n(l));
586
- let p = u.digest("hex"), m = c.join(t, p);
587
- try {
588
- await r.access(m), await r.rm(l, { force: !0 });
589
- } catch {
590
- await r.rename(l, m);
591
- }
592
- return {
593
- sha256: p,
594
- size: d,
595
- contentType: e.contentType
596
- };
597
- },
598
- catch: (e) => new H({
599
- reason: `asset write failed: ${String(e?.message ?? e)}`,
600
- path: t
601
- })
602
- }), ft = (e, t, r) => n.gen(function* () {
603
- let i = yield* n.promise(() => import("node:path")), { createReadStream: a } = yield* n.promise(() => import("node:fs")), { createHash: o } = yield* n.promise(() => import("node:crypto")), s = i.join(t, r.sha256), l = o("sha256"), u = me("bundle", () => a(s)).pipe(c.tap((e) => n.sync(() => l.update(e))));
604
- yield* e.write(r.key, u, {
605
- contentType: r.contentType,
606
- totalSize: r.size
607
- });
608
- let d = l.digest("hex");
609
- if (d !== r.sha256) return yield* n.fail(new Xe({
610
- what: `asset ${r.key}`,
611
- expected: r.sha256,
612
- actual: d
613
- }));
614
- }), pt = {
615
- done: () => !1,
616
- mark: () => n.void
617
- }, mt = (e) => typeof e == "object" && !!e && e.status === 404, ht = (e) => n.gen(function* () {
618
- let t = e.progress ?? pt, r = 0, i = 0, a = 0, o = 0, s = /* @__PURE__ */ new Set(), l = /* @__PURE__ */ new Set(), u = [];
619
- return yield* c.runForEach(e.source.list(), (c) => (a++, t.done(c.key) || l.has(c.key) ? n.void : (l.add(c.key), e.source.open(c.key).pipe(n.map((e) => e), n.catchIf(mt, () => n.sync(() => {
620
- u.push(c.key);
621
- })), n.flatMap((a) => a === void 0 ? n.void : dt(a, e.assetsDir).pipe(n.flatMap((e) => {
622
- let n = {
623
- key: c.key,
624
- sha256: e.sha256,
625
- size: e.size,
626
- contentType: e.contentType
627
- };
628
- return r++, i += e.size, s.has(e.sha256) || (s.add(e.sha256), o += e.size), t.mark(c.key, n);
629
- }))))))).pipe(n.mapError((e) => e instanceof H ? e : new H({ reason: `asset export failed: ${String(e)}` }))), {
630
- count: r,
631
- totalBytes: i,
632
- references: a,
633
- objects: s.size,
634
- objectBytes: o,
635
- missing: u
636
- };
637
- }), gt = (e) => me("bundle", () => ue(e)).pipe(c.decodeText(), c.splitLines, c.filter((e) => e.trim().length > 0), c.map((e) => JSON.parse(e)), c.mapError((t) => t instanceof H ? t : new H({
638
- reason: `asset index unreadable: ${String(t)}`,
639
- path: e
640
- }))), _t = (e) => n.gen(function* () {
641
- let t = e.progress ?? pt, r = 0, i = /* @__PURE__ */ new Set(), a = /* @__PURE__ */ new Set();
642
- return yield* c.runForEach(gt(e.indexPath), (o) => t.done(o.key) || a.has(o.key) ? n.void : (a.add(o.key), ft(e.sink, e.assetsDir, o)).pipe(n.andThen(n.sync(() => {
643
- r++, i.add(o.sha256);
644
- })), n.andThen(t.mark(o.key, o)))), {
645
- count: r,
646
- objects: i.size
647
- };
648
- }), vt = () => typeof M.createZstdCompress == "function", yt = (e) => e === "none" ? "none" : e === "gzip" ? "gzip" : vt() ? "zstd" : "gzip", bt = (e) => e === "zstd" ? ".zst" : e === "gzip" ? ".gz" : "", xt = (e) => e === "zstd" ? M.createZstdCompress() : e === "gzip" ? M.createGzip() : new ge(), St = (e) => e === "zstd" ? M.createZstdDecompress() : e === "gzip" ? M.createGunzip() : new ge(), Ct = 1, wt = s.Struct({
649
- name: s.String,
650
- file: s.String,
651
- rowCount: s.Number,
652
- checksum: s.String,
653
- columnTypes: s.Record(s.String, s.String),
654
- primaryKey: s.String
655
- }), Tt = s.Struct({
656
- kind: s.Literals([
657
- "all",
658
- "tenant",
659
- "tables"
660
- ]),
661
- tenantId: s.optional(s.String),
662
- tables: s.optional(s.Array(s.String)),
663
- exclude: s.optional(s.Array(s.String))
664
- }), Et = s.Struct({
665
- count: s.Number,
666
- totalBytes: s.Number,
667
- index: s.optional(s.String)
668
- });
669
- s.Struct({
670
- key: s.String,
671
- sha256: s.String,
672
- size: s.Number,
673
- contentType: s.String
674
- });
675
- var Dt = s.Struct({
676
- formatVersion: s.Number,
677
- createdAt: s.String,
678
- source: s.Struct({
679
- dialect: s.String,
680
- schemaFingerprint: s.String
681
- }),
682
- scope: Tt,
683
- consistency: s.Literals(["live", "snapshot"]),
684
- compression: s.Literals([
685
- "zstd",
686
- "gzip",
687
- "none"
688
- ]),
689
- tables: s.Array(wt),
690
- assets: Et,
691
- masking: s.optional(s.Struct({
692
- policyId: s.optional(s.String),
693
- transformed: s.Array(s.String)
694
- }))
695
- }), Ot = s.encodeSync(s.fromJsonString(Dt)), kt = s.decodeUnknownSync(s.fromJsonString(Dt)), At = "manifest.json", jt = "README.md", Mt = "data", Nt = "assets", Pt = "assets/index.ndjson", Ft = ".ledger.json", It = (e) => e.kind === "tenant" ? `tenant \`${e.tenantId}\`` : e.kind === "tables" ? `tables \`${(e.tables ?? []).join(", ")}\`` : "all tables", Lt = (e, t) => {
696
- let n = e.tables.reduce((e, t) => e + t.rowCount, 0), r = e.masking ? `✅ **Masked at the source** — ${e.masking.transformed.length} column(s) transformed${e.masking.policyId ? ` (policy \`${e.masking.policyId}\`)` : ""}. Personal data was replaced with realistic, referentially-consistent fakes before any byte was written.` : "⚠️ **NOT masked — this bundle contains REAL data (PII included).** Treat it as production data: do not copy it into a lower environment or share it without review. To produce a safe copy, re-export under a masking profile (`--profile`).", i = e.masking && e.masking.transformed.length > 0 ? `\n\nMasked columns: ${e.masking.transformed.map((e) => `\`${e}\``).join(", ")}.` : "", a = e.assets.count > 0 ? `${e.assets.count} object(s), ${e.assets.totalBytes} bytes` : "none", o = e.tables.map((e) => `| \`${e.name}\` | ${e.rowCount} |`).join("\n");
697
- return `# Voltro data export
698
-
699
- The machine-readable manifest is \`manifest.json\`; this file is a human summary.
700
-
701
- | | |
702
- |---|---|
703
- | **Generated** | ${e.createdAt} |
704
- | **Exported from** | \`${t.hostname}\` |
705
- | **Source dialect** | \`${e.source.dialect}\` |
706
- | **Format version** | ${e.formatVersion} |
707
- | **Consistency** | ${e.consistency} |
708
- | **Compression** | ${e.compression} |
709
- | **Scope** | ${It(e.scope)} |
710
- | **Tables** | ${e.tables.length} |
711
- | **Rows (total)** | ${n} |
712
- | **Assets** | ${a} |
713
-
714
- ## Data sensitivity
715
-
716
- ${r}${i}
717
-
718
- ## Tables
719
-
720
- | Table | Rows |
721
- |---|---|
722
- ${o}
723
-
724
- ## Restore
725
-
726
- \`\`\`bash
727
- # into a stopped/replica DB directly:
728
- voltro data import <this-directory>
729
-
730
- # or into a running instance (needs the data-transfer secret):
731
- voltro data import <this-directory> --target api --api-url <url> --token <secret>
732
- \`\`\`
733
- `;
734
- }, Rt = i.counter("voltro_data_transfer_rows", { description: "Rows exported/imported by @voltro/data-transfer, tagged by phase + table." }), zt = Array.from({ length: 16 }, (e, t) => .01 * 2 ** t), Bt = i.histogram("voltro_data_transfer_table_seconds", {
735
- boundaries: zt,
736
- description: "Per-table export/import duration in seconds, tagged by phase."
737
- }), W = (e, t) => {
738
- if (e) try {
739
- e(t);
740
- } catch {}
741
- }, Vt = (e, t, r, a, o, s) => n.Do.pipe(n.bind("start", () => n.clockWith((e) => e.currentTimeMillis)), n.bind("result", () => s), n.tap(({ start: r, result: a }) => n.clockWith((e) => e.currentTimeMillis).pipe(n.flatMap((s) => n.andThen(Bt.pipe(i.withAttributes({ phase: e }), i.update((s - r) / 1e3)), Rt.pipe(i.withAttributes({ phase: e }), i.withAttributes({ table: t }), i.update(o(a))))))), n.map(({ result: e }) => e), n.withSpan(`data-transfer.${e}.table`, { attributes: {
742
- table: t,
743
- index: r,
744
- tableCount: a,
745
- phase: e
746
- } })), Ht = () => ({
747
- tables: {},
748
- assets: {}
749
- }), Ut = (e) => n.tryPromise(async () => {
750
- let t = await (await import("node:fs/promises")).readFile(e, "utf8");
751
- return JSON.parse(t);
752
- }).pipe(n.orElseSucceed(Ht)), G = (e, t) => n.tryPromise({
753
- try: async () => {
754
- let n = await import("node:fs/promises"), { randomUUID: r } = await import("node:crypto"), i = `${e}.tmp-${r()}`;
755
- await n.writeFile(i, JSON.stringify(t)), await n.rename(i, e);
756
- },
757
- catch: (t) => new H({
758
- reason: `ledger write failed: ${String(t?.message ?? t)}`,
759
- path: e
760
- })
761
- }), Wt = (e, t) => t in e.tables, Gt = (e, t) => t in e.assets, Kt = /* @__PURE__ */ new Set([
762
- "json",
763
- "array",
764
- "vector"
765
- ]), qt = (e) => typeof e == "object" && !!e && !Array.isArray(e) && typeof e.$v == "string", Jt = (e) => Buffer.from(e).toString("base64"), Yt = (e) => new Uint8Array(Buffer.from(e, "base64")), Xt = (e) => e instanceof Date ? e.toISOString() : typeof e == "number" ? new Date(e).toISOString() : String(e), Zt = (e, t) => e == null ? null : t !== void 0 && Kt.has(t) ? e : t === "bytes" ? Jt(e) : t === "timestamp" || t === "date" ? Xt(e) : typeof e == "bigint" ? {
766
- $v: "bigint",
767
- d: e.toString()
768
- } : typeof e == "number" && !Number.isFinite(e) ? {
769
- $v: "number",
770
- d: String(e)
771
- } : e instanceof Date ? {
772
- $v: "date",
773
- d: e.toISOString()
774
- } : e instanceof Uint8Array ? {
775
- $v: "bytes",
776
- d: Jt(e)
777
- } : e, Qt = (e) => {
778
- switch (e.$v) {
779
- case "bigint": return BigInt(e.d);
780
- case "number": return Number(e.d);
781
- case "date": return new Date(e.d);
782
- case "bytes": return Yt(e.d);
783
- default: return e;
784
- }
785
- }, $t = (e, t) => e == null ? null : t !== void 0 && Kt.has(t) ? e : qt(e) ? Qt(e) : t === "bytes" ? Yt(e) : t === "timestamp" || t === "date" ? new Date(e) : e, en = (e, t) => {
786
- let n = {};
787
- for (let [r, i] of Object.entries(e)) n[r] = Zt(i, t[r]);
788
- return n;
789
- }, tn = (e, t) => {
790
- let n = {};
791
- for (let [r, i] of Object.entries(e)) n[r] = $t(i, t[r]);
792
- return n;
793
- }, nn = new TextEncoder(), rn = (e, t) => e.pipe(c.map((e) => nn.encode(`${JSON.stringify(en(e, t))}\n`))), an = (e, t, r) => e.pipe(c.decodeText(), c.splitLines, c.filter((e) => e.trim().length > 0), c.mapEffect((e) => n.try({
794
- try: () => tn(JSON.parse(e), r),
795
- catch: (e) => new Ye({
796
- table: t,
797
- reason: String(e?.message ?? e)
798
- })
799
- }))), on = (e, t) => {
800
- let n = new Set(e), r = /* @__PURE__ */ new Map();
801
- for (let e of t.tables) {
802
- if (!n.has(e.name)) continue;
803
- let t = /* @__PURE__ */ new Set();
804
- for (let r of e.columns) {
805
- let i = r.references?.table;
806
- i && i !== e.name && n.has(i) && t.add(i);
807
- }
808
- r.set(e.name, t);
809
- }
810
- let i = [], a = /* @__PURE__ */ new Set(), o = /* @__PURE__ */ new Set(), s = (e) => {
811
- if (!(a.has(e) || o.has(e))) {
812
- o.add(e);
813
- for (let t of r.get(e) ?? []) s(t);
814
- o.delete(e), a.add(e), i.push(e);
815
- }
816
- };
817
- for (let t of e) n.has(t) && s(t);
818
- return i;
819
- }, K = (e) => {
820
- let t = e, n = [];
821
- typeof t?._tag == "string" && t._tag !== "" && n.push(t._tag);
822
- let r = typeof t?.message == "string" ? t.message.trim() : "";
823
- if (r === "" ? t?.cause !== void 0 && t.cause !== null && n.push(K(t.cause)) : n.push(r), n.length === 0) {
824
- let t = String(e ?? "").trim();
825
- return t === "" || t === "[object Object]" ? sn(e) : t;
826
- }
827
- return n.join(": ");
828
- }, sn = (e) => {
829
- try {
830
- let t = JSON.stringify(e);
831
- return t === void 0 || t === "{}" ? "unknown error (no message, cause or tag)" : t;
832
- } catch {
833
- return "unknown error (not serialisable)";
834
- }
835
- }, cn = (e, t) => {
836
- let n = e.tables.find((e) => e.name === t), r = {};
837
- for (let e of n?.columns ?? []) e.generatedAs === void 0 && (r[e.name] = e.type);
838
- return r;
839
- }, ln = (e, t) => (e.tables.find((e) => e.name === t)?.columns ?? []).filter((e) => e.generatedAs !== void 0).map((e) => e.name), un = (e, t) => {
840
- if (t.length === 0) return e;
841
- let n = { ...e };
842
- for (let e of t) delete n[e];
843
- return n;
844
- }, dn = (e, t) => {
845
- let n = e.tables.find((e) => e.name === t);
846
- if (n === void 0) return { refusal: `${t}: not in the schema snapshot, so its primary key is unknown.` };
847
- let r = n.columns.find((e) => e.type === "id");
848
- if (r !== void 0) return { column: r.name };
849
- let i = n.indexes.find((e) => e.name === `${t}_pkey`)?.columns ?? n.primaryKey ?? [];
850
- return i.length === 1 ? { column: i[0] } : i.length > 1 ? { refusal: `${t}: composite primary key (${i.join(", ")}). Keyset export orders by ONE column; ordering by the first alone would split equal values across pages and drop or duplicate rows. Exclude this table from the scope, or give it a single-column key.` } : { refusal: `${t}: no primary key, so there is no stable order to paginate by. A keyset export would return rows in an order the database may change between pages. Exclude this table from the scope.` };
851
- }, fn = (e, t, r) => n.tryPromise({
852
- try: async () => {
853
- let { createWriteStream: n } = await import("node:fs"), i = await import("node:fs/promises"), { createHash: a, randomUUID: o } = await import("node:crypto"), { pipeline: s } = await import("node:stream/promises"), { Readable: l, Transform: u } = await import("node:stream"), d = a("sha256"), f = 0, p = new u({ transform(e, t, n) {
854
- d.update(e);
855
- let r = e;
856
- for (let e = 0; e < r.length; e++) r[e] === 10 && f++;
857
- n(null, e);
858
- } }), m = l.fromWeb(c.toReadableStream(e)), h = `${t}.tmp-${o()}`;
859
- try {
860
- await s(m, p, xt(r), n(h)), await i.rename(h, t);
861
- } catch (e) {
862
- throw await i.rm(h, { force: !0 }).catch(() => {}), e;
863
- }
864
- return {
865
- rowCount: f,
866
- checksum: d.digest("hex")
867
- };
868
- },
869
- catch: (e) => new H({
870
- reason: `write table failed: ${K(e)}`,
871
- path: t
872
- })
873
- }), pn = (e, t) => n.tryPromise({
874
- try: async () => {
875
- await (await import("node:fs/promises")).appendFile(e, `${t}\n`);
876
- },
877
- catch: (t) => new H({
878
- reason: `append failed: ${K(t)}`,
879
- path: e
880
- })
881
- }), mn = (e, t) => n.gen(function* () {
882
- let r = [], i = t.ordered.length;
883
- for (let a = 0; a < t.ordered.length; a++) {
884
- let o = t.ordered[a], s = cn(t.snapshot, o), l = dn(t.snapshot, o);
885
- if ("refusal" in l) return yield* n.fail(new Qe({ reason: `export: ${l.refusal}` }));
886
- let u = l.column, d = `${Mt}/${o}.ndjson${bt(t.compression)}`;
887
- if (Wt(t.ledger, o)) {
888
- let e = t.ledger.tables[o];
889
- W(t.onProgress, {
890
- phase: "export",
891
- table: o,
892
- index: a,
893
- tableCount: i,
894
- event: "done",
895
- rowsDone: e.rowCount,
896
- total: e.rowCount
897
- }), r.push({
898
- name: o,
899
- file: d,
900
- rowCount: e.rowCount,
901
- checksum: e.checksum,
902
- columnTypes: s,
903
- primaryKey: u
904
- });
905
- continue;
906
- }
907
- W(t.onProgress, {
908
- phase: "export",
909
- table: o,
910
- index: a,
911
- tableCount: i,
912
- event: "start",
913
- rowsDone: 0
914
- });
915
- let f = t.resolved.predicateFor(o), p = C(e, {
916
- table: o,
917
- pk: u,
918
- chunkSize: t.chunkSize,
919
- retry: t.retry,
920
- ...f === void 0 ? {} : { where: f }
921
- }), m = ln(t.snapshot, o), h = m.length === 0 ? p : p.pipe(c.map((e) => un(e, m))), g = t.mask ? h.pipe(c.map((e) => We(e, o, t.mask.actions, t.mask.seed, s))) : h, { rowCount: _, checksum: v } = yield* Vt("export", o, a, i, (e) => e.rowCount, fn(rn(g, s), t.bundleFile(d), t.compression));
922
- t.ledger.tables[o] = {
923
- rowCount: _,
924
- checksum: v
925
- }, yield* G(t.ledgerPath, t.ledger), W(t.onProgress, {
926
- phase: "export",
927
- table: o,
928
- index: a,
929
- tableCount: i,
930
- event: "done",
931
- rowsDone: _,
932
- total: _
933
- }), r.push({
934
- name: o,
935
- file: d,
936
- rowCount: _,
937
- checksum: v,
938
- columnTypes: s,
939
- primaryKey: u
940
- });
941
- }
942
- return r;
943
- }), hn = (e) => ({
944
- _tag: "RawSqlFragment",
945
- strings: [e],
946
- values: []
947
- }), gn = (t, i, a) => n.tryPromise({
948
- try: () => t.transactional(async (t) => {
949
- if (t.raw) for (let e of Te(i)) await t.raw(hn(e));
950
- let o = await n.runPromiseExit(a(t));
951
- if (r.isFailure(o)) throw Error(e.pretty(o.cause));
952
- return o.value;
953
- }),
954
- catch: (e) => e instanceof H ? e : new H({ reason: `snapshot export failed: ${K(e)}` })
955
- }), _n = (e) => n.gen(function* () {
956
- let t = e.scope ?? { kind: "all" }, r = yt(e.compression), i = e.chunkSize ?? 1e3, a = e.retryTimes ?? 5, s = o.max([o.exponential("50 millis").pipe(o.jittered), o.recurs(a)]), c = yield* n.promise(() => import("node:path")), l = (t) => c.join(e.outDir, t);
957
- yield* n.tryPromise({
958
- try: async () => {
959
- let t = await import("node:fs/promises");
960
- await t.mkdir(l(Mt), { recursive: !0 }), e.assets && await t.mkdir(l(Nt), { recursive: !0 });
961
- },
962
- catch: (t) => new H({
963
- reason: `mkdir failed: ${K(t)}`,
964
- path: e.outDir
965
- })
966
- });
967
- let u = l(Ft), d = yield* Ut(u), f = e.subset ? ot(yield* at(e.store, e.snapshot, e.subset), e.snapshot) : yield* ct(t, e.snapshot, {
968
- store: e.store,
969
- ...e.tenantTables ? { tenantTables: e.tenantTables } : {}
970
- }), p = on(f.tables, e.snapshot), m = e.consistency ?? "live", h, g;
971
- if (e.masking) {
972
- let t = e.classification ?? De(e.snapshot), r = Ve(p.map((t) => ({
973
- name: t,
974
- columns: (e.snapshot.tables.find((e) => e.name === t)?.columns ?? []).map((e) => ({
975
- name: e.name,
976
- type: e.type
977
- }))
978
- })), t, e.masking);
979
- if (r.unclassified.length > 0 || r.invalid.length > 0) return yield* n.fail(new tt({
980
- unclassified: r.unclassified,
981
- ...r.invalid.length > 0 ? { invalidActions: r.invalid.map((e) => `${e.column}: ${JSON.stringify(e.action)}`) } : {}
982
- }));
983
- g = {
984
- actions: r.actions,
985
- seed: e.masking.seed
986
- };
987
- let i = Object.entries(r.actions).filter(([, e]) => e !== "keep").map(([e]) => e);
988
- h = {
989
- ...e.masking.id ? { policyId: e.masking.id } : {},
990
- transformed: i
991
- };
992
- }
993
- let _ = {
994
- snapshot: e.snapshot,
995
- ordered: p,
996
- resolved: f,
997
- compression: r,
998
- chunkSize: i,
999
- retry: s,
1000
- ledger: d,
1001
- ledgerPath: u,
1002
- bundleFile: l,
1003
- ...g ? { mask: g } : {},
1004
- ...e.onProgress ? { onProgress: e.onProgress } : {}
1005
- }, v = m === "snapshot" ? yield* gn(e.store, e.dialect, (e) => mn(e, _)) : yield* mn(e.store, _), y = 0, b = 0;
1006
- if (e.assets) {
1007
- let t = l(Pt), r = yield* ht({
1008
- source: e.assets,
1009
- assetsDir: l(Nt),
1010
- progress: {
1011
- done: (e) => e in d.assets,
1012
- mark: (e, r) => (d.assets[e] = !0, pn(t, JSON.stringify(r)).pipe(n.andThen(G(u, d))))
1013
- }
1014
- });
1015
- y = r.count, b = r.totalBytes, r.missing.length > 0 && (yield* n.logWarning(`${r.missing.length} of ${r.missing.length + r.count} blob reference(s) point at objects the storage provider does not have; they are NOT in this bundle and no import can bring them back. e.g. ${r.missing.slice(0, 3).map((e) => `'${e}'`).join(", ")}` + (r.missing.length > 3 ? `, and ${r.missing.length - 3} more` : "")));
1016
- }
1017
- let x = {
1018
- formatVersion: 1,
1019
- createdAt: e.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
1020
- source: {
1021
- dialect: e.dialect,
1022
- schemaFingerprint: ie(e.snapshot)
1023
- },
1024
- scope: t,
1025
- consistency: m,
1026
- compression: r,
1027
- tables: v,
1028
- assets: y > 0 ? {
1029
- count: y,
1030
- totalBytes: b,
1031
- index: Pt
1032
- } : {
1033
- count: 0,
1034
- totalBytes: 0
1035
- },
1036
- ...h ? { masking: h } : {}
1037
- };
1038
- return yield* n.tryPromise({
1039
- try: async () => {
1040
- await (await import("node:fs/promises")).writeFile(l(At), Ot(x));
1041
- },
1042
- catch: (e) => new H({
1043
- reason: `manifest write failed: ${K(e)}`,
1044
- path: At
1045
- })
1046
- }), yield* n.tryPromise({
1047
- try: async () => {
1048
- let e = await import("node:fs/promises"), t = await import("node:os");
1049
- await e.writeFile(l(jt), Lt(x, { hostname: t.hostname() }));
1050
- },
1051
- catch: (e) => new H({
1052
- reason: `readme write failed: ${K(e)}`,
1053
- path: jt
1054
- })
1055
- }), x;
1056
- }), vn = (e, t) => {
1057
- let n = new Set(t), r = [];
1058
- for (let t of e.tables) if (!n.has(t.name)) for (let e of t.columns) {
1059
- let i = e.references?.table;
1060
- i !== void 0 && n.has(i) && r.push({
1061
- from: t.name,
1062
- column: e.name,
1063
- to: i
1064
- });
1065
- }
1066
- return r;
1067
- }, yn = (e) => {
1068
- let t = /* @__PURE__ */ new Map();
1069
- for (let n of e) t.set(n.from, [...t.get(n.from) ?? [], n]);
1070
- let n = [...t.entries()].map(([e, t]) => {
1071
- let n = t[0].rowCount, r = t.map((e) => `${e.column} → ${e.to}`).join(", ");
1072
- return ` ${e} (${n} row${n === 1 ? "" : "s"}): ${r}`;
1073
- });
1074
- return `replace cannot empty this target: ${t.size} table(s) OUTSIDE the bundle hold rows that reference tables INSIDE it. Emptying the bundle's tables would leave those rows pointing at nothing, and the bundle cannot restore them.\n${n.join("\n")}\nNothing has been deleted. Either re-export with a scope that includes those tables, or use --mode upsert if those rows are meant to survive.`;
1075
- }, bn = (e) => {
1076
- switch (e) {
1077
- case "id":
1078
- case "text":
1079
- case "enum": return e === "enum" ? "enum" : "string";
1080
- case "integer": return "integer";
1081
- case "real": return "float";
1082
- case "boolean": return "boolean";
1083
- case "timestamp": return "datetime";
1084
- case "date": return "date";
1085
- case "json": return "json";
1086
- case "bytes": return "binary";
1087
- case "reference": return "reference";
1088
- case "vector": return "vector";
1089
- case "array": return "array";
1090
- case "interval": return "interval";
1091
- case "raw": return "raw";
1092
- default: return "string";
1093
- }
1094
- }, xn = {
1095
- vector: /* @__PURE__ */ new Set(["postgres"]),
1096
- array: /* @__PURE__ */ new Set(["postgres"]),
1097
- interval: /* @__PURE__ */ new Set(["postgres"])
1098
- }, Sn = (e, t, n, r) => {
1099
- let i = bn(n);
1100
- if (i === "raw") return {
1101
- table: e,
1102
- column: t,
1103
- type: n,
1104
- severity: "block",
1105
- reason: `raw() is verbatim source-dialect SQL — it cannot be assumed valid on ${r}`
1106
- };
1107
- let a = xn[i];
1108
- return !a || a.has(r) ? null : i === "array" || i === "interval" ? {
1109
- table: e,
1110
- column: t,
1111
- type: n,
1112
- severity: "warn",
1113
- reason: `${i} is native to postgres; ${r} will represent it as json/text — verify downstream reads`
1114
- } : {
1115
- table: e,
1116
- column: t,
1117
- type: n,
1118
- severity: "block",
1119
- reason: `${i} columns require a ${[...a].join("/")} target; ${r} has no compatible column type`
1120
- };
1121
- }, Cn = (e, t, n) => {
1122
- if (t === n) return [];
1123
- let r = [];
1124
- for (let t of e) for (let [e, i] of Object.entries(t.columnTypes)) {
1125
- let a = Sn(t.name, e, i, n);
1126
- a && r.push(a);
1127
- }
1128
- return r;
1129
- }, wn = (e) => `[${e.severity}] ${e.table}.${e.column} (${e.type}): ${e.reason}`, Tn = (e) => {
1130
- let t = e.constraint ?? e.column, n = t === void 0 ? "" : ` ${t}`;
1131
- switch (e.kind) {
1132
- case "foreignKey": return `foreign key${n}: the referenced row does not exist (import it first, or check the bundle's table order)`;
1133
- case "foreignKeyInUse": return `foreign key${n}: the row is still referenced by other rows`;
1134
- case "unique": return `unique constraint${n}: a row with this value already exists`;
1135
- case "notNull": return `not-null constraint${n}: the column requires a value`;
1136
- case "check": return `check constraint${n}: the row does not satisfy it`;
1137
- }
1138
- }, En = (e) => {
1139
- let t = [e.dbCode, e.dbErrno ?? e.dbNumber].filter((e) => e !== void 0).map(String);
1140
- return t.length > 0 ? t.join("/") : void 0;
1141
- }, Dn = "Failed to execute statement", On = (e) => {
1142
- let t = (e.split("\n", 1)[0] ?? "").trim();
1143
- return t === `SqlError: ${Dn}` ? Dn : t;
1144
- }, kn = (e) => {
1145
- let t = String(e?.stack ?? e), n = t.length <= 1200 ? t : `${t.slice(0, 1200)}…`, r = (e, t) => ({
1146
- reason: e.length <= 300 ? e : `${e.slice(0, 300)}…`,
1147
- unclassified: t,
1148
- raw: n
1149
- }), i = _(e), a = p(i), o = En(i), s = o === void 0 ? "" : ` [${o}]`;
1150
- if (a !== void 0) return r(`${Tn(a)}${s}`, !1);
1151
- let c = typeof i.dbMessage == "string" ? i.dbMessage : void 0, l = e?.message, u = On(typeof l == "string" && l.length > 0 ? l : String(e)), d = c !== void 0 && c !== u && !c.startsWith("SqlError:") ? c : u, f = typeof i.dbCauseChain == "string" ? i.dbCauseChain : void 0, m = f !== void 0 && /[←‖]/.test(f) ? ` (via ${f})` : "";
1152
- return r(d === Dn || d.length === 0 ? `the database refused the write and the driver gave no detail${s}${m}` : `${d}${s}`, d === Dn || d.length === 0);
1153
- }, An = (e) => kn(e).reason, jn = (e) => {
1154
- let t = _(e), n = p(t);
1155
- if (n?.kind === "foreignKeyInUse") {
1156
- let e = n.constraint ?? n.column, r = e === void 0 ? "" : ` ${e}`, i = En(t);
1157
- return `foreign key${r}: rows elsewhere still reference this table${i === void 0 ? "" : ` [${i}]`}`;
1158
- }
1159
- return An(e);
1160
- }, Mn = d, q = (e) => `${Mn}${e}`, Nn = (e) => typeof e.run == "function", Pn = (e) => `staging is not available for this run — loading directly into the target instead: ${e}\n The load therefore holds the destructive transaction open for its whole duration, so a
1161
- process that dies mid-load depends on the database rolling it back. The rollback capture
1162
- written before the first delete is what covers that case.`, Fn = /^[a-zA-Z_][a-zA-Z0-9_]*$/, J = (e, t) => {
1163
- if (!Fn.test(e)) throw Error(`refusing to build SQL for the table name ${JSON.stringify(e)}: it is outside the identifier class the framework allows. A name from a bundle manifest reaches this concatenation, so it is checked here rather than assumed to still be what the declaration validated.`);
1164
- return t === "mysql" || t === "mariadb" ? `\`${e}\`` : t === "mssql" ? `[${e}]` : `"${e}"`;
1165
- }, In = (e, t) => {
1166
- let n = J(e, t), r = J(q(e), t);
1167
- switch (t) {
1168
- case "postgres": return `CREATE TABLE ${r} (LIKE ${n})`;
1169
- case "mysql":
1170
- case "mariadb": return `CREATE TABLE ${r} LIKE ${n}`;
1171
- case "sqlite": return `CREATE TABLE ${r} AS SELECT * FROM ${n} WHERE 0`;
1172
- case "mssql": return `SELECT * INTO ${r} FROM ${n} WHERE 1 = 0`;
1173
- }
1174
- }, Ln = (e, t) => {
1175
- let n = J(q(e), t);
1176
- return t === "mssql" ? `IF OBJECT_ID('${q(e)}', 'U') IS NOT NULL DROP TABLE ${n}` : `DROP TABLE IF EXISTS ${n}`;
1177
- }, Rn = (e, t) => {
1178
- let n = t === "mysql" || t === "mariadb", r = [];
1179
- n && r.push("SET FOREIGN_KEY_CHECKS = 0");
1180
- for (let n of [...e].reverse()) r.push(`DELETE FROM ${J(n.name, t)}`);
1181
- for (let n of e) {
1182
- if (n.columns.length === 0) throw Error(`refusing to build a swap for '${n.name}' with no columns: an empty list would have to mean \`SELECT *\`, which is the shape that fails on a generated column.`);
1183
- let e = n.columns.map((e) => J(e, t)).join(", ");
1184
- r.push(`INSERT INTO ${J(n.name, t)} (${e}) SELECT ${e} FROM ${J(q(n.name), t)}`);
1185
- }
1186
- return n && r.push("SET FOREIGN_KEY_CHECKS = 1"), r;
1187
- }, zn = (e, t) => {
1188
- let n = [], r = () => {
1189
- for (let e of n) t.unregisterTable(e);
1190
- };
1191
- try {
1192
- for (let r of e) {
1193
- let e = t.getTable(r);
1194
- if (e === void 0) continue;
1195
- let i = q(r);
1196
- t.registerTable({
1197
- ...e,
1198
- tableName: i,
1199
- isReactive: !1
1200
- }), n.push(i);
1201
- }
1202
- } catch (e) {
1203
- throw r(), e;
1204
- }
1205
- return {
1206
- staged: n,
1207
- undo: r
1208
- };
1209
- }, Bn = (e, t) => {
1210
- let n = new Set(t), r = [];
1211
- for (let t of e.tables) {
1212
- if (!n.has(t.name)) continue;
1213
- let e = t.primaryKey?.[0] ?? "id";
1214
- for (let i of t.columns) {
1215
- let a = i.references;
1216
- a !== void 0 && n.has(a.table) && r.push({
1217
- from: t.name,
1218
- column: i.name,
1219
- to: a.table,
1220
- toColumn: a.column,
1221
- primaryKey: e
1222
- });
1223
- }
1224
- }
1225
- return r;
1226
- }, Vn = (e, t) => {
1227
- let n = J(q(e.from), t), r = J(q(e.to), t), i = J(e.column, t), a = J(e.toColumn, t);
1228
- return `SELECT ${J(e.primaryKey, t)} AS pk, ${i} AS value FROM ${n} c WHERE ${i} IS NOT NULL AND NOT EXISTS (SELECT 1 FROM ${r} p WHERE p.${a} = c.${i})`;
1229
- }, Hn = (e) => {
1230
- if (e.length === 0) return;
1231
- let t = e.slice(0, 20), n = t.map((e) => ` ${e.table}.${e.primaryKey}: ${e.column} = ${JSON.stringify(e.value)} — no such row in ${e.references}`);
1232
- return `the swap could not run: ${e.length} row(s) in the bundle reference a row the bundle does not carry.\n${n.join("\n")}\n` + (e.length > t.length ? ` … and ${e.length - t.length} more\n` : "") + " The target is UNCHANGED — the swap runs in one transaction and none of it committed.\n Every offending row is listed above rather than only the first, so this can be fixed in one pass.";
1233
- }, Un = async (e, t) => {
1234
- t.length !== 0 && await e.run(ve.SqlClient.pipe(n.flatMap((e) => n.forEach(t, (t) => e.unsafe(t), { discard: !0 }))));
1235
- }, Wn = async (e, t, r) => {
1236
- let i = Rn(t, r);
1237
- i.length !== 0 && await e.run(ve.SqlClient.pipe(n.flatMap((e) => e.withTransaction(n.forEach(i, (t) => e.unsafe(t), { discard: !0 })))));
1238
- }, Gn = async (e, t, r) => {
1239
- let i = [];
1240
- for (let a of t) {
1241
- let t = await e.run(ve.SqlClient.pipe(n.flatMap((e) => e.unsafe(Vn(a, r)))));
1242
- for (let e of t) i.push({
1243
- table: a.from,
1244
- primaryKey: String(e.pk ?? ""),
1245
- column: a.column,
1246
- value: e.value,
1247
- references: a.to
1248
- });
1249
- }
1250
- return i;
1251
- }, Kn = (e, t) => {
1252
- let n = new Map(t.map((e, t) => [e, t]));
1253
- return e.filter((e) => {
1254
- if (e.from === e.to) return !1;
1255
- let t = n.get(e.from), r = n.get(e.to);
1256
- return t === void 0 || r === void 0 ? !1 : r > t;
1257
- });
1258
- }, qn = (e) => e === "mysql" || e === "mariadb", Jn = (e) => {
1259
- if (e.mode !== "replace") return {
1260
- active: !1,
1261
- tables: [],
1262
- blocker: `staging applies to \`replace\` only (this run is \`${e.mode}\`).`
1263
- };
1264
- let t = (() => {
1265
- let t = e.targetDialect;
1266
- return t === "postgres" || t === "mysql" || t === "mariadb" || t === "sqlite" || t === "mssql" ? t : void 0;
1267
- })();
1268
- if (t === void 0) return {
1269
- active: !1,
1270
- tables: [],
1271
- blocker: `the target dialect (${e.targetDialect ?? "unknown"}) is not one this build can stage on.`
1272
- };
1273
- if (!e.canRunSql) return {
1274
- active: !1,
1275
- tables: [],
1276
- blocker: "this store cannot send DDL (no raw-SQL channel), which a staged swap needs."
1277
- };
1278
- let n = e.targetSnapshot;
1279
- if (n === void 0) return {
1280
- active: !1,
1281
- tables: [],
1282
- blocker: "this caller did not supply a snapshot of the target schema, so the staging tables cannot be given the target's column shape."
1283
- };
1284
- let r = new Map(n.tables.map((e) => [e.name, e])), i = e.bundleTables.filter((t) => !e.isSkipped(t)), a = i.filter((e) => !r.has(e));
1285
- if (a.length > 0) return {
1286
- active: !1,
1287
- tables: [],
1288
- blocker: `the bundle carries ${a.length} table(s) the target does not have (${a.join(", ")}). Staging copies the TARGET's column shape, and there is none for those.`
1289
- };
1290
- let o = [];
1291
- for (let e of i) {
1292
- let t = r.get(e).columns.filter((e) => e.generatedAs === void 0).map((e) => e.name);
1293
- if (t.length === 0) return {
1294
- active: !1,
1295
- tables: [],
1296
- blocker: `\`${e}\` has no ordinary columns in the target (every column is generated), so a staged copy would have nothing to write.`
1297
- };
1298
- o.push({
1299
- name: e,
1300
- columns: t
1301
- });
1302
- }
1303
- if (o.length === 0) return {
1304
- active: !1,
1305
- tables: [],
1306
- blocker: "every table in the bundle is one that describes a deployment, so there is nothing for a staged swap to move."
1307
- };
1308
- let s = o.map((e) => e.name), c = qn(t) ? [] : Kn(Bn(n, s), s);
1309
- return c.length > 0 ? {
1310
- active: !1,
1311
- tables: [],
1312
- blocker: `the bundle's tables reference each other in a cycle (${c.map((e) => `${e.from}.${e.column} → ${e.to}`).join(", ")}), and a bulk copy cannot satisfy both directions on this dialect.`
1313
- } : {
1314
- active: !0,
1315
- tables: o
1316
- };
1317
- }, Yn = (e) => n.gen(function* () {
1318
- if (e.mode !== "replace" || e.dir === void 0 || e.tables.length === 0) return;
1319
- if (e.snapshot === void 0) return yield* n.fail(new H({
1320
- reason: "rollback capture needs the TARGET's schema, and this run has none.\n `--mode replace` empties the target first, so a run without a capture can leave it\n emptied and unfilled if the process does not survive to roll back. Pass the target\n snapshot, or `--no-rollback` to accept that risk deliberately.",
1321
- path: e.dir
1322
- }));
1323
- let t = {
1324
- ...e.snapshot,
1325
- tables: e.snapshot.tables.filter((t) => e.tables.includes(t.name))
1326
- };
1327
- if (t.tables.length === 0) return;
1328
- e.onWarn?.(`capturing the target's current rows before emptying it → ${e.dir}\n This is what a failed replace is restored FROM. It is written before the first delete,
1329
- so it survives a run whose process does not.`);
1330
- let r = yield* _n({
1331
- store: e.store,
1332
- snapshot: t,
1333
- dialect: e.dialect ?? "unknown",
1334
- outDir: e.dir,
1335
- scope: { kind: "all" },
1336
- compression: "none"
1337
- }).pipe(n.mapError((t) => new H({
1338
- reason: `rollback capture failed, so the import did not start and the target is untouched: ${t.reason ?? String(t)}\n Fix the capture, or re-run with \`--no-rollback\` to proceed without one.`,
1339
- path: e.dir
1340
- })));
1341
- return {
1342
- dir: e.dir,
1343
- rows: r.tables.reduce((e, t) => e + t.rowCount, 0),
1344
- tables: r.tables.length
1345
- };
1346
- }), Xn = (e) => `rollback capture: ${e.rows} row(s) across ${e.tables} table(s) → ${e.dir}\n If this run does not finish, restore with: voltro data import ${e.dir} --mode replace`, Zn = /__dropped_\d+$/, Qn = (e, t) => {
1347
- let n = [], r = /* @__PURE__ */ new Map();
1348
- for (let e of t.tables) {
1349
- let t = e.name;
1350
- r.has(t) || r.set(t, e);
1351
- }
1352
- for (let t of [...e.tables].sort((e, t) => e.name.localeCompare(t.name))) {
1353
- let e = r.get(t.name);
1354
- if (e === void 0) {
1355
- n.push(`table '${t.name}' is in the bundle but MISSING from the target — its rows have nowhere to go`);
1356
- continue;
1357
- }
1358
- let i = new Map(e.columns.map((e) => [e.name, e])), a = new Map(t.columns.map((e) => [e.name, e]));
1359
- for (let [e, r] of a) {
1360
- let a = i.get(e);
1361
- if (a === void 0) {
1362
- n.push(Zn.test(e) ? `${t.name}.${e}: a soft-dropped column, present only in the SOURCE — reclaim it there (voltro db gc-snapshots --before <date>) or re-export without it; the target is not missing anything` : `${t.name}.${e}: in the bundle, MISSING from the target — the value has nowhere to go`);
1363
- continue;
1364
- }
1365
- a.type !== r.type && n.push(`${t.name}.${e}: type '${r.type}' (bundle) vs '${a.type}' (target)`);
1366
- }
1367
- for (let [e, r] of i) a.has(e) || !r.nullable && !r.hasDefault && n.push(`${t.name}.${e}: the target requires it (NOT NULL, no default) and the bundle carries no value — every row of this table would fail`);
1368
- }
1369
- return n.length > 30 ? [...n.slice(0, 30), `… and ${n.length - 30} more difference(s)`] : n;
1370
- }, $n = (e, t, n) => {
1371
- let r = ie(t);
1372
- if (r === e || n === void 0) return {
1373
- drifted: !1,
1374
- bundleFingerprint: e,
1375
- targetFingerprint: r,
1376
- diff: []
1377
- };
1378
- let i = Qn(n, t);
1379
- return {
1380
- drifted: i.length > 0,
1381
- bundleFingerprint: e,
1382
- targetFingerprint: r,
1383
- diff: i
1384
- };
1385
- }, er = 2e3, tr = 2e3, nr = 200, rr = /* @__PURE__ */ new Set([
1386
- "BundleError",
1387
- "CodecError",
1388
- "IntegrityError",
1389
- "CrossDialectError",
1390
- "ImportModeError",
1391
- "RowsRefusedError",
1392
- "SchemaDriftError",
1393
- "StorageError"
1394
- ]), ir = (t) => {
1395
- let n = e.isCause(t) ? t : r.isExit(t) && r.isFailure(t) ? t.cause : void 0, i = n === void 0 ? [t] : n.reasons.flatMap((t) => e.isFailReason(t) ? [t.error] : e.isDieReason(t) ? [t.defect] : []);
1396
- for (let n of i) {
1397
- let i = n?._tag;
1398
- if (typeof i == "string" && rr.has(i)) return n;
1399
- if (n !== t && (e.isCause(n) || r.isExit(n))) {
1400
- let e = ir(n);
1401
- if (e) return e;
1402
- }
1403
- }
1404
- }, ar = (t) => a.getOrUndefined(e.findErrorOption(t)) ?? Error(e.pretty(t)), or = (e) => n.tryPromise({
1405
- try: async () => {
1406
- let t = await import("node:fs/promises"), n = await import("node:path");
1407
- return kt(await t.readFile(n.join(e, At), "utf8"));
1408
- },
1409
- catch: (e) => new H({
1410
- reason: `manifest read/parse failed: ${String(e?.message ?? e)}`,
1411
- path: At
1412
- })
1413
- }), sr = (e) => {
1414
- let t;
1415
- return n.gen(function* () {
1416
- let i = yield* n.promise(() => import("node:path")), a = yield* or(e.bundleDir);
1417
- if (a.formatVersion !== 1) return yield* n.fail(new H({ reason: `unsupported bundle formatVersion ${a.formatVersion} (expected 1)` }));
1418
- if (e.tables !== void 0) {
1419
- let t = new Set(a.tables.map((e) => e.name)), r = e.tables.filter((e) => !t.has(e));
1420
- if (r.length > 0) return yield* n.fail(new H({ reason: `--tables names ${r.length} table(s) this bundle does not carry: ${r.join(", ")}. The bundle carries: ${[...t].sort().join(", ")}.` }));
1421
- }
1422
- let o = e.tables === void 0 ? void 0 : new Set(e.tables), s = o === void 0 ? a : {
1423
- ...a,
1424
- tables: a.tables.filter((e) => o.has(e.name))
1425
- };
1426
- if (e.targetSnapshot) {
1427
- let t = { tables: s.tables.map((e) => ({
1428
- name: e.name,
1429
- indexes: [],
1430
- columns: Object.entries(e.columnTypes).map(([e, t]) => ({
1431
- name: e,
1432
- type: t,
1433
- nullable: !0,
1434
- unique: !1,
1435
- hasDefault: !1
1436
- }))
1437
- })) }, r = $n(s.source.schemaFingerprint, e.targetSnapshot, t);
1438
- if (r.drifted) {
1439
- if (!e.force) return yield* n.fail(new et({
1440
- bundleFingerprint: r.bundleFingerprint,
1441
- targetFingerprint: r.targetFingerprint,
1442
- diff: r.diff
1443
- }));
1444
- e.onWarn?.(`schema drift: the target schema (fingerprint ${r.targetFingerprint.slice(0, 16)}) differs from the bundle's source (${r.bundleFingerprint.slice(0, 16)}); importing anyway (--force). ${r.diff.join("; ")}`);
1445
- }
1446
- }
1447
- if (e.targetDialect && e.targetDialect !== s.source.dialect) {
1448
- let t = Cn(s.tables, s.source.dialect, e.targetDialect).filter((e) => e.severity === "block");
1449
- if (t.length > 0 && !e.allowIncompatible) return yield* n.fail(new rt({
1450
- sourceDialect: s.source.dialect,
1451
- targetDialect: e.targetDialect,
1452
- blocking: t.map(wn)
1453
- }));
1454
- }
1455
- let d = e.verify ?? !0, p = e.mode ?? "upsert", g = e.atomic ?? p === "replace", _ = e.onConflict ?? "skip", y = e.ledgerPath ?? i.join(e.bundleDir, ".import..ledger.json"), x = yield* Ut(y);
1456
- if (p === "replace" && s.scope.kind !== "all") return yield* n.fail(new nt({
1457
- mode: p,
1458
- reason: `replace refuses a partial bundle (scope='${s.scope.kind}') — truncating would delete rows not in the bundle. Use --mode upsert, or re-export with full scope. To leave specific tables out of a FULL export, use \`--exclude a,b\`: it keeps the scope \`all\` and records what it left out.`
1459
- }));
1460
- if (p === "replace" && (s.scope.exclude?.length ?? 0) > 0) {
1461
- let t = s.scope.exclude;
1462
- e.onWarn?.(`this bundle was exported with ${t.length} table(s) excluded: ${t.join(", ")}.\n A replace empties and refills the tables the bundle CARRIES, so those keep whatever rows the
1463
- target already holds. That is not "everything the bundle says" — check it is what you meant.`);
1464
- }
1465
- if (p === "replace") {
1466
- let t = s.tables.map((e) => e.name), r = e.store.incomingForeignKeys ? yield* n.tryPromise({
1467
- try: () => e.store.incomingForeignKeys(t),
1468
- catch: (e) => new H({ reason: `foreign-key pre-flight failed: ${jn(e)}` })
1469
- }) : e.targetSnapshot === void 0 ? [] : vn(e.targetSnapshot, t), i = [], a = /* @__PURE__ */ new Map();
1470
- for (let t of r) {
1471
- if (!a.has(t.from)) {
1472
- let r = yield* n.tryPromise({
1473
- try: () => e.store.query({
1474
- table: t.from,
1475
- predicate: void 0,
1476
- order: [],
1477
- take: void 0,
1478
- skip: void 0,
1479
- projection: []
1480
- }),
1481
- catch: () => new H({ reason: `unreadable: ${t.from}` })
1482
- }).pipe(n.map((e) => e.length), n.orElseSucceed(() => 0));
1483
- a.set(t.from, r);
1484
- }
1485
- let r = a.get(t.from) ?? 0;
1486
- r > 0 && i.push({
1487
- ...t,
1488
- rowCount: r
1489
- });
1490
- }
1491
- if (i.length > 0) return yield* n.fail(new nt({
1492
- mode: p,
1493
- reason: yn(i)
1494
- }));
1495
- }
1496
- if (e.dryRun === !0) return e.traceId !== void 0 && (yield* n.promise(() => e.store.update(l, e.traceId, {
1497
- finishedAt: /* @__PURE__ */ new Date(),
1498
- tablesWritten: 0,
1499
- rowsWritten: 0,
1500
- failure: null
1501
- }).then(() => void 0, () => void 0))), {
1502
- manifest: s,
1503
- tablesWritten: 0,
1504
- rowsWritten: 0,
1505
- tablesSkipped: 0,
1506
- rowsSkipped: 0,
1507
- fullyResumed: !1
1508
- };
1509
- let C = /* @__PURE__ */ new Map();
1510
- for (let t of e.targetSnapshot?.tables ?? []) {
1511
- let e = t.columns.filter((e) => e.generatedAs !== void 0).map((e) => e.name);
1512
- e.length > 0 && C.set(t.name, e);
1513
- }
1514
- let ie = (e, t) => {
1515
- let n = C.get(e);
1516
- if (n === void 0) return t;
1517
- let r;
1518
- for (let e of n) e in t && (r ??= { ...t }, delete r[e]);
1519
- return r ?? t;
1520
- }, ae = (e, t, n, r = !1) => {
1521
- let i = A ? q(t.name) : t.name, a = t.primaryKey, o = w(t.name, ie(t.name, n), t.columnTypes);
1522
- return p === "append" ? _ === "fail" ? e.insert(i, o) : e.insertIgnore(i, o, { conflictColumns: [a] }) : p === "replace" ? r ? e.upsert(i, o, { conflictColumns: [a] }) : e.insert(i, o) : e.upsert(i, o, { conflictColumns: [a] });
1523
- }, oe = e.targetDialect !== void 0 && e.targetDialect !== "memory", w = (e, t, n) => {
1524
- if (!oe || n === void 0 || v(e) !== void 0) return t;
1525
- let r = null;
1526
- for (let [e, i] of Object.entries(n)) {
1527
- let n = t[e];
1528
- i === "json" && n != null && (r === null && (r = { ...t }), r[e] = JSON.stringify(n));
1529
- }
1530
- return r ?? t;
1531
- }, T = (e, t) => e.savepoint ? e.savepoint(t) : t(), E = 0, se = (t, n, r) => {
1532
- r.unclassified && (E++, !(E > 3) && e.onWarn?.(`row refused with no driver detail — table '${t.name}', row '${String(n.id ?? "?")}'. The full rendering follows so the LAYER that refused is on the record; it is logged, never returned. ${r.raw}` + (E === 3 ? " (further unclassified refusals are counted, not logged)" : "")));
1533
- }, ce = (e, t, r = !0) => n.tryPromise({
1534
- try: e,
1535
- catch: (e) => kn(e)
1536
- }).pipe(n.map(() => !0), n.catch((e) => n.sync(() => (r && (t.reason = e.reason, se(t.entry, t.row, e)), !1)))), le = (e) => {
1537
- let t = Object.entries(e.entry.columnTypes).filter(([t, n]) => n === "reference" && e.row[t] !== null && e.row[t] !== void 0).map(([e]) => e);
1538
- if (t.length !== 0) return {
1539
- ...e.row,
1540
- ...Object.fromEntries(t.map((e) => [e, null]))
1541
- };
1542
- }, de = (e) => {
1543
- let t = new Set(e.map((e) => String(e.row[e.entry.primaryKey]))), n = (e) => Object.entries(e.entry.columnTypes).some(([n, r]) => {
1544
- if (r !== "reference") return !1;
1545
- let i = e.row[n];
1546
- return i != null && t.has(String(i));
1547
- }), r = e.map((e) => ({
1548
- table: e.entry.name,
1549
- id: String(e.row[e.entry.primaryKey]),
1550
- reason: e.reason,
1551
- derived: n(e)
1552
- })), i = [...r.filter((e) => !e.derived), ...r.filter((e) => e.derived)], a = /* @__PURE__ */ new Map();
1553
- for (let e of r) {
1554
- let t = a.get(e.table) ?? {
1555
- refused: 0,
1556
- primary: 0
1557
- };
1558
- t.refused++, e.derived || t.primary++, a.set(e.table, t);
1559
- }
1560
- return new $e({
1561
- rows: i.slice(0, 20),
1562
- primaryCount: r.filter((e) => !e.derived).length,
1563
- totalCount: e.length,
1564
- byTable: [...a.entries()].map(([e, t]) => ({
1565
- table: e,
1566
- refused: t.refused,
1567
- primary: t.primary
1568
- })).sort((e, t) => t.refused - e.refused || e.table.localeCompare(t.table)).slice(0, 100)
1569
- });
1570
- }, fe = (e, t) => n.gen(function* () {
1571
- let r = t, i = [], a = !0;
1572
- for (; r.length > 0 && a;) {
1573
- a = !1;
1574
- let t = [];
1575
- for (let n of r) (yield* ce(() => T(e, () => ae(e, n.entry, n.row, !0)), n)) ? a = !0 : t.push(n);
1576
- if (!a && t.length > 0) for (let n = t.length - 1; n >= 0; n--) {
1577
- let r = t[n], o = le(r);
1578
- o !== void 0 && (yield* ce(() => T(e, () => ae(e, r.entry, o, !0)), r, !1)) && (i.push(r), t.splice(n, 1), a = !0);
1579
- }
1580
- r = t;
1581
- }
1582
- for (let t of i) (yield* ce(() => T(e, () => e.upsert(t.entry.name, w(t.entry.name, t.row, t.entry.columnTypes), { conflictColumns: [t.entry.primaryKey] })), t)) || (r = [...r, t]);
1583
- if (r.length > 0) return yield* n.fail(de(r));
1584
- }), D = (() => {
1585
- let t = e.targetDialect;
1586
- return t === "postgres" || t === "mysql" || t === "mariadb" || t === "sqlite" || t === "mssql" ? t : void 0;
1587
- })(), O = Jn({
1588
- mode: p,
1589
- ...e.targetDialect === void 0 ? {} : { targetDialect: e.targetDialect },
1590
- ...e.targetSnapshot === void 0 ? {} : { targetSnapshot: e.targetSnapshot },
1591
- bundleTables: s.tables.map((e) => e.name),
1592
- isSkipped: V,
1593
- canRunSql: Nn(e.store)
1594
- }), k = O.tables, pe = s.tables.filter((e) => !V(e.name) && S(e.name)).map((e) => e.name), A = O.active, he = !1, j = 0, M = 0, ge, _e, ve = 0, N = 0, ye = (t, r) => n.gen(function* () {
1595
- if (p === "replace" && !A && !(r && x.truncated)) {
1596
- e.recordInterruption === !0 && (yield* n.tryPromise({
1597
- try: () => t.insert(u, {
1598
- id: m("repl", be),
1599
- startedAt: P,
1600
- tables: String(s.tables.length),
1601
- capture: F?.dir ?? null,
1602
- via: e.via ?? "direct"
1603
- }),
1604
- catch: (e) => new H({ reason: `could not record that this replace started: ${String(e?.message ?? e)}\n The marker is what makes an interrupted run visible instead of silent, so the run does
1605
- not proceed without it.` })
1606
- }));
1607
- let i = [...s.tables].reverse().map((e) => e.name).filter((e) => !V(e)), a = !r, o = t.emptyTables ? () => t.emptyTables(i) : a ? async () => {
1608
- for (let e of [...s.tables].reverse()) await t.deleteMany(e.name, { where: b(e.primaryKey) });
1609
- } : () => t.transactional(async (e) => {
1610
- for (let t of [...s.tables].reverse()) await e.deleteMany(t.name, { where: b(t.primaryKey) });
1611
- });
1612
- yield* n.tryPromise({
1613
- try: o,
1614
- catch: (e) => new H({ reason: `emptying the target for --mode replace failed (nothing was deleted): ${jn(e)}` })
1615
- }), r && (x.truncated = !0, yield* G(y, x));
1616
- }
1617
- let a = [], o = [], l = [], f = [], h = s.tables.length;
1618
- for (let u = 0; u < s.tables.length; u++) {
1619
- let m = s.tables[u];
1620
- if (V(m.name)) {
1621
- f.push(m.name);
1622
- continue;
1623
- }
1624
- if (r && m.name in x.tables) {
1625
- l.push(m.name), N++, ve += x.tables[m.name].rowCount, W(e.onProgress, {
1626
- phase: "import",
1627
- table: m.name,
1628
- index: u,
1629
- tableCount: h,
1630
- event: "done",
1631
- rowsDone: x.tables[m.name].rowCount,
1632
- total: m.rowCount
1633
- });
1634
- continue;
1635
- }
1636
- let _ = i.join(e.bundleDir, m.file);
1637
- W(e.onProgress, {
1638
- phase: "import",
1639
- table: m.name,
1640
- index: u,
1641
- tableCount: h,
1642
- event: "start",
1643
- rowsDone: 0,
1644
- total: m.rowCount
1645
- });
1646
- let v = a.length, b = n.gen(function* () {
1647
- let { createHash: r } = yield* n.promise(() => import("node:crypto")), i = r("sha256"), o = 0, l = me("bundle", () => ue(_).pipe(St(s.compression))).pipe(c.tap((e) => n.sync(() => i.update(e)))), u = async (e) => {
1648
- let n = {
1649
- entry: m,
1650
- row: e,
1651
- reason: ""
1652
- };
1653
- try {
1654
- await T(t, () => ae(t, m, e));
1655
- } catch (t) {
1656
- let r = kn(t);
1657
- n.reason = r.reason, se(m, e, r), a.push(n);
1658
- }
1659
- }, d = yield* n.promise(async () => {
1660
- let n = e.copyLoader;
1661
- if (!n || n.dialect !== "postgres") return !1;
1662
- if (g) return he || (he = !0, e.onWarn?.("bulk COPY is off for this run: it needs its own connection and this import runs in ONE transaction (the default for --mode replace, so a failure mid-load cannot leave the target emptied and unfilled). Pass --no-atomic to trade that guarantee back for COPY.")), !1;
1663
- if (p === "replace") return !0;
1664
- if (p !== "upsert") return !1;
1665
- try {
1666
- return (await t.query({
1667
- table: m.name,
1668
- predicate: void 0,
1669
- order: [],
1670
- take: 1,
1671
- skip: void 0,
1672
- projection: void 0
1673
- })).length === 0;
1674
- } catch {
1675
- return !1;
1676
- }
1677
- }), f = Object.keys(m.columnTypes), h = Math.max(1, e.copyBatchSize ?? 5e3), v = [], y = async () => {
1678
- if (v.length === 0) return;
1679
- let t = v;
1680
- v = [];
1681
- try {
1682
- await e.copyLoader.copyInto({
1683
- table: m.name,
1684
- columns: f,
1685
- columnTypes: m.columnTypes,
1686
- rows: t
1687
- });
1688
- } catch (n) {
1689
- d = !1, e.onWarn?.(`bulk COPY into ${m.name} failed (${String(n?.message ?? n)}); falling back to per-row writes for this table`);
1690
- for (let e of t) await u(e);
1691
- }
1692
- }, b = Math.max(1, e.savepointBatchSize ?? 200), x = typeof t.savepoint == "function", S = [], ee = async () => {
1693
- if (S.length === 0) return;
1694
- let e = S;
1695
- S = [];
1696
- try {
1697
- await t.savepoint(async () => {
1698
- for (let n of e) await ae(t, m, n);
1699
- });
1700
- } catch {
1701
- for (let t of e) await u(t);
1702
- }
1703
- };
1704
- return yield* c.runForEach(an(l, m.name, m.columnTypes), (e) => n.promise(async () => {
1705
- if (o++, _e?.(), d) {
1706
- v.push(e), v.length >= h && await y();
1707
- return;
1708
- }
1709
- if (x) {
1710
- S.push(e), S.length >= b && await ee();
1711
- return;
1712
- }
1713
- await u(e);
1714
- })), yield* n.promise(ee), yield* n.promise(y), {
1715
- rowCount: o,
1716
- checksum: i.digest("hex")
1717
- };
1718
- }), { rowCount: S, checksum: ee } = yield* Vt("import", m.name, u, h, (e) => e.rowCount, b);
1719
- if (d) {
1720
- if (ee !== m.checksum) return yield* n.fail(new Xe({
1721
- what: `table ${m.name} checksum`,
1722
- expected: m.checksum,
1723
- actual: ee
1724
- }));
1725
- if (S !== m.rowCount) return yield* n.fail(new Xe({
1726
- what: `table ${m.name} rowCount`,
1727
- expected: String(m.rowCount),
1728
- actual: String(S)
1729
- }));
1730
- }
1731
- W(e.onProgress, {
1732
- phase: "import",
1733
- table: m.name,
1734
- index: u,
1735
- tableCount: h,
1736
- event: "done",
1737
- rowsDone: S,
1738
- total: m.rowCount
1739
- }), M++, j += S, ge?.(), _e?.(), r && (a.length > v ? o.push({
1740
- name: m.name,
1741
- rowCount: S
1742
- }) : (x.tables[m.name] = {
1743
- rowCount: S,
1744
- checksum: m.checksum
1745
- }, yield* G(y, x)));
1746
- }
1747
- if (a.length > 0 && (yield* fe(t, a)), p === "replace" && (yield* n.promise(() => t.deleteMany(u, { where: {
1748
- column: "startedAt",
1749
- op: "lte",
1750
- value: P
1751
- } }).then(() => void 0, () => void 0))), r && o.length > 0) {
1752
- for (let e of o) {
1753
- let t = s.tables.find((t) => t.name === e.name);
1754
- x.tables[e.name] = {
1755
- rowCount: e.rowCount,
1756
- checksum: t.checksum
1757
- };
1758
- }
1759
- yield* G(y, x);
1760
- }
1761
- {
1762
- let t = Je(f, "skipped");
1763
- t !== void 0 && e.onWarn?.(t);
1764
- }
1765
- if (l.length > 0) {
1766
- let t = l.length === s.tables.length;
1767
- e.onWarn?.(`resume: ${l.length} of ${s.tables.length} table(s) were already applied by an earlier run of THIS bundle directory, so this run wrote NO rows for them${t ? " — that is every table in the bundle, so nothing was written at all" : ""}. Tables: ${l.join(", ")}. Delete ${y} to force a full re-import.`);
1768
- }
1769
- }), be = `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8)}`, P = /* @__PURE__ */ new Date(), F = yield* Yn({
1770
- store: e.store,
1771
- mode: p,
1772
- dir: e.rollbackDir,
1773
- snapshot: e.targetSnapshot,
1774
- dialect: e.targetDialect,
1775
- tables: s.tables.map((e) => e.name).filter((e) => !V(e)),
1776
- onWarn: e.onWarn
1777
- });
1778
- F !== void 0 && (e.onRollbackCaptured !== void 0 && (yield* n.tryPromise({
1779
- try: () => e.onRollbackCaptured(F),
1780
- catch: (e) => new H({
1781
- reason: `the rollback capture could not be stored durably, so the import did not start and the target is untouched: ${String(e?.message ?? e)}\n The capture only helps if it outlives this process — that is the failure it exists for.`,
1782
- path: F.dir
1783
- })
1784
- })), e.onWarn?.(Xn(F)));
1785
- let xe = (e) => p === "replace" ? te(e) : e(), I = (e) => p === "replace" ? ne(e) : e, L = e.traceId ?? m("dimp", `${Date.now()}-${s.source.schemaFingerprint ?? ""}`), Se = e.traceId !== void 0, Ce = (e.targetSnapshot?.tables.some((e) => e.name === l) ?? !1) || v(l) !== void 0;
1786
- Ce && h(f), Ce && e.traceId === void 0 && (yield* n.promise(() => e.store.insert(l, {
1787
- id: L,
1788
- mode: p,
1789
- via: e.via ?? "direct",
1790
- direction: "import",
1791
- bundle: e.bundleDir,
1792
- sourceFingerprint: s.source.schemaFingerprint ?? null,
1793
- tablesWritten: 0,
1794
- rowsWritten: 0,
1795
- staged: p === "replace" ? A : null
1796
- }).then(() => {
1797
- Se = !0;
1798
- }, (t) => {
1799
- e.onWarn?.(`the import trace could not be written (${String(t?.message ?? t)}).\n The import continues — this is a record of what happened, not a safety check. Run
1800
- \`voltro db apply\` so the next one is recorded.`);
1801
- }))), e.traceId !== void 0 && Ce && (yield* n.promise(() => e.store.update(l, L, {
1802
- staged: p === "replace" ? A : null,
1803
- sourceFingerprint: s.source.schemaFingerprint ?? null
1804
- }).then(() => void 0, () => void 0)));
1805
- let we = 0, Te = !1;
1806
- ge = () => {
1807
- if (!Se || Te) return;
1808
- let t = Date.now();
1809
- t - we < er || (we = t, e.store.update(l, L, {
1810
- tablesWritten: M,
1811
- rowsWritten: j
1812
- }).then(() => void 0, () => {
1813
- Te = !0;
1814
- }));
1815
- };
1816
- let Ee = (t) => Se ? e.store.update(l, L, {
1817
- finishedAt: t === null ? /* @__PURE__ */ new Date() : null,
1818
- tablesWritten: M,
1819
- rowsWritten: j,
1820
- failure: t
1821
- }).then(() => void 0, () => void 0) : Promise.resolve();
1822
- if (t = (e) => Ee(e), p === "replace" && !A && O.blocker !== void 0 && e.onWarn?.(Pn(O.blocker)), p === "replace" && pe.length > 0 && e.onWarn?.(`write recorders are SUSPENDED for this replace (${pe.length} table(s) have one).\n A replace sets a state rather than changing rows, so a per-row history entry would describe
1823
- something that did not happen — and this path already writes through the raw store, without
1824
- tenant scoping or \`audit()\` stamping, for the same reason.`), A) {
1825
- e.onWarn?.(`staging ${k.length} table(s) before the swap — the target keeps its rows until the load stands.\n The destructive step is one server-side transaction at the end, not the whole load.`);
1826
- let t = e.store, r = k.map((e) => e.name), i = m("repl", `staging-${be}`), a = !1;
1827
- if (e.recordInterruption === !0) {
1828
- yield* n.promise(() => e.store.insert(u, {
1829
- id: i,
1830
- startedAt: P,
1831
- kind: "staging",
1832
- tables: String(k.length),
1833
- capture: F?.dir ?? null,
1834
- via: e.via ?? "direct",
1835
- staging: r.map(q).join(","),
1836
- heartbeatAt: /* @__PURE__ */ new Date(),
1837
- resumable: !g
1838
- }).then(() => {
1839
- a = !0;
1840
- }, (t) => {
1841
- e.onWarn?.(`the staging tables could not be recorded (${String(t?.message ?? t)}).\n The import continues and the target is unaffected — but if this run dies before the swap,
1842
- nothing at boot will mention the scratch tables it leaves. \`voltro data clear-staging\` finds
1843
- them by introspection.`);
1844
- }));
1845
- let t = Date.now();
1846
- _e = () => {
1847
- if (!a) return;
1848
- let n = Date.now();
1849
- n - t < tr || (t = n, e.store.update(u, i, { heartbeatAt: /* @__PURE__ */ new Date() }).then(() => void 0, () => void 0));
1850
- };
1851
- }
1852
- let o = () => a ? e.store.delete(u, i).then(() => void 0, () => void 0) : Promise.resolve(), s = zn(r, {
1853
- getTable: (e) => v(e),
1854
- registerTable: (e) => {
1855
- ee(e);
1856
- },
1857
- unregisterTable: re
1858
- }), c = !1;
1859
- try {
1860
- let i = g ? r : r.filter((e) => !Wt(x, e));
1861
- yield* n.tryPromise({
1862
- try: async () => {
1863
- await Un(t, i.map((e) => Ln(e, D))), await Un(t, i.map((e) => In(e, D)));
1864
- },
1865
- catch: (e) => new H({ reason: `could not prepare the staging tables, so the import did not start and the target is untouched: ${String(e?.message ?? e)}` })
1866
- }), yield* I(ye(e.store, !g)), !g && x.truncated === !0 ? c = !0 : yield* n.tryPromise({
1867
- try: () => Wn(t, k, D).then(() => {
1868
- c = !0;
1869
- }),
1870
- catch: (e) => new H({ reason: String(e?.message ?? e) })
1871
- }).pipe(n.catch((i) => n.gen(function* () {
1872
- let a = Hn(yield* n.promise(() => Gn(t, Bn(e.targetSnapshot, r), D).then((e) => e, () => [])));
1873
- return yield* n.fail(new H({ reason: a ?? `the swap failed and the target is UNCHANGED: ${i.reason}` }));
1874
- }))), !g && c && x.truncated !== !0 && (x.truncated = !0, yield* G(y, x));
1875
- } finally {
1876
- s.undo(), c || g ? (_e = void 0, yield* n.promise(() => Un(t, r.map((e) => Ln(e, D))).then(() => void 0, () => void 0)), yield* n.promise(o)) : e.onWarn?.("the staged rows are KEPT so a re-run can continue from them rather than reload.\n If you are not going to re-run this bundle, drop them: voltro data clear-staging --yes");
1877
- }
1878
- } else g ? yield* n.tryPromise({
1879
- try: () => e.store.transactional(async (e) => {
1880
- let t = await xe(() => n.runPromiseExit(ye(e, !1)));
1881
- if (r.isFailure(t)) throw ar(t.cause);
1882
- }),
1883
- catch: (e) => ir(e) ?? new H({ reason: `atomic import failed (rolled back): ${String(e?.message ?? e)}` })
1884
- }) : yield* I(ye(e.store, !0));
1885
- if (e.assets && s.assets.index && !e.assetsAlreadyRestored) {
1886
- let t = e.assets;
1887
- yield* _t({
1888
- sink: t,
1889
- assetsDir: i.join(e.bundleDir, Nt),
1890
- indexPath: i.join(e.bundleDir, s.assets.index ?? "assets/index.ndjson"),
1891
- progress: {
1892
- done: (e) => e in x.assets,
1893
- mark: (e) => (x.assets[e] = !0, G(y, x))
1894
- }
1895
- });
1896
- }
1897
- return yield* n.promise(() => Ee(null)), {
1898
- manifest: s,
1899
- tablesWritten: M,
1900
- rowsWritten: j,
1901
- tablesSkipped: N,
1902
- rowsSkipped: ve,
1903
- fullyResumed: M === 0 && N > 0,
1904
- ...F === void 0 ? {} : { rollback: F }
1905
- };
1906
- }).pipe(n.tapError((e) => n.promise(() => t?.(e?.reason ?? e?._tag ?? "the import did not finish") ?? Promise.resolve())));
1907
- }, cr = (e) => e, lr = (e) => {
1908
- if (e.allowLive || e.liveInstance === null || e.sameTarget === "different") return { refuse: !1 };
1909
- let t = e.targetLabel === void 0 ? "" : ` Target: ${e.targetLabel}.`, n = e.sameTarget === "same" ? " That instance is connected to the SAME database." : " We could not confirm which database that instance is connected to, so this is refused on the conservative reading — an older instance, or one whose connection could not be parsed, reports nothing to compare.";
1910
- return {
1911
- refuse: !0,
1912
- message: `refusing to write into a target with a LIVE instance (${e.liveInstance}).${t}${n} A direct import is an uncoordinated concurrent writer — readers see partial state, the reactive layer either storms or goes stale, and rows race with live writes. Re-run with --allow-live to override, or use a live-safe strategy (shadow-swap for replace, or the api target).`
1913
- };
1914
- }, Y = Buffer.from("VBUNDLE2"), ur = 0, dr = 1, fr = 2, pr = 32, mr = async (e) => {
1915
- let t = [], n = async (r) => {
1916
- let i;
1917
- try {
1918
- i = await ye(I(e, r), { withFileTypes: !0 });
1919
- } catch {
1920
- return;
1921
- }
1922
- for (let e of i) {
1923
- let i = r ? `${r}/${e.name}` : e.name;
1924
- e.isDirectory() ? await n(i) : e.isFile() && t.push(i);
1925
- }
1926
- };
1927
- return await n(""), t.sort();
1928
- }, hr = (e, t) => {
1929
- let n = Buffer.from(e, "utf8"), r = Buffer.alloc(5 + n.length + 8);
1930
- return r.writeUInt8(dr, 0), r.writeUInt32BE(n.length, 1), n.copy(r, 5), r.writeBigUInt64BE(BigInt(t), 5 + n.length), r;
1931
- }, gr = (e, t, n) => {
1932
- let r = Buffer.from(e, "utf8"), i = Buffer.from(t, "utf8"), a = Buffer.alloc(5 + r.length + 2 + i.length + 8), o = 0;
1933
- return a.writeUInt8(fr, o), o += 1, a.writeUInt32BE(r.length, o), o += 4, r.copy(a, o), o += r.length, a.writeUInt16BE(i.length, o), o += 2, i.copy(a, o), o += i.length, a.writeBigUInt64BE(BigInt(n), o), a;
1934
- };
1935
- async function* _r(e) {
1936
- yield new Uint8Array(Y);
1937
- for (let t of await mr(e.dir)) {
1938
- let n = I(e.dir, t), r = await be(n);
1939
- if (yield new Uint8Array(hr(t, r.size)), r.size > 0) for await (let e of ue(n)) yield new Uint8Array(e);
1940
- }
1941
- if (e.blobs) for await (let t of e.blobs.list()) {
1942
- yield new Uint8Array(gr(t.key, t.contentType, t.size));
1943
- let n = w("sha256"), r = 0;
1944
- for await (let i of e.blobs.open(t.key)) {
1945
- let e = Buffer.from(i);
1946
- n.update(e), r += e.length, yield new Uint8Array(e);
1947
- }
1948
- if (r !== t.size) throw Error(`blob '${t.key}' size mismatch: header ${t.size}, streamed ${r}`);
1949
- yield new Uint8Array(n.digest());
1950
- }
1951
- yield new Uint8Array(Buffer.from([ur]));
1952
- }
1953
- var vr = (e, t) => new Promise((n, r) => e.write(t, (e) => e ? r(e) : n())), yr = (e) => new Promise((t) => e.end(t)), br = async (e, t) => {
1954
- let n = L(t.dir);
1955
- await N(n, { recursive: !0 });
1956
- let r = Buffer.alloc(0), i = e[Symbol.asyncIterator](), a = !1, o = async () => {
1957
- let { value: e, done: t } = await i.next();
1958
- return t ? (a = !0, !1) : (r = r.length === 0 ? Buffer.from(e) : Buffer.concat([r, Buffer.from(e)]), !0);
1959
- }, s = async (e) => {
1960
- for (; r.length < e;) if (!await o()) throw Error("truncated bundle archive");
1961
- }, c = (e) => {
1962
- let t = r.subarray(0, e);
1963
- return r = r.subarray(e), t;
1964
- };
1965
- try {
1966
- if (await s(Y.length), !c(Y.length).equals(Y)) throw Error("not a bundle archive (bad magic)");
1967
- for (;;) {
1968
- await s(1);
1969
- let e = c(1).readUInt8(0);
1970
- if (e === ur) return;
1971
- if (e === dr) {
1972
- await s(4);
1973
- let e = c(4).readUInt32BE(0);
1974
- await s(e);
1975
- let t = c(e).toString("utf8");
1976
- await s(8);
1977
- let i = c(8).readBigUInt64BE(0), a = L(n, t);
1978
- if (a !== n && !a.startsWith(n + Se)) throw Error(`archive path '${t}' escapes the destination`);
1979
- await N(xe(a), { recursive: !0 });
1980
- let o = de(a);
1981
- for (; i > 0n;) {
1982
- r.length === 0 && await s(1);
1983
- let e = i < BigInt(r.length) ? Number(i) : r.length;
1984
- await vr(o, c(e)), i -= BigInt(e);
1985
- }
1986
- await yr(o);
1987
- } else if (e === fr) {
1988
- await s(4);
1989
- let e = c(4).readUInt32BE(0);
1990
- await s(e);
1991
- let n = c(e).toString("utf8");
1992
- await s(2);
1993
- let i = c(2).readUInt16BE(0);
1994
- await s(i);
1995
- let a = c(i).toString("utf8");
1996
- await s(8);
1997
- let o = c(8).readBigUInt64BE(0), l = await t.blobs.has(n), u = w("sha256"), d = new ge(), f = l ? Promise.resolve() : t.blobs.write(n, d, {
1998
- contentType: a,
1999
- size: Number(o)
2000
- }), p = o;
2001
- for (; p > 0n;) {
2002
- r.length === 0 && await s(1);
2003
- let e = p < BigInt(r.length) ? Number(p) : r.length, t = c(e);
2004
- u.update(t), l || await vr(d, t), p -= BigInt(e);
2005
- }
2006
- l ? d.destroy() : await yr(d), await f, await s(pr);
2007
- let m = c(pr);
2008
- if (!u.digest().equals(m)) throw Error(`blob '${n}' failed integrity (sha mismatch)`);
2009
- } else throw Error(`unknown bundle entry kind ${e}`);
2010
- }
2011
- } finally {
2012
- a || await Promise.resolve(i.return?.()).catch(() => void 0);
2013
- }
2014
- }, xr = async (e, t) => {
2015
- let n = new Set(t), r = /* @__PURE__ */ new Map(), i = Buffer.alloc(0), a = e[Symbol.asyncIterator](), o = async () => {
2016
- let { value: e, done: t } = await a.next();
2017
- return !t && (i = i.length === 0 ? Buffer.from(e) : Buffer.concat([i, Buffer.from(e)]), !0);
2018
- }, s = async (e) => {
2019
- for (; i.length < e;) if (!await o()) return !1;
2020
- return !0;
2021
- }, c = (e) => {
2022
- let t = i.subarray(0, e);
2023
- return i = i.subarray(e), t;
2024
- }, l = async (e) => {
2025
- let t = [], n = e;
2026
- for (; n > 0n;) {
2027
- if (i.length === 0 && !await s(1)) return null;
2028
- let e = n < BigInt(i.length) ? Number(n) : i.length;
2029
- t.push(c(e)), n -= BigInt(e);
2030
- }
2031
- return Buffer.concat(t);
2032
- };
2033
- if (!await s(Y.length) || !c(Y.length).equals(Y)) throw Error("not a bundle archive (bad magic)");
2034
- for (; await s(1);) {
2035
- let e = c(1).readUInt8(0);
2036
- if (e === ur || e === fr) break;
2037
- if (e !== dr) throw Error(`unknown bundle entry kind ${e}`);
2038
- if (!await s(4)) break;
2039
- let t = c(4).readUInt32BE(0);
2040
- if (!await s(t)) break;
2041
- let i = c(t).toString("utf8");
2042
- if (!await s(8)) break;
2043
- let a = await l(c(8).readBigUInt64BE(0));
2044
- if (a === null || n.has(i) && (r.set(i, a), n.delete(i), n.size === 0)) break;
2045
- }
2046
- return r;
2047
- }, Sr = (e, t) => br(e, {
2048
- dir: t,
2049
- blobs: Cr(t)
2050
- }), Cr = (e) => {
2051
- let t = /* @__PURE__ */ new Set();
2052
- return {
2053
- has: async (e) => t.has(e),
2054
- write: async (n, r, i) => {
2055
- let a = await import("node:fs/promises"), { pipeline: o } = await import("node:stream/promises"), { Transform: s } = await import("node:stream"), c = I(e, "assets");
2056
- await N(c, { recursive: !0 });
2057
- let l = w("sha256"), u = 0, d = new s({ transform(e, t, n) {
2058
- l.update(e), u += e.length, n(null, e);
2059
- } }), f = I(c, `.tmp-${Date.now()}-${t.size}`);
2060
- await o(r, d, de(f));
2061
- let p = l.digest("hex");
2062
- await a.rename(f, I(c, p)), await a.appendFile(I(c, "index.ndjson"), `${JSON.stringify({
2063
- key: n,
2064
- sha256: p,
2065
- size: u,
2066
- contentType: i.contentType
2067
- })}\n`), t.add(n);
2068
- }
2069
- };
2070
- }, wr = Ce(ce), Tr = Buffer.from("VENC1\0\0\0"), Er = 32, Dr = 16, Or = 7, kr = 16, X = 262144, Ar = {
2071
- N: 32768,
2072
- r: 8,
2073
- p: 1
2074
- }, jr = 134217728, Mr = 43, Nr = (e, t, n) => {
2075
- let r = Buffer.alloc(12);
2076
- return e.copy(r, 0, 0, Or), r.writeUInt32BE(t >>> 0, Or), r.writeUInt8(+!!n, 11), r;
2077
- };
2078
- async function* Pr(e, t) {
2079
- if (!t) throw Error("encryptStream: a non-empty passphrase is required");
2080
- let n = E(Dr), r = E(Or), i = await wr(t, n, Er, {
2081
- ...Ar,
2082
- maxmem: jr
2083
- }), a = Buffer.alloc(Mr);
2084
- Tr.copy(a, 0), a.writeUInt32BE(Ar.N, 8), a.writeUInt32BE(Ar.r, 12), a.writeUInt32BE(Ar.p, 16), n.copy(a, 20), r.copy(a, 36), yield new Uint8Array(a);
2085
- let o = 0, s = (e, t) => {
2086
- let n = ae("aes-256-gcm", i, Nr(r, o, t));
2087
- o === 0 && n.setAAD(a);
2088
- let s = Buffer.concat([n.update(e), n.final()]), c = n.getAuthTag();
2089
- o++;
2090
- let l = Buffer.alloc(5 + s.length + kr);
2091
- return l.writeUInt8(+!!t, 0), l.writeUInt32BE(s.length + kr, 1), s.copy(l, 5), c.copy(l, 5 + s.length), new Uint8Array(l);
2092
- }, c = Buffer.alloc(0), l = 0;
2093
- for await (let t of e) {
2094
- let e = Buffer.from(t), n = 0;
2095
- for (; n < e.length;) {
2096
- if (l === X && (yield s(c, !1), l = 0), l === 0 && e.length - n >= X) {
2097
- let t = e.subarray(n, n + X);
2098
- n += X, n < e.length ? yield s(t, !1) : (c = t, l = X);
2099
- continue;
2100
- }
2101
- c.length !== X && (c = Buffer.alloc(X));
2102
- let t = Math.min(X - l, e.length - n);
2103
- e.copy(c, l, n, n + t), l += t, n += t;
2104
- }
2105
- }
2106
- yield s(c.subarray(0, l), !0);
2107
- }
2108
- async function* Fr(e, t) {
2109
- if (!t) throw Error("decryptStream: a non-empty passphrase is required");
2110
- let n = e[Symbol.asyncIterator](), r = Buffer.alloc(0), i = 0, a = !1, o = async (e) => {
2111
- let t, o = 0;
2112
- for (; o < e;) {
2113
- if (i === r.length) {
2114
- if (a) break;
2115
- let e = await n.next();
2116
- if (e.done) {
2117
- a = !0;
2118
- break;
2119
- }
2120
- if (r = Buffer.from(e.value), i = 0, r.length === 0) continue;
2121
- }
2122
- if (o === 0 && r.length - i >= e) {
2123
- let t = r.subarray(i, i + e);
2124
- return i += e, t;
2125
- }
2126
- t ??= Buffer.alloc(e);
2127
- let s = Math.min(e - o, r.length - i);
2128
- r.copy(t, o, i, i + s), i += s, o += s;
2129
- }
2130
- return t?.subarray(0, o) ?? Buffer.alloc(0);
2131
- }, s = !1;
2132
- try {
2133
- let e = await o(Mr);
2134
- if (e.length !== Mr) throw Error("encrypted bundle truncated (no header)");
2135
- if (!e.subarray(0, 8).equals(Tr)) throw Error("not an encrypted bundle (bad magic / wrong passphrase target)");
2136
- let n = e.readUInt32BE(8), r = e.readUInt32BE(12), i = e.readUInt32BE(16), a = Buffer.from(e.subarray(20, 36)), s = Buffer.from(e.subarray(36, Mr)), c = await wr(t, a, Er, {
2137
- N: n,
2138
- r,
2139
- p: i,
2140
- maxmem: jr
2141
- }), l = 0, u = !1;
2142
- for (;;) {
2143
- let t = await o(5);
2144
- if (t.length !== 5) {
2145
- if (t.length === 0 && u) return;
2146
- throw Error("encrypted bundle truncated (no authenticated final frame)");
2147
- }
2148
- if (u) throw Error("encrypted bundle has trailing data after the final frame");
2149
- let n = t.readUInt8(0);
2150
- if (n !== 0 && n !== 1) throw Error("encrypted bundle corrupt (invalid final flag)");
2151
- let r = n === 1, i = t.readUInt32BE(1);
2152
- if (i < kr) throw Error("encrypted bundle corrupt (short frame)");
2153
- if (i > 262160) throw Error("encrypted bundle corrupt (oversized frame)");
2154
- let a = await o(i);
2155
- if (a.length !== i) throw Error("encrypted bundle truncated (frame body)");
2156
- let d = a.subarray(0, i - kr), f = a.subarray(i - kr), p = oe("aes-256-gcm", c, Nr(s, l, r));
2157
- l === 0 && p.setAAD(e), p.setAuthTag(f);
2158
- let m = Buffer.concat([p.update(d), p.final()]);
2159
- l++, r && (u = !0), m.length > 0 && (yield new Uint8Array(m));
2160
- }
2161
- } catch (e) {
2162
- throw s = !0, e;
2163
- } finally {
2164
- if (!a) try {
2165
- await n.return?.();
2166
- } catch (e) {
2167
- if (!s) throw e;
2168
- }
2169
- }
2170
- }
2171
- //#endregion
2172
- //#region src/blobStorage.ts
2173
- var Ir = (e, t) => ({
2174
- list: () => {
2175
- let e = t();
2176
- return { async *[Symbol.asyncIterator]() {
2177
- let t = await n.runPromise(c.runCollect(e));
2178
- for (let e of t) yield {
2179
- key: e.key,
2180
- contentType: e.contentType,
2181
- size: e.size
2182
- };
2183
- } };
2184
- },
2185
- open: (t) => ({ async *[Symbol.asyncIterator]() {
2186
- let r = await n.runPromise(A(e, t)), i = await j(r.stream);
2187
- for await (let e of i) yield e instanceof Uint8Array ? e : new Uint8Array(e);
2188
- } })
2189
- }), Lr = (e, t) => Ir(e, () => C(t, {
2190
- table: pe,
2191
- chunkSize: 500
2192
- }).pipe(c.map((e) => ({
2193
- key: String(e.key),
2194
- contentType: String(e.contentType ?? "application/octet-stream"),
2195
- size: Number(e.size ?? 0)
2196
- })))), Rr = (e) => ({
2197
- has: (t) => n.runPromise(e.head(t).pipe(n.map((e) => e !== null))),
2198
- write: async (t, r, i) => {
2199
- let a = me("storage", () => _e.from(r));
2200
- await n.runPromise(he(e, t, a, {
2201
- contentType: i.contentType,
2202
- totalSize: i.size
2203
- }));
2204
- }
2205
- }), zr = (e, t) => {
2206
- let n = t === "dump" ? "mariadb-dump" : "mariadb", r = t === "dump" ? "mysqldump" : "mysql";
2207
- return e === "mariadb" ? Br(n) ? { tool: n } : {
2208
- tool: r,
2209
- hint: `'${n}' is not on PATH, so this ran '${r}' instead. If that binary came from Oracle's MySQL client it queries information_schema.COLUMN_STATISTICS, which MariaDB does not have, and the run dies with error 1109 after the first table. Install the MariaDB client package (which ships '${n}') and re-run. Do NOT reach for --column-statistics=0: that flag does not exist on '${n}', so it fixes the wrong client and breaks the right one. Note: a MariaDB 12.x client requires TLS by default, so running '${n}' BY HAND against a server without it fails with "TLS/SSL error: SSL is required" (2026) and needs --skip-ssl. This command is not affected.`
2210
- } : { tool: r };
2211
- }, Br = (e) => (process.env.PATH ?? "").split(F).filter((e) => e !== "").some((t) => fe(I(t, e))), Vr = (e) => {
2212
- switch (e) {
2213
- case "postgres": return "db.dump";
2214
- case "mysql":
2215
- case "mariadb": return "db.sql";
2216
- case "sqlite":
2217
- case "turso": return "db.sqlite";
2218
- case "mssql": return "db.bacpac";
2219
- default: return "db.dump";
2220
- }
2221
- }, Hr = (e) => e.filename ?? e.database ?? ((e.url ?? "").replace(/^file:/, "") || "app.sqlite"), Z = (e, t = 3306) => {
2222
- if (e.host || e.database) return {
2223
- host: e.host ?? "localhost",
2224
- port: e.port ?? t,
2225
- username: e.username ?? "root",
2226
- password: e.password ?? "",
2227
- database: e.database ?? ""
2228
- };
2229
- let n = new URL(e.url ?? "");
2230
- return {
2231
- host: n.hostname || "localhost",
2232
- port: e.port ?? (n.port ? Number(n.port) : t),
2233
- username: decodeURIComponent(n.username) || "root",
2234
- password: decodeURIComponent(n.password) || "",
2235
- database: n.pathname.replace(/^\//, "") || ""
2236
- };
2237
- }, Ur = (e) => `Server=${e.host},${e.port};Database=${e.database};User ID=${e.username};Password=${e.password};TrustServerCertificate=True`, Wr = (e, t, n) => {
2238
- switch (e) {
2239
- case "postgres": {
2240
- if (t.url) return {
2241
- kind: "spawn",
2242
- tool: "pg_dump",
2243
- args: [
2244
- "--format=custom",
2245
- "--no-owner",
2246
- "--no-privileges",
2247
- ...Kr,
2248
- "--dbname",
2249
- t.url,
2250
- "--file",
2251
- n
2252
- ],
2253
- env: {}
2254
- };
2255
- let e = Z(t, 5432);
2256
- return {
2257
- kind: "spawn",
2258
- tool: "pg_dump",
2259
- args: [
2260
- "--format=custom",
2261
- "--no-owner",
2262
- "--no-privileges",
2263
- ...Kr,
2264
- "-h",
2265
- e.host,
2266
- "-p",
2267
- String(e.port),
2268
- "-U",
2269
- e.username,
2270
- "-d",
2271
- e.database,
2272
- "--file",
2273
- n
2274
- ],
2275
- env: { PGPASSWORD: e.password }
2276
- };
2277
- }
2278
- case "mysql":
2279
- case "mariadb": {
2280
- let r = Z(t);
2281
- return {
2282
- kind: "spawn",
2283
- ...zr(e, "dump"),
2284
- args: [
2285
- "--single-transaction",
2286
- "--routines",
2287
- "--triggers",
2288
- "--no-tablespaces",
2289
- "--protocol=TCP",
2290
- ...Gr.map((e) => `--ignore-table=${r.database}.${e}`),
2291
- "-h",
2292
- r.host,
2293
- "-P",
2294
- String(r.port),
2295
- "-u",
2296
- r.username,
2297
- r.database
2298
- ],
2299
- env: { MYSQL_PWD: r.password },
2300
- stdoutFile: n
2301
- };
2302
- }
2303
- case "sqlite":
2304
- case "turso": return {
2305
- kind: "copy",
2306
- from: Hr(t),
2307
- to: n
2308
- };
2309
- case "mssql": return {
2310
- kind: "spawn",
2311
- tool: "sqlpackage",
2312
- args: [
2313
- "/Action:Export",
2314
- `/SourceConnectionString:${Ur(Z(t, 1433))}`,
2315
- `/TargetFile:${n}`
2316
- ],
2317
- env: {}
2318
- };
2319
- default: throw new U({
2320
- tool: "backup",
2321
- reason: `no native backup for dialect '${e}'`
2322
- });
2323
- }
2324
- }, Gr = [u, l], Kr = Gr.flatMap((e) => ["--exclude-table", e]), qr = (e) => e === "postgres" || e === "mysql" || e === "mariadb" ? Gr : [], Jr = (e, t, n) => {
2325
- switch (e) {
2326
- case "postgres": {
2327
- if (t.url) return {
2328
- kind: "spawn",
2329
- tool: "pg_restore",
2330
- args: [
2331
- "--clean",
2332
- "--if-exists",
2333
- "--no-owner",
2334
- "--dbname",
2335
- t.url,
2336
- n
2337
- ],
2338
- env: {}
2339
- };
2340
- let e = Z(t, 5432);
2341
- return {
2342
- kind: "spawn",
2343
- tool: "pg_restore",
2344
- args: [
2345
- "--clean",
2346
- "--if-exists",
2347
- "--no-owner",
2348
- "-h",
2349
- e.host,
2350
- "-p",
2351
- String(e.port),
2352
- "-U",
2353
- e.username,
2354
- "-d",
2355
- e.database,
2356
- n
2357
- ],
2358
- env: { PGPASSWORD: e.password }
2359
- };
2360
- }
2361
- case "mysql":
2362
- case "mariadb": {
2363
- let r = Z(t);
2364
- return {
2365
- kind: "spawn",
2366
- ...zr(e, "client"),
2367
- args: [
2368
- "--protocol=TCP",
2369
- "-h",
2370
- r.host,
2371
- "-P",
2372
- String(r.port),
2373
- "-u",
2374
- r.username,
2375
- r.database
2376
- ],
2377
- env: { MYSQL_PWD: r.password },
2378
- stdinFile: n
2379
- };
2380
- }
2381
- case "sqlite":
2382
- case "turso": return {
2383
- kind: "copy",
2384
- from: n,
2385
- to: Hr(t)
2386
- };
2387
- case "mssql": {
2388
- let e = Z(t, 1433), r = Xr(e.database);
2389
- return {
2390
- kind: "mssql-replace",
2391
- load: {
2392
- kind: "spawn",
2393
- tool: "sqlpackage",
2394
- args: [
2395
- "/Action:Import",
2396
- `/TargetConnectionString:${Ur({
2397
- ...e,
2398
- database: r
2399
- })}`,
2400
- `/SourceFile:${n}`
2401
- ],
2402
- env: {}
2403
- },
2404
- server: {
2405
- host: e.host,
2406
- port: e.port,
2407
- username: e.username,
2408
- password: e.password,
2409
- database: "master"
2410
- },
2411
- live: e.database,
2412
- scratch: r,
2413
- carry: Gr
2414
- };
2415
- }
2416
- default: throw new U({
2417
- tool: "restore",
2418
- reason: `no native restore for dialect '${e}'`
2419
- });
2420
- }
2421
- }, Q = (e) => `[${e.replaceAll("]", "]]")}]`, $ = (e) => `N'${e.replaceAll("'", "''")}'`, Yr = "__voltro_restore_", Xr = (e, t = se().slice(0, 8)) => `${e}${Yr}${t}`, Zr = (e) => e.replace(Yr, "__voltro_replaced_"), Qr = (e) => `IF DB_ID(${$(e)}) IS NOT NULL BEGIN ALTER DATABASE ${Q(e)} SET SINGLE_USER WITH ROLLBACK IMMEDIATE; DROP DATABASE ${Q(e)} END`, $r = (e) => `DECLARE @leftover sysname, @drop nvarchar(max);
2422
- DECLARE leftovers CURSOR LOCAL FAST_FORWARD FOR SELECT name FROM sys.databases WHERE name LIKE ${$(`${`${e}${Yr}`.replace(/[\\%_[]/g, (e) => `\\${e}`)}%`)} ESCAPE N'\\';
2423
- OPEN leftovers; FETCH NEXT FROM leftovers INTO @leftover;
2424
- WHILE @@FETCH_STATUS = 0 BEGIN
2425
- SET @drop = N'ALTER DATABASE ' + QUOTENAME(@leftover) + N' SET SINGLE_USER WITH ROLLBACK IMMEDIATE; DROP DATABASE ' + QUOTENAME(@leftover);
2426
- EXEC(@drop);
2427
- FETCH NEXT FROM leftovers INTO @leftover;
2428
- END`, ei = (e) => {
2429
- let t = Q(e.live), n = Q(e.scratch), r = Zr(e.scratch);
2430
- return {
2431
- prepare: [$r(e.live)],
2432
- carry: e.carry.map((e) => {
2433
- let r = $(`${n}.dbo.${Q(e)}`), i = $(`${t}.dbo.${Q(e)}`);
2434
- return `IF OBJECT_ID(${r}) IS NOT NULL BEGIN
2435
- DELETE FROM ${n}.dbo.${Q(e)};
2436
- IF OBJECT_ID(${i}) IS NOT NULL BEGIN
2437
- DECLARE @columns nvarchar(max) = (SELECT STRING_AGG(QUOTENAME(s.name), ',') FROM ${n}.sys.columns s
2438
- JOIN ${t}.sys.columns l ON l.name = s.name AND l.object_id = OBJECT_ID(${i})
2439
- WHERE s.object_id = OBJECT_ID(${r}) AND s.is_computed = 0);
2440
- EXEC(N'INSERT INTO ${n.replaceAll("'", "''")}.dbo.${Q(e).replaceAll("'", "''")} (' + @columns + N') SELECT ' + @columns + N' FROM ${t.replaceAll("'", "''")}.dbo.${Q(e).replaceAll("'", "''")}');
2441
- END
2442
- END`;
2443
- }),
2444
- swap: [
2445
- `ALTER DATABASE ${t} SET SINGLE_USER WITH ROLLBACK IMMEDIATE`,
2446
- `ALTER DATABASE ${t} MODIFY NAME = ${Q(r)}`,
2447
- `ALTER DATABASE ${n} MODIFY NAME = ${t}`,
2448
- Qr(r)
2449
- ],
2450
- undoSwap: [`IF DB_ID(${$(e.live)}) IS NULL AND DB_ID(${$(r)}) IS NOT NULL ALTER DATABASE ${Q(r)} MODIFY NAME = ${t}`, `IF DB_ID(${$(e.live)}) IS NOT NULL ALTER DATABASE ${t} SET MULTI_USER`],
2451
- cleanup: [Qr(e.scratch)]
2452
- };
2453
- }, ti = (e) => {
2454
- let t = [];
2455
- for (let n = e, r = 0; n != null && r < 4; r++) {
2456
- let e = n instanceof Error ? n.message : typeof n == "object" && "message" in n ? String(n.message) : String(n);
2457
- e && !t.includes(e) && t.push(e), n = typeof n == "object" && "cause" in n ? n.cause : void 0;
2458
- }
2459
- return t.join(": ");
2460
- }, ni = (e, t) => {
2461
- if (t === void 0) return n.fail(new U({
2462
- tool: "sqlpackage",
2463
- reason: "an mssql restore swaps databases and needs a SQL connection to the server; none was provided"
2464
- }));
2465
- let r = ei(e), i = (r, i) => t(e.server, i).pipe(n.mapError((e) => new U({
2466
- tool: "sqlpackage",
2467
- reason: `${r}: ${ti(e)}`
2468
- }))), a = i("dropping the scratch database", r.cleanup).pipe(n.ignore);
2469
- return n.gen(function* () {
2470
- yield* i(`preparing ${e.scratch}`, r.prepare), yield* ri(e.load).pipe(n.tapError(() => a)), yield* i(`carrying ${e.carry.join(", ")} into the imported copy`, r.carry).pipe(n.tapError(() => a)), yield* i(`swapping ${e.scratch} in as ${e.live}`, r.swap).pipe(n.tapError(() => i("putting the original database back", r.undoSwap).pipe(n.ignore)));
2471
- });
2472
- }, ri = (e, t) => e.kind === "mssql-replace" ? ni(e, t) : e.kind === "copy" ? n.callback((t) => {
2473
- let r = `${e.to}.restoring-${se()}`, i = (e) => {
2474
- try {
2475
- k(r);
2476
- } catch {}
2477
- t(n.fail(new U({
2478
- tool: "copy",
2479
- reason: e
2480
- })));
2481
- };
2482
- le(e.from, r, (a) => {
2483
- if (a) {
2484
- i(`copy ${e.from} → ${e.to}: ${a.message}`);
2485
- return;
2486
- }
2487
- for (let t of [
2488
- `${e.to}-wal`,
2489
- `${e.to}-shm`,
2490
- `${e.to}-journal`
2491
- ]) try {
2492
- k(t);
2493
- } catch {}
2494
- D(r, e.to, (r) => {
2495
- if (r) {
2496
- i(`copy ${e.from} → ${e.to}: ${r.message}`);
2497
- return;
2498
- }
2499
- t(n.void);
2500
- });
2501
- });
2502
- }) : n.callback((t) => {
2503
- let r = we(e.tool, [...e.args], {
2504
- env: {
2505
- ...process.env,
2506
- ...e.env
2507
- },
2508
- stdio: [
2509
- "pipe",
2510
- e.stdoutFile ? "pipe" : "inherit",
2511
- "pipe"
2512
- ]
2513
- }), i = "";
2514
- r.stderr?.on("data", (e) => {
2515
- i += String(e);
2516
- });
2517
- let a = !1, o = (e) => {
2518
- a || (a = !0, t(e));
2519
- }, s, c = (e) => {
2520
- s ??= e;
2521
- };
2522
- if (e.stdoutFile && r.stdout) {
2523
- let t = de(e.stdoutFile);
2524
- t.on("error", (t) => c(`writing ${P(e.stdoutFile)}: ${t.message}`)), r.stdout.on("error", (e) => c(`reading the tool's output: ${e.message}`)), r.stdout.pipe(t);
2525
- }
2526
- let l = 0;
2527
- if (e.stdinFile && r.stdin) {
2528
- let t = ue(e.stdinFile);
2529
- t.on("data", (e) => {
2530
- l += e.length;
2531
- }), t.on("error", (t) => {
2532
- c(`reading ${P(e.stdinFile)}: ${t.message}`), r.stdin?.end();
2533
- }), r.stdin.on("error", (t) => {
2534
- t.code !== "EPIPE" && c(`feeding ${P(e.stdinFile)} to ${e.tool}: ${t.message}`);
2535
- }), t.pipe(r.stdin);
2536
- }
2537
- let u = () => {
2538
- if (e.stdoutFile !== void 0) try {
2539
- k(e.stdoutFile);
2540
- } catch {}
2541
- };
2542
- r.on("error", (t) => {
2543
- u(), o(n.fail(new U({
2544
- tool: e.tool,
2545
- reason: `failed to launch (is '${e.tool}' installed + on PATH?): ${t.message}`,
2546
- ...e.hint === void 0 ? {} : { hint: e.hint }
2547
- })));
2548
- }), r.on("close", (t) => {
2549
- if (t === 0 && s === void 0) {
2550
- o(n.void);
2551
- return;
2552
- }
2553
- u();
2554
- let r = e.stdinFile === void 0 ? "" : ` — ${l} of ${ii(e.stdinFile)} byte(s) of ${P(e.stdinFile)} had been fed to it`;
2555
- o(n.fail(new U({
2556
- tool: e.tool,
2557
- reason: (t === 0 ? `exited cleanly, but ${s}` : `exited with code ${t}${s === void 0 ? "" : ` (${s})`}`) + r + (e.stdoutFile === void 0 ? "" : ` — the partial ${P(e.stdoutFile)} was removed, so it cannot be mistaken for a backup`),
2558
- stderr: i.trim() === "" ? `(${e.tool} printed nothing to stderr)` : i.slice(-2e3),
2559
- ...e.hint === void 0 ? {} : { hint: e.hint }
2560
- })));
2561
- });
2562
- }), ii = (e) => {
2563
- try {
2564
- return String(O(e).size);
2565
- } catch {
2566
- return "?";
2567
- }
2568
- }, ai = (e) => typeof e == "object" && e ? "fake" in e ? `fake:${e.fake}` : "custom" : String(e), oi = (e) => n.gen(function* () {
2569
- let t = yield* ct(e.scope ?? { kind: "all" }, e.snapshot, {
2570
- store: e.store,
2571
- ...e.tenantTables ? { tenantTables: e.tenantTables } : {}
2572
- }), r = on(t.tables, e.snapshot), i = e.classification ?? De(e.snapshot), a = e.sampleSize ?? 20, o = Ve(r.map((t) => ({
2573
- name: t,
2574
- columns: (e.snapshot.tables.find((e) => e.name === t)?.columns ?? []).map((e) => ({
2575
- name: e.name,
2576
- type: e.type
2577
- }))
2578
- })), i, e.masking), s = [], l = [];
2579
- for (let i of r) {
2580
- let r = t.predicateFor(i), u = yield* C(e.store, {
2581
- table: i,
2582
- chunkSize: a,
2583
- ...r === void 0 ? {} : { where: r }
2584
- }).pipe(c.take(a), c.runCollect, n.map((e) => [...e])), d = u.map((t) => We(t, i, o.actions, e.masking.seed));
2585
- l.push(...Ue(d, i, o.actions));
2586
- let f = u[0], p = d[0], m = [];
2587
- if (f && p) for (let [e, t] of Object.entries(o.actions)) {
2588
- if (!e.startsWith(`${i}.`) || t === "keep") continue;
2589
- let n = e.slice(i.length + 1);
2590
- m.push({
2591
- column: n,
2592
- action: ai(t),
2593
- before: f[n],
2594
- after: p[n]
2595
- });
2596
- }
2597
- s.push({
2598
- table: i,
2599
- samples: m
2600
- });
2601
- }
2602
- return {
2603
- tables: s,
2604
- leaks: l,
2605
- unclassified: o.unclassified
2606
- };
2607
- }), si = (e, t) => {
2608
- let n = new Map(t.map((e) => [e.name, e])), r = [], i = {};
2609
- for (let t of e) {
2610
- let e = n.get(t.name);
2611
- if (e === void 0) {
2612
- r.push({
2613
- verdict: "refuse",
2614
- table: t.name,
2615
- detail: `the bundle carries '${t.name}' and the target schema has no such table — nowhere to put ${t.name}'s rows`
2616
- });
2617
- continue;
2618
- }
2619
- let a = new Map(e.columns.map((e) => [e.name, e])), o = new Map(t.columns.map((e) => [e.name, e])), s = [];
2620
- for (let e of t.columns) {
2621
- let n = a.get(e.name);
2622
- if (n === void 0) {
2623
- s.push(e.name), r.push({
2624
- verdict: "value-dropped",
2625
- table: t.name,
2626
- column: e.name,
2627
- detail: `'${t.name}.${e.name}' is in the bundle and not in the schema — its values are DISCARDED`
2628
- });
2629
- continue;
2630
- }
2631
- n.type !== e.type && r.push({
2632
- verdict: "refuse",
2633
- table: t.name,
2634
- column: e.name,
2635
- detail: `'${t.name}.${e.name}' changed type (${e.type} → ${n.type}) — the bundle's values would be written through a codec that no longer matches them`
2636
- });
2637
- }
2638
- for (let n of e.columns) if (!o.has(n.name)) {
2639
- if (n.nullable || n.hasDefault) {
2640
- r.push({
2641
- verdict: "filled-by-default",
2642
- table: t.name,
2643
- column: n.name,
2644
- detail: `'${t.name}.${n.name}' is in the schema and not in the bundle — every imported row gets ${n.hasDefault ? "its default" : "NULL"}`
2645
- });
2646
- continue;
2647
- }
2648
- r.push({
2649
- verdict: "refuse",
2650
- table: t.name,
2651
- column: n.name,
2652
- detail: `'${t.name}.${n.name}' is NOT NULL with no default and the bundle has no value for it`
2653
- });
2654
- }
2655
- s.length > 0 && (i[t.name] = s);
2656
- }
2657
- return {
2658
- findings: r,
2659
- skipColumns: i,
2660
- refuse: r.some((e) => e.verdict === "refuse")
2661
- };
2662
- }, ci = (e) => {
2663
- if (e.findings.length === 0) return [" schema matches the bundle"];
2664
- let t = {
2665
- safe: "✓",
2666
- "value-dropped": "⊖",
2667
- "filled-by-default": "⊕",
2668
- refuse: "✗"
2669
- }, n = e.findings.map((e) => ` ${t[e.verdict]} ${e.detail}`), r = (t) => e.findings.filter((e) => e.verdict === t).length;
2670
- return n.push(""), n.push(` discarded: ${r("value-dropped")} defaulted: ${r("filled-by-default")} refused: ${r("refuse")}`), e.refuse && (n.push(""), n.push(" REFUSED — no row was written. These are the differences that would make"), n.push(" imported rows WRONG rather than merely incomplete.")), n;
2671
- };
2672
- //#endregion
2673
- export { Nt as ASSETS_DIR, Pt as ASSET_INDEX_FILE, H as BundleError, Ye as CodecError, Ze as CompressionError, rt as CrossDialectError, Mt as DATA_DIR, Ee as DEFAULT_CLASS_ACTIONS, Ge as ENVIRONMENT_LOCAL_TABLES, Ke as ENVIRONMENT_PORTABLE_TABLES, Gr as EXCLUDED_FROM_DUMP, Ct as FORMAT_VERSION, nt as ImportModeError, Xe as IntegrityError, Ft as LEDGER_FILE, At as MANIFEST_FILE, tt as MaskingError, U as NativeToolError, $e as RowsRefusedError, nr as SAVEPOINT_BATCH_SIZE, Mn as STAGING_PREFIX, et as SchemaDriftError, Qe as ScopeError, B as applyAction, ir as asImportError, lr as assessLiveImport, Gt as assetDone, Vr as backupArtifactName, Wr as backupCommand, Nn as canStage, bn as canonicalType, ht as captureAssetsToCas, Cn as checkPortability, $n as checkSchemaDrift, De as classificationFromSnapshot, si as classifyImportDrift, bt as compressionExt, at as computeSubsetIds, Jn as decideStaging, kt as decodeManifest, tn as decodeRow, $t as decodeValue, Fr as decryptStream, cr as defineDataProfile, Qn as diffSnapshots, Cr as dirBlobSink, Ln as dropStagingSql, qr as dumpExcludedTables, Ht as emptyLedger, Ot as encodeManifest, en as encodeRow, Zt as encodeValue, Pr as encryptStream, Je as environmentLocalNotice, qe as environmentLocalReason, Un as execStatements, ci as formatImportDrift, wn as formatIssue, vt as hasZstd, V as isEnvironmentLocal, xt as makeCompressor, St as makeDecompressor, We as maskRow, ei as mssqlReplaceStatements, Xr as mssqlScratchName, an as ndjsonToRows, pt as noAssetProgress, yn as outsideReferenceRefusal, vn as outsideReferencesInto, _r as packBundle, xr as peekBundle, yt as pickCompression, Ve as planMasking, oi as previewMasking, gt as readAssetIndex, Ut as readLedger, Oe as resolveAction, ct as resolveScope, ft as restoreAssetFromCas, _t as restoreAssetsFromCas, Jr as restoreCommand, rn as rowsToNdjson, _n as runExport, sr as runImport, ri as runNativeStep, Ue as scanForLeaks, q as stagingNameFor, ut as storageAssetSink, lt as storageAssetSource, Rr as storageBlobSink, Ir as storageBlobSource, Lr as storageRefsBlobSource, ot as subsetToScope, Wt as tableDone, on as topoSortTables, br as unpackBundle, Sr as unpackBundleToDir, dt as writeAssetToCas, G as writeLedger };
1
+ import { DEFAULT_CLASS_ACTIONS as e, applyAction as t, classificationFromSnapshot as n, maskRow as r, planMasking as i, resolveAction as a, scanForLeaks as o } from "./masking.js";
2
+ import { ENVIRONMENT_LOCAL_TABLES as s, ENVIRONMENT_PORTABLE_TABLES as c, environmentLocalNotice as l, environmentLocalReason as u, isEnvironmentLocal as d } from "./environmentLocal.js";
3
+ import { BundleError as f, CodecError as p, CompressionError as m, CrossDialectError as h, ImportModeError as g, IntegrityError as _, MaskingError as v, NativeToolError as y, RowsRefusedError as b, SchemaDriftError as x, ScopeError as S } from "./errors.js";
4
+ import { computeSubsetIds as C, subsetToScope as w } from "./subset.js";
5
+ import { resolveScope as T } from "./scope.js";
6
+ import { restoreAssetFromCas as E, storageAssetSink as D, storageAssetSource as O, writeAssetToCas as k } from "./assets.js";
7
+ import { captureAssetsToCas as A, noAssetProgress as j, readAssetIndex as M, restoreAssetsFromCas as N } from "./casAssetPhase.js";
8
+ import { compressionExt as P, hasZstd as F, makeCompressor as I, makeDecompressor as L, pickCompression as R } from "./compress.js";
9
+ import { ASSETS_DIR as z, ASSET_INDEX_FILE as B, DATA_DIR as V, FORMAT_VERSION as H, LEDGER_FILE as U, MANIFEST_FILE as W, decodeManifest as G, encodeManifest as K } from "./manifest.js";
10
+ import { assetDone as q, emptyLedger as J, readLedger as Y, tableDone as X, writeLedger as Z } from "./ledger.js";
11
+ import { decodeRow as Q, decodeValue as $, encodeRow as ee, encodeValue as te } from "./typedCodec.js";
12
+ import { ndjsonToRows as ne, rowsToNdjson as re } from "./ndjson.js";
13
+ import { topoSortTables as ie } from "./topo.js";
14
+ import { runExport as ae } from "./exporter.js";
15
+ import { outsideReferenceRefusal as oe, outsideReferencesInto as se } from "./replacePreflight.js";
16
+ import { canonicalType as ce, checkPortability as le, formatIssue as ue } from "./crossDialect.js";
17
+ import { STAGING_PREFIX as de, canStage as fe, decideStaging as pe, dropStagingSql as me, execStatements as he, stagingNameFor as ge } from "./stagedSwap.js";
18
+ import { checkSchemaDrift as _e, diffSnapshots as ve } from "./schemaDrift.js";
19
+ import { SAVEPOINT_BATCH_SIZE as ye, asImportError as be, runImport as xe } from "./importer.js";
20
+ import { defineDataProfile as Se } from "./profile.js";
21
+ import { assessLiveImport as Ce } from "./liveGuard.js";
22
+ import { dirBlobSink as we, packBundle as Te, peekBundle as Ee, unpackBundle as De, unpackBundleToDir as Oe } from "./bundleArchive.js";
23
+ import { decryptStream as ke, encryptStream as Ae } from "./bundleCrypto.js";
24
+ import { storageBlobSink as je, storageBlobSource as Me, storageRefsBlobSource as Ne } from "./blobStorage.js";
25
+ import { EXCLUDED_FROM_DUMP as Pe, backupArtifactName as Fe, backupCommand as Ie, dumpExcludedTables as Le, mssqlReplaceStatements as Re, mssqlScratchName as ze, restoreCommand as Be, runNativeStep as Ve } from "./nativeDump.js";
26
+ import { previewMasking as He } from "./maskingPreview.js";
27
+ import { classifyImportDrift as Ue, formatImportDrift as We } from "./driftClassification.js";
28
+ export { z as ASSETS_DIR, B as ASSET_INDEX_FILE, f as BundleError, p as CodecError, m as CompressionError, h as CrossDialectError, V as DATA_DIR, e as DEFAULT_CLASS_ACTIONS, s as ENVIRONMENT_LOCAL_TABLES, c as ENVIRONMENT_PORTABLE_TABLES, Pe as EXCLUDED_FROM_DUMP, H as FORMAT_VERSION, g as ImportModeError, _ as IntegrityError, U as LEDGER_FILE, W as MANIFEST_FILE, v as MaskingError, y as NativeToolError, b as RowsRefusedError, ye as SAVEPOINT_BATCH_SIZE, de as STAGING_PREFIX, x as SchemaDriftError, S as ScopeError, t as applyAction, be as asImportError, Ce as assessLiveImport, q as assetDone, Fe as backupArtifactName, Ie as backupCommand, fe as canStage, ce as canonicalType, A as captureAssetsToCas, le as checkPortability, _e as checkSchemaDrift, n as classificationFromSnapshot, Ue as classifyImportDrift, P as compressionExt, C as computeSubsetIds, pe as decideStaging, G as decodeManifest, Q as decodeRow, $ as decodeValue, ke as decryptStream, Se as defineDataProfile, ve as diffSnapshots, we as dirBlobSink, me as dropStagingSql, Le as dumpExcludedTables, J as emptyLedger, K as encodeManifest, ee as encodeRow, te as encodeValue, Ae as encryptStream, l as environmentLocalNotice, u as environmentLocalReason, he as execStatements, We as formatImportDrift, ue as formatIssue, F as hasZstd, d as isEnvironmentLocal, I as makeCompressor, L as makeDecompressor, r as maskRow, Re as mssqlReplaceStatements, ze as mssqlScratchName, ne as ndjsonToRows, j as noAssetProgress, oe as outsideReferenceRefusal, se as outsideReferencesInto, Te as packBundle, Ee as peekBundle, R as pickCompression, i as planMasking, He as previewMasking, M as readAssetIndex, Y as readLedger, a as resolveAction, T as resolveScope, E as restoreAssetFromCas, N as restoreAssetsFromCas, Be as restoreCommand, re as rowsToNdjson, ae as runExport, xe as runImport, Ve as runNativeStep, o as scanForLeaks, ge as stagingNameFor, D as storageAssetSink, O as storageAssetSource, je as storageBlobSink, Me as storageBlobSource, Ne as storageRefsBlobSource, w as subsetToScope, X as tableDone, ie as topoSortTables, De as unpackBundle, Oe as unpackBundleToDir, k as writeAssetToCas, Z as writeLedger };