@voltro/data-transfer 0.49.0 → 0.50.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +114 -0
- package/dist/index.d.ts +90 -0
- package/dist/index.js +502 -415
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { Cause as e, Data as t, Effect as n, Exit as r, Metric as i, MetricBoundaries as a, Runtime as o, Schedule as s, Schema as c, Stream as l } from "effect";
|
|
2
2
|
import { DATA_TRANSFERS_TABLE as u, REPLACE_IN_PROGRESS_TABLE 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 C, streamTable as w, suspendWriteRecorders as ee, suspendWriteRecordersEffect as te, unregisterTable as ne } from "@voltro/database";
|
|
3
3
|
import { fingerprintSchema as re } from "@voltro/database/sql";
|
|
4
|
-
import { createCipheriv as T, createDecipheriv as
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import {
|
|
9
|
-
import { SqlClient as
|
|
10
|
-
import { mkdir as
|
|
11
|
-
import { basename as
|
|
12
|
-
import { promisify as
|
|
13
|
-
import { spawn as
|
|
4
|
+
import { createCipheriv as T, createDecipheriv as E, createHash as D, createHmac as ie, randomBytes as ae, randomUUID as oe, scrypt as se } from "node:crypto";
|
|
5
|
+
import { copyFile as ce, createReadStream as le, createWriteStream as O, existsSync as ue, rename as de, statSync as fe, unlinkSync as k } from "node:fs";
|
|
6
|
+
import { STORAGE_REFS_TABLE as pe, fromNodeReadable as me, getObjectStream as A, putObjectStream as j, toNodeReadable as he } from "@voltro/plugin-storage";
|
|
7
|
+
import * as M from "node:zlib";
|
|
8
|
+
import { PassThrough as N, Readable as ge } from "node:stream";
|
|
9
|
+
import { SqlClient as _e } from "@effect/sql";
|
|
10
|
+
import { mkdir as P, readdir as F, stat as ve } from "node:fs/promises";
|
|
11
|
+
import { basename as I, delimiter as L, dirname as ye, join as R, resolve as be, sep as xe } from "node:path";
|
|
12
|
+
import { promisify as Se } from "node:util";
|
|
13
|
+
import { spawn as Ce } from "node:child_process";
|
|
14
14
|
//#region src/consistency.ts
|
|
15
|
-
var
|
|
15
|
+
var we = (e) => {
|
|
16
16
|
switch (e) {
|
|
17
17
|
case "postgres": return ["SET TRANSACTION ISOLATION LEVEL REPEATABLE READ"];
|
|
18
18
|
case "mysql":
|
|
@@ -22,7 +22,7 @@ var xe = (e) => {
|
|
|
22
22
|
case "turso": return [];
|
|
23
23
|
default: return [];
|
|
24
24
|
}
|
|
25
|
-
},
|
|
25
|
+
}, Te = {
|
|
26
26
|
email: { fake: "email" },
|
|
27
27
|
fullName: { fake: "fullName" },
|
|
28
28
|
firstName: { fake: "firstName" },
|
|
@@ -37,7 +37,7 @@ var xe = (e) => {
|
|
|
37
37
|
date: "dateShift",
|
|
38
38
|
secret: "null",
|
|
39
39
|
freeText: "redact"
|
|
40
|
-
},
|
|
40
|
+
}, Ee = (e) => {
|
|
41
41
|
let t = {};
|
|
42
42
|
for (let n of e.tables) {
|
|
43
43
|
let e = {};
|
|
@@ -49,15 +49,15 @@ var xe = (e) => {
|
|
|
49
49
|
t[n.name] = e;
|
|
50
50
|
}
|
|
51
51
|
return t;
|
|
52
|
-
},
|
|
52
|
+
}, De = (e, t, n, r) => {
|
|
53
53
|
let i = r.columns?.[`${e}.${t}`];
|
|
54
54
|
if (i) return i;
|
|
55
55
|
if (n?.sensitive) {
|
|
56
56
|
let e = n.sensitive.class;
|
|
57
|
-
return r.classes?.[e] ??
|
|
57
|
+
return r.classes?.[e] ?? Te[e] ?? "redact";
|
|
58
58
|
}
|
|
59
59
|
return n?.safe ? r.onSafe ?? "keep" : n?.columnType === "id" || n?.columnType === "reference" ? "keep" : r.onUnclassified ?? "error";
|
|
60
|
-
},
|
|
60
|
+
}, Oe = (e, t) => ie("sha256", e).update(t == null ? "∅" : String(t)).digest(), z = (e, t) => e.readUInt32BE(t * 4 % (e.length - 4)), ke = [
|
|
61
61
|
"Ada",
|
|
62
62
|
"Alan",
|
|
63
63
|
"Grace",
|
|
@@ -70,7 +70,7 @@ var xe = (e) => {
|
|
|
70
70
|
"Guido",
|
|
71
71
|
"Margaret",
|
|
72
72
|
"Bjarne"
|
|
73
|
-
],
|
|
73
|
+
], Ae = [
|
|
74
74
|
"Lovelace",
|
|
75
75
|
"Turing",
|
|
76
76
|
"Hopper",
|
|
@@ -83,7 +83,7 @@ var xe = (e) => {
|
|
|
83
83
|
"Rossum",
|
|
84
84
|
"Hamilton",
|
|
85
85
|
"Stroustrup"
|
|
86
|
-
],
|
|
86
|
+
], je = [
|
|
87
87
|
"Maple",
|
|
88
88
|
"Oak",
|
|
89
89
|
"Cedar",
|
|
@@ -92,7 +92,7 @@ var xe = (e) => {
|
|
|
92
92
|
"Birch",
|
|
93
93
|
"Willow",
|
|
94
94
|
"Ash"
|
|
95
|
-
],
|
|
95
|
+
], Me = [
|
|
96
96
|
"Initech",
|
|
97
97
|
"Hooli",
|
|
98
98
|
"Umbrella",
|
|
@@ -101,23 +101,23 @@ var xe = (e) => {
|
|
|
101
101
|
"Soylent",
|
|
102
102
|
"Stark",
|
|
103
103
|
"Wayne"
|
|
104
|
-
], B = (e, t, n) => e[z(t, n) % e.length],
|
|
105
|
-
let r =
|
|
104
|
+
], B = (e, t, n) => e[z(t, n) % e.length], Ne = (e, t, n) => {
|
|
105
|
+
let r = Oe(n, `${e}:${String(t)}`), i = z(r, 1) % 1e4;
|
|
106
106
|
switch (e) {
|
|
107
|
-
case "email": return `${B(
|
|
108
|
-
case "fullName": return `${B(
|
|
109
|
-
case "firstName": return B(
|
|
110
|
-
case "lastName": return B(
|
|
111
|
-
case "username": return `${B(
|
|
107
|
+
case "email": return `${B(ke, r, 0).toLowerCase()}.${B(Ae, r, 2).toLowerCase()}${i}@example.com`;
|
|
108
|
+
case "fullName": return `${B(ke, r, 0)} ${B(Ae, r, 1)}`;
|
|
109
|
+
case "firstName": return B(ke, r, 0);
|
|
110
|
+
case "lastName": return B(Ae, r, 1);
|
|
111
|
+
case "username": return `${B(ke, r, 0).toLowerCase()}${i}`;
|
|
112
112
|
case "phone": return `+1${String(z(r, 0) % 1e10).padStart(10, "0")}`;
|
|
113
|
-
case "address": return `${z(r, 0) % 9899 + 100} ${B(
|
|
114
|
-
case "company": return B(
|
|
113
|
+
case "address": return `${z(r, 0) % 9899 + 100} ${B(je, r, 1)} St`;
|
|
114
|
+
case "company": return B(Me, r, 0);
|
|
115
115
|
case "url": return `https://example.com/${r.toString("hex").slice(0, 12)}`;
|
|
116
116
|
case "ip": return `${z(r, 0) % 256}.${z(r, 1) % 256}.${z(r, 2) % 256}.${z(r, 3) % 254 + 1}`;
|
|
117
|
-
case "creditCard": return
|
|
117
|
+
case "creditCard": return Pe(`4${r.toString("hex").replace(/\D/g, "").padEnd(14, "0").slice(0, 14)}`);
|
|
118
118
|
default: return `${e}_${r.toString("hex").slice(0, 8)}`;
|
|
119
119
|
}
|
|
120
|
-
},
|
|
120
|
+
}, Pe = (e) => {
|
|
121
121
|
let t = e.slice(0, 15).split("").map(Number), n = 0;
|
|
122
122
|
t.forEach((e, t) => {
|
|
123
123
|
let r = e;
|
|
@@ -125,31 +125,31 @@ var xe = (e) => {
|
|
|
125
125
|
});
|
|
126
126
|
let r = (10 - n % 10) % 10;
|
|
127
127
|
return e.slice(0, 15) + String(r);
|
|
128
|
-
},
|
|
128
|
+
}, Fe = 864e5, Ie = (e, t) => {
|
|
129
129
|
let n = e instanceof Date ? e.getTime() : Date.parse(String(e));
|
|
130
130
|
if (Number.isNaN(n)) return e;
|
|
131
|
-
let r = z(
|
|
131
|
+
let r = z(Oe(t, "date-shift-offset"), 0) % 730 - 365, i = new Date(n + r * Fe);
|
|
132
132
|
return e instanceof Date ? i : i.toISOString();
|
|
133
|
-
},
|
|
133
|
+
}, Le = (e, t) => {
|
|
134
134
|
if (t.value === null || t.value === void 0) return null;
|
|
135
135
|
if (e === "error") throw Error(`masking: unresolved 'error' action for ${t.table}.${t.column}`);
|
|
136
136
|
if (e === "keep") return t.value;
|
|
137
137
|
if (e === "null") return null;
|
|
138
138
|
if (e === "redact") return t.columnType === "text" || typeof t.value == "string" ? "[redacted]" : null;
|
|
139
|
-
if (e === "hash") return
|
|
140
|
-
if (e === "dateShift") return
|
|
141
|
-
if (typeof e == "object" && "fake" in e) return
|
|
139
|
+
if (e === "hash") return Oe(t.seed, t.value).toString("hex").slice(0, 16);
|
|
140
|
+
if (e === "dateShift") return Ie(t.value, t.seed);
|
|
141
|
+
if (typeof e == "object" && "fake" in e) return Ne(e.fake, t.value, t.seed);
|
|
142
142
|
if (typeof e == "object" && "custom" in e) return e.custom(t);
|
|
143
143
|
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 }.`);
|
|
144
|
-
},
|
|
144
|
+
}, Re = (e) => typeof e == "string" ? e === "keep" || e === "null" || e === "redact" || e === "hash" || e === "dateShift" || e === "error" : typeof e != "object" || !e ? !1 : "fake" in e ? typeof e.fake == "string" : "custom" in e && typeof e.custom == "function", ze = (e, t, n) => {
|
|
145
145
|
let r = {}, i = [], a = [];
|
|
146
146
|
for (let o of e) for (let e of o.columns) {
|
|
147
|
-
let s = `${o.name}.${e.name}`, c =
|
|
147
|
+
let s = `${o.name}.${e.name}`, c = De(o.name, e.name, t[o.name]?.[e.name], n);
|
|
148
148
|
if (c === "error") {
|
|
149
149
|
i.push(s);
|
|
150
150
|
continue;
|
|
151
151
|
}
|
|
152
|
-
if (!
|
|
152
|
+
if (!Re(c)) {
|
|
153
153
|
a.push({
|
|
154
154
|
column: s,
|
|
155
155
|
action: c
|
|
@@ -163,7 +163,7 @@ var xe = (e) => {
|
|
|
163
163
|
unclassified: i,
|
|
164
164
|
invalid: a
|
|
165
165
|
};
|
|
166
|
-
},
|
|
166
|
+
}, Be = [
|
|
167
167
|
{
|
|
168
168
|
name: "email",
|
|
169
169
|
re: /[\w.+-]+@[\w-]+\.[\w.-]+/
|
|
@@ -180,7 +180,7 @@ var xe = (e) => {
|
|
|
180
180
|
name: "creditCard",
|
|
181
181
|
re: /\b(?:\d[ -]?){13,16}\b/
|
|
182
182
|
}
|
|
183
|
-
],
|
|
183
|
+
], Ve = (e, t, n) => {
|
|
184
184
|
let r = [], i = /* @__PURE__ */ new Set();
|
|
185
185
|
for (let [a, o] of Object.entries(n)) {
|
|
186
186
|
if (!a.startsWith(`${t}.`) || o !== "keep") continue;
|
|
@@ -188,7 +188,7 @@ var xe = (e) => {
|
|
|
188
188
|
for (let a of e) {
|
|
189
189
|
let e = a[n];
|
|
190
190
|
if (typeof e != "string" || e.length === 0) continue;
|
|
191
|
-
let o =
|
|
191
|
+
let o = Be.find((t) => t.re.test(e));
|
|
192
192
|
if (o && !i.has(n)) {
|
|
193
193
|
i.add(n), r.push({
|
|
194
194
|
table: t,
|
|
@@ -201,9 +201,9 @@ var xe = (e) => {
|
|
|
201
201
|
}
|
|
202
202
|
}
|
|
203
203
|
return r;
|
|
204
|
-
},
|
|
204
|
+
}, He = (e, t, n, r, i) => {
|
|
205
205
|
let a = {};
|
|
206
|
-
for (let [o, s] of Object.entries(e)) a[o] =
|
|
206
|
+
for (let [o, s] of Object.entries(e)) a[o] = Le(n[`${t}.${o}`] ?? "keep", {
|
|
207
207
|
value: s,
|
|
208
208
|
table: t,
|
|
209
209
|
column: o,
|
|
@@ -211,7 +211,7 @@ var xe = (e) => {
|
|
|
211
211
|
...i?.[o] ? { columnType: i[o] } : {}
|
|
212
212
|
});
|
|
213
213
|
return a;
|
|
214
|
-
},
|
|
214
|
+
}, Ue = /* @__PURE__ */ new Map([
|
|
215
215
|
["_voltro_migration_plans", "the schema fingerprint THIS deployment applied — a foreign row makes the next boot refuse"],
|
|
216
216
|
["_voltro_migrations", "which file-based migrations ran HERE — a foreign row marks unrun migrations as done"],
|
|
217
217
|
["_voltro_seeds", "which seeds ran HERE — a foreign row suppresses seeding the target still needs"],
|
|
@@ -243,7 +243,7 @@ var xe = (e) => {
|
|
|
243
243
|
["_voltro_notification_held", "notifications waiting to be SENT from here — a foreign row makes the target deliver something queued elsewhere"],
|
|
244
244
|
["_voltro_webhook_event_stats", "per-deployment delivery counters a backoff decision reads — foreign counts steer the target's retries"],
|
|
245
245
|
["_voltro_webhook_rate_windows", "the live rate-limit windows this deployment is inside — a foreign window throttles or releases wrongly"]
|
|
246
|
-
]),
|
|
246
|
+
]), We = /* @__PURE__ */ new Map([
|
|
247
247
|
["_voltro_agent_threads", "conversations the app's users had — app data"],
|
|
248
248
|
["_voltro_agent_messages", "the messages in those conversations — app data"],
|
|
249
249
|
["_voltro_prompts", "prompt definitions the app authored — app data"],
|
|
@@ -274,21 +274,21 @@ var xe = (e) => {
|
|
|
274
274
|
["_voltro_audit_log", "the app's audit trail — compliance data the app owns, not deployment bookkeeping"],
|
|
275
275
|
["_voltro_feature_flags", "the flags the app defined — app configuration"],
|
|
276
276
|
["_voltro_feature_flag_audit", "who changed which flag — belongs with the flags"]
|
|
277
|
-
]), V = (e) =>
|
|
277
|
+
]), V = (e) => Ue.has(e), Ge = (e) => Ue.get(e), Ke = (e, t) => {
|
|
278
278
|
if (e.length === 0) return;
|
|
279
|
-
let n = e.map((e) => ` ${e} — ${
|
|
279
|
+
let n = e.map((e) => ` ${e} — ${Ge(e) ?? "describes this deployment"}`);
|
|
280
280
|
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.`;
|
|
281
281
|
}, H = class extends c.TaggedError()("BundleError", {
|
|
282
282
|
reason: c.String,
|
|
283
283
|
path: c.optional(c.String)
|
|
284
|
-
}) {},
|
|
284
|
+
}) {}, qe = class extends c.TaggedError()("CodecError", {
|
|
285
285
|
table: c.String,
|
|
286
286
|
reason: c.String
|
|
287
|
-
}) {},
|
|
287
|
+
}) {}, Je = class extends c.TaggedError()("IntegrityError", {
|
|
288
288
|
what: c.String,
|
|
289
289
|
expected: c.String,
|
|
290
290
|
actual: c.String
|
|
291
|
-
}) {},
|
|
291
|
+
}) {}, Ye = class extends t.TaggedError("CompressionError") {}, U = class extends c.TaggedError()("ScopeError", { reason: c.String }) {}, Xe = class extends c.TaggedError()("RowsRefusedError", {
|
|
292
292
|
rows: c.Array(c.Struct({
|
|
293
293
|
table: c.String,
|
|
294
294
|
id: c.String,
|
|
@@ -302,29 +302,29 @@ var xe = (e) => {
|
|
|
302
302
|
refused: c.Number,
|
|
303
303
|
primary: c.Number
|
|
304
304
|
}))
|
|
305
|
-
}) {},
|
|
305
|
+
}) {}, Ze = class extends c.TaggedError()("SchemaDriftError", {
|
|
306
306
|
bundleFingerprint: c.String,
|
|
307
307
|
targetFingerprint: c.String,
|
|
308
308
|
diff: c.Array(c.String)
|
|
309
309
|
}) {}, W = class extends t.TaggedError("NativeToolError") {
|
|
310
310
|
get message() {
|
|
311
|
-
let e = this.stderr?.trim();
|
|
312
|
-
return `${this.tool}: ${this.reason}${e ? `\n${e}` : ""}`;
|
|
311
|
+
let e = this.stderr?.trim(), t = this.hint?.trim();
|
|
312
|
+
return `${this.tool}: ${this.reason}${e ? `\n${e}` : ""}${t ? `\n\nhint: ${t}` : ""}`;
|
|
313
313
|
}
|
|
314
|
-
},
|
|
314
|
+
}, Qe = class extends c.TaggedError()("MaskingError", {
|
|
315
315
|
unclassified: c.Array(c.String),
|
|
316
316
|
invalidActions: c.optional(c.Array(c.String))
|
|
317
|
-
}) {},
|
|
317
|
+
}) {}, $e = class extends c.TaggedError()("ImportModeError", {
|
|
318
318
|
mode: c.String,
|
|
319
319
|
reason: c.String
|
|
320
|
-
}) {},
|
|
320
|
+
}) {}, et = class extends c.TaggedError()("CrossDialectError", {
|
|
321
321
|
sourceDialect: c.String,
|
|
322
322
|
targetDialect: c.String,
|
|
323
323
|
blocking: c.Array(c.String)
|
|
324
|
-
}) {}, G = (e, t) => e.tables.find((e) => e.name === t)?.columns.find((e) => e.type === "id")?.name ?? "id",
|
|
324
|
+
}) {}, G = (e, t) => e.tables.find((e) => e.name === t)?.columns.find((e) => e.type === "id")?.name ?? "id", tt = (e, t) => (e.tables.find((e) => e.name === t)?.columns ?? []).filter((e) => e.references).map((e) => ({
|
|
325
325
|
col: e.name,
|
|
326
326
|
refTable: e.references.table
|
|
327
|
-
})),
|
|
327
|
+
})), nt = (e, t, r) => n.gen(function* () {
|
|
328
328
|
let i = r.maxDepth ?? 12, a = new Set(r.pinned ?? []), o = /* @__PURE__ */ new Map(), s = /* @__PURE__ */ new Map(), c = /* @__PURE__ */ new Map(), u = (e) => {
|
|
329
329
|
let t = s.get(e) ?? /* @__PURE__ */ new Set();
|
|
330
330
|
return s.set(e, t), t;
|
|
@@ -342,7 +342,7 @@ var xe = (e) => {
|
|
|
342
342
|
for (let t of n) d(t.refTable, e[t.col]);
|
|
343
343
|
};
|
|
344
344
|
}, p = (r, i) => {
|
|
345
|
-
let a = f(r, G(t, r),
|
|
345
|
+
let a = f(r, G(t, r), tt(t, r));
|
|
346
346
|
return l.runForEach(w(e, {
|
|
347
347
|
table: r,
|
|
348
348
|
pk: G(t, r),
|
|
@@ -377,13 +377,13 @@ var xe = (e) => {
|
|
|
377
377
|
}
|
|
378
378
|
}
|
|
379
379
|
return o;
|
|
380
|
-
}),
|
|
380
|
+
}), rt = (e, t) => ({
|
|
381
381
|
tables: [...e.keys()],
|
|
382
382
|
predicateFor: (n) => {
|
|
383
383
|
let r = e.get(n);
|
|
384
384
|
return r ? y(G(t, n), [...r]) : void 0;
|
|
385
385
|
}
|
|
386
|
-
}),
|
|
386
|
+
}), it = "tenantId", at = (e, t, r) => n.gen(function* () {
|
|
387
387
|
let i = t.tables.map((e) => e.name), a = new Map(t.tables.map((e) => [e.name, e]));
|
|
388
388
|
if (e.kind === "all") {
|
|
389
389
|
let t = new Set(e.exclude ?? []);
|
|
@@ -403,29 +403,29 @@ var xe = (e) => {
|
|
|
403
403
|
}
|
|
404
404
|
let o = e.tenantId;
|
|
405
405
|
if (o === void 0 || o === "") return yield* n.fail(new U({ reason: "scope { kind: 'tenant' } requires a tenantId — pass --tenant <id> (CLI) or scope.tenantId (API). Refusing to fall back to an unfiltered export." }));
|
|
406
|
-
let s = r.tenantTables ? r.tenantTables.filter((e) => a.has(e)) : i.filter((e) => a.get(e)?.columns.some((e) => e.name ===
|
|
407
|
-
if (s.length === 0) return yield* n.fail(new U({ reason: r.tenantTables ? "tenant scope: none of the provided tenantTables exist in the schema — nothing to filter." : `tenant scope: no table carries a '${
|
|
408
|
-
let c = yield*
|
|
409
|
-
seeds: Object.fromEntries(s.map((e) => [e, g(
|
|
406
|
+
let s = r.tenantTables ? r.tenantTables.filter((e) => a.has(e)) : i.filter((e) => a.get(e)?.columns.some((e) => e.name === it) ?? !1);
|
|
407
|
+
if (s.length === 0) return yield* n.fail(new U({ reason: r.tenantTables ? "tenant scope: none of the provided tenantTables exist in the schema — nothing to filter." : `tenant scope: no table carries a '${it}' column — nothing to filter. A tenant export of a schema without tenant-scoped tables would be the full database; refusing.` }));
|
|
408
|
+
let c = yield* nt(r.store, t, {
|
|
409
|
+
seeds: Object.fromEntries(s.map((e) => [e, g(it, o)])),
|
|
410
410
|
pinned: s,
|
|
411
411
|
children: { roots: s }
|
|
412
412
|
}), l = [...c.entries()].filter(([, e]) => e.size > 0).map(([e]) => e), u = new Set(s);
|
|
413
413
|
return {
|
|
414
414
|
tables: [...s, ...l],
|
|
415
|
-
predicateFor: (e) => u.has(e) ? g(
|
|
415
|
+
predicateFor: (e) => u.has(e) ? g(it, o) : c.has(e) ? y(G(t, e), [...c.get(e)]) : void 0
|
|
416
416
|
};
|
|
417
|
-
}),
|
|
417
|
+
}), ot = (e, t) => ({
|
|
418
418
|
list: t,
|
|
419
|
-
open: (t) =>
|
|
420
|
-
}),
|
|
421
|
-
write: (t, n, r) =>
|
|
419
|
+
open: (t) => A(e, t)
|
|
420
|
+
}), st = (e) => ({
|
|
421
|
+
write: (t, n, r) => j(e, t, n, r),
|
|
422
422
|
has: (t) => e.head(t).pipe(n.map((e) => e !== null))
|
|
423
|
-
}),
|
|
423
|
+
}), ct = (e, t) => n.tryPromise({
|
|
424
424
|
try: async () => {
|
|
425
425
|
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) {
|
|
426
426
|
u.update(e), d += e.length, n(null, e);
|
|
427
427
|
} });
|
|
428
|
-
await o(await
|
|
428
|
+
await o(await he(e.stream), f, n(l));
|
|
429
429
|
let p = u.digest("hex"), m = c.join(t, p);
|
|
430
430
|
try {
|
|
431
431
|
await r.access(m), await r.rm(l, { force: !0 });
|
|
@@ -442,19 +442,47 @@ var xe = (e) => {
|
|
|
442
442
|
reason: `asset write failed: ${String(e?.message ?? e)}`,
|
|
443
443
|
path: t
|
|
444
444
|
})
|
|
445
|
-
}),
|
|
446
|
-
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), c = o("sha256"), u =
|
|
445
|
+
}), lt = (e, t, r) => n.gen(function* () {
|
|
446
|
+
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), c = o("sha256"), u = me("bundle", () => a(s)).pipe(l.tap((e) => n.sync(() => c.update(e))));
|
|
447
447
|
yield* e.write(r.key, u, {
|
|
448
448
|
contentType: r.contentType,
|
|
449
449
|
totalSize: r.size
|
|
450
450
|
});
|
|
451
451
|
let d = c.digest("hex");
|
|
452
|
-
if (d !== r.sha256) return yield* n.fail(new
|
|
452
|
+
if (d !== r.sha256) return yield* n.fail(new Je({
|
|
453
453
|
what: `asset ${r.key}`,
|
|
454
454
|
expected: r.sha256,
|
|
455
455
|
actual: d
|
|
456
456
|
}));
|
|
457
|
-
}),
|
|
457
|
+
}), ut = {
|
|
458
|
+
done: () => !1,
|
|
459
|
+
mark: () => n.void
|
|
460
|
+
}, dt = (e) => typeof e == "object" && !!e && e.status === 404, ft = (e) => n.gen(function* () {
|
|
461
|
+
let t = e.progress ?? ut, r = 0, i = 0, a = [];
|
|
462
|
+
return yield* l.runForEach(e.source.list(), (o) => t.done(o.key) ? n.void : e.source.open(o.key).pipe(n.map((e) => e), n.catchIf(dt, () => n.sync(() => {
|
|
463
|
+
a.push(o.key);
|
|
464
|
+
})), n.flatMap((a) => a === void 0 ? n.void : ct(a, e.assetsDir).pipe(n.flatMap((e) => {
|
|
465
|
+
let n = {
|
|
466
|
+
key: o.key,
|
|
467
|
+
sha256: e.sha256,
|
|
468
|
+
size: e.size,
|
|
469
|
+
contentType: e.contentType
|
|
470
|
+
};
|
|
471
|
+
return r++, i += e.size, t.mark(o.key, n);
|
|
472
|
+
}))))).pipe(n.mapError((e) => e instanceof H ? e : new H({ reason: `asset export failed: ${String(e)}` }))), {
|
|
473
|
+
count: r,
|
|
474
|
+
totalBytes: i,
|
|
475
|
+
missing: a
|
|
476
|
+
};
|
|
477
|
+
}), pt = (e) => me("bundle", () => le(e)).pipe(l.decodeText(), l.splitLines, l.filter((e) => e.trim().length > 0), l.map((e) => JSON.parse(e)), l.mapError((t) => t instanceof H ? t : new H({
|
|
478
|
+
reason: `asset index unreadable: ${String(t)}`,
|
|
479
|
+
path: e
|
|
480
|
+
}))), mt = (e) => n.gen(function* () {
|
|
481
|
+
let t = e.progress ?? ut, r = 0;
|
|
482
|
+
return yield* l.runForEach(pt(e.indexPath), (i) => t.done(i.key) ? n.void : lt(e.sink, e.assetsDir, i).pipe(n.andThen(n.sync(() => {
|
|
483
|
+
r++;
|
|
484
|
+
})), n.andThen(t.mark(i.key, i)))), r;
|
|
485
|
+
}), ht = () => typeof M.createZstdCompress == "function", gt = (e) => e === "none" ? "none" : e === "gzip" ? "gzip" : ht() ? "zstd" : "gzip", _t = (e) => e === "zstd" ? ".zst" : e === "gzip" ? ".gz" : "", vt = (e) => e === "zstd" ? M.createZstdCompress() : e === "gzip" ? M.createGzip() : new N(), yt = (e) => e === "zstd" ? M.createZstdDecompress() : e === "gzip" ? M.createGunzip() : new N(), bt = 1, xt = c.Struct({
|
|
458
486
|
name: c.String,
|
|
459
487
|
file: c.String,
|
|
460
488
|
rowCount: c.Number,
|
|
@@ -464,12 +492,12 @@ var xe = (e) => {
|
|
|
464
492
|
value: c.String
|
|
465
493
|
}),
|
|
466
494
|
primaryKey: c.String
|
|
467
|
-
}),
|
|
495
|
+
}), St = c.Struct({
|
|
468
496
|
kind: c.Literal("all", "tenant", "tables"),
|
|
469
497
|
tenantId: c.optional(c.String),
|
|
470
498
|
tables: c.optional(c.Array(c.String)),
|
|
471
499
|
exclude: c.optional(c.Array(c.String))
|
|
472
|
-
}),
|
|
500
|
+
}), Ct = c.Struct({
|
|
473
501
|
count: c.Number,
|
|
474
502
|
totalBytes: c.Number,
|
|
475
503
|
index: c.optional(c.String)
|
|
@@ -480,23 +508,23 @@ c.Struct({
|
|
|
480
508
|
size: c.Number,
|
|
481
509
|
contentType: c.String
|
|
482
510
|
});
|
|
483
|
-
var
|
|
511
|
+
var wt = c.Struct({
|
|
484
512
|
formatVersion: c.Number,
|
|
485
513
|
createdAt: c.String,
|
|
486
514
|
source: c.Struct({
|
|
487
515
|
dialect: c.String,
|
|
488
516
|
schemaFingerprint: c.String
|
|
489
517
|
}),
|
|
490
|
-
scope:
|
|
518
|
+
scope: St,
|
|
491
519
|
consistency: c.Literal("live", "snapshot"),
|
|
492
520
|
compression: c.Literal("zstd", "gzip", "none"),
|
|
493
|
-
tables: c.Array(
|
|
494
|
-
assets:
|
|
521
|
+
tables: c.Array(xt),
|
|
522
|
+
assets: Ct,
|
|
495
523
|
masking: c.optional(c.Struct({
|
|
496
524
|
policyId: c.optional(c.String),
|
|
497
525
|
transformed: c.Array(c.String)
|
|
498
526
|
}))
|
|
499
|
-
}),
|
|
527
|
+
}), Tt = c.encodeSync(c.parseJson(wt)), Et = c.decodeUnknownSync(c.parseJson(wt)), K = "manifest.json", Dt = "README.md", Ot = "data", kt = "assets", At = "assets/index.ndjson", jt = ".ledger.json", Mt = (e) => e.kind === "tenant" ? `tenant \`${e.tenantId}\`` : e.kind === "tables" ? `tables \`${(e.tables ?? []).join(", ")}\`` : "all tables", Nt = (e, t) => {
|
|
500
528
|
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");
|
|
501
529
|
return `# Voltro data export
|
|
502
530
|
|
|
@@ -510,7 +538,7 @@ The machine-readable manifest is \`manifest.json\`; this file is a human summary
|
|
|
510
538
|
| **Format version** | ${e.formatVersion} |
|
|
511
539
|
| **Consistency** | ${e.consistency} |
|
|
512
540
|
| **Compression** | ${e.compression} |
|
|
513
|
-
| **Scope** | ${
|
|
541
|
+
| **Scope** | ${Mt(e.scope)} |
|
|
514
542
|
| **Tables** | ${e.tables.length} |
|
|
515
543
|
| **Rows (total)** | ${n} |
|
|
516
544
|
| **Assets** | ${a} |
|
|
@@ -535,26 +563,26 @@ voltro data import <this-directory>
|
|
|
535
563
|
voltro data import <this-directory> --target api --api-url <url> --token <secret>
|
|
536
564
|
\`\`\`
|
|
537
565
|
`;
|
|
538
|
-
},
|
|
566
|
+
}, Pt = i.counter("voltro_data_transfer_rows", { description: "Rows exported/imported by @voltro/data-transfer, tagged by phase + table." }), Ft = a.exponential({
|
|
539
567
|
start: .01,
|
|
540
568
|
factor: 2,
|
|
541
569
|
count: 16
|
|
542
|
-
}),
|
|
570
|
+
}), It = i.histogram("voltro_data_transfer_table_seconds", Ft, "Per-table export/import duration in seconds, tagged by phase."), q = (e, t) => {
|
|
543
571
|
if (e) try {
|
|
544
572
|
e(t);
|
|
545
573
|
} catch {}
|
|
546
|
-
},
|
|
574
|
+
}, Lt = (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.zipRight(It.pipe(i.tagged("phase", e), i.update((s - r) / 1e3)), Pt.pipe(i.tagged("phase", e), i.tagged("table", t), i.update(o(a))))))), n.map(({ result: e }) => e), n.withSpan(`data-transfer.${e}.table`, { attributes: {
|
|
547
575
|
table: t,
|
|
548
576
|
index: r,
|
|
549
577
|
tableCount: a,
|
|
550
578
|
phase: e
|
|
551
|
-
} })),
|
|
579
|
+
} })), Rt = () => ({
|
|
552
580
|
tables: {},
|
|
553
581
|
assets: {}
|
|
554
|
-
}),
|
|
582
|
+
}), zt = (e) => n.tryPromise(async () => {
|
|
555
583
|
let t = await (await import("node:fs/promises")).readFile(e, "utf8");
|
|
556
584
|
return JSON.parse(t);
|
|
557
|
-
}).pipe(n.orElseSucceed(
|
|
585
|
+
}).pipe(n.orElseSucceed(Rt)), J = (e, t) => n.tryPromise({
|
|
558
586
|
try: async () => {
|
|
559
587
|
let n = await import("node:fs/promises"), { randomUUID: r } = await import("node:crypto"), i = `${e}.tmp-${r()}`;
|
|
560
588
|
await n.writeFile(i, JSON.stringify(t)), await n.rename(i, e);
|
|
@@ -563,11 +591,11 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
563
591
|
reason: `ledger write failed: ${String(t?.message ?? t)}`,
|
|
564
592
|
path: e
|
|
565
593
|
})
|
|
566
|
-
}),
|
|
594
|
+
}), Bt = (e, t) => t in e.tables, Vt = (e, t) => t in e.assets, Ht = /* @__PURE__ */ new Set([
|
|
567
595
|
"json",
|
|
568
596
|
"array",
|
|
569
597
|
"vector"
|
|
570
|
-
]),
|
|
598
|
+
]), Ut = (e) => typeof e == "object" && !!e && !Array.isArray(e) && typeof e.$v == "string", Wt = (e) => Buffer.from(e).toString("base64"), Gt = (e) => new Uint8Array(Buffer.from(e, "base64")), Kt = (e) => e instanceof Date ? e.toISOString() : typeof e == "number" ? new Date(e).toISOString() : String(e), qt = (e, t) => e == null ? null : t !== void 0 && Ht.has(t) ? e : t === "bytes" ? Wt(e) : t === "timestamp" || t === "date" ? Kt(e) : typeof e == "bigint" ? {
|
|
571
599
|
$v: "bigint",
|
|
572
600
|
d: e.toString()
|
|
573
601
|
} : typeof e == "number" && !Number.isFinite(e) ? {
|
|
@@ -578,30 +606,30 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
578
606
|
d: e.toISOString()
|
|
579
607
|
} : e instanceof Uint8Array ? {
|
|
580
608
|
$v: "bytes",
|
|
581
|
-
d:
|
|
582
|
-
} : e,
|
|
609
|
+
d: Wt(e)
|
|
610
|
+
} : e, Jt = (e) => {
|
|
583
611
|
switch (e.$v) {
|
|
584
612
|
case "bigint": return BigInt(e.d);
|
|
585
613
|
case "number": return Number(e.d);
|
|
586
614
|
case "date": return new Date(e.d);
|
|
587
|
-
case "bytes": return
|
|
615
|
+
case "bytes": return Gt(e.d);
|
|
588
616
|
default: return e;
|
|
589
617
|
}
|
|
590
|
-
},
|
|
618
|
+
}, Yt = (e, t) => e == null ? null : t !== void 0 && Ht.has(t) ? e : Ut(e) ? Jt(e) : t === "bytes" ? Gt(e) : t === "timestamp" || t === "date" ? new Date(e) : e, Xt = (e, t) => {
|
|
591
619
|
let n = {};
|
|
592
|
-
for (let [r, i] of Object.entries(e)) n[r] =
|
|
620
|
+
for (let [r, i] of Object.entries(e)) n[r] = qt(i, t[r]);
|
|
593
621
|
return n;
|
|
594
|
-
},
|
|
622
|
+
}, Zt = (e, t) => {
|
|
595
623
|
let n = {};
|
|
596
|
-
for (let [r, i] of Object.entries(e)) n[r] =
|
|
624
|
+
for (let [r, i] of Object.entries(e)) n[r] = Yt(i, t[r]);
|
|
597
625
|
return n;
|
|
598
|
-
},
|
|
599
|
-
try: () =>
|
|
600
|
-
catch: (e) => new
|
|
626
|
+
}, Qt = new TextEncoder(), $t = (e, t) => e.pipe(l.map((e) => Qt.encode(`${JSON.stringify(Xt(e, t))}\n`))), en = (e, t, r) => e.pipe(l.decodeText(), l.splitLines, l.filter((e) => e.trim().length > 0), l.mapEffect((e) => n.try({
|
|
627
|
+
try: () => Zt(JSON.parse(e), r),
|
|
628
|
+
catch: (e) => new qe({
|
|
601
629
|
table: t,
|
|
602
630
|
reason: String(e?.message ?? e)
|
|
603
631
|
})
|
|
604
|
-
}))),
|
|
632
|
+
}))), tn = (e, t) => {
|
|
605
633
|
let n = new Set(e), r = /* @__PURE__ */ new Map();
|
|
606
634
|
for (let e of t.tables) {
|
|
607
635
|
if (!n.has(e.name)) continue;
|
|
@@ -627,33 +655,33 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
627
655
|
let r = typeof t?.message == "string" ? t.message.trim() : "";
|
|
628
656
|
if (r === "" ? t?.cause !== void 0 && t.cause !== null && n.push(Y(t.cause)) : n.push(r), n.length === 0) {
|
|
629
657
|
let t = String(e ?? "").trim();
|
|
630
|
-
return t === "" || t === "[object Object]" ?
|
|
658
|
+
return t === "" || t === "[object Object]" ? nn(e) : t;
|
|
631
659
|
}
|
|
632
660
|
return n.join(": ");
|
|
633
|
-
},
|
|
661
|
+
}, nn = (e) => {
|
|
634
662
|
try {
|
|
635
663
|
let t = JSON.stringify(e);
|
|
636
664
|
return t === void 0 || t === "{}" ? "unknown error (no message, cause or tag)" : t;
|
|
637
665
|
} catch {
|
|
638
666
|
return "unknown error (not serialisable)";
|
|
639
667
|
}
|
|
640
|
-
},
|
|
668
|
+
}, rn = (e, t) => {
|
|
641
669
|
let n = e.tables.find((e) => e.name === t), r = {};
|
|
642
670
|
for (let e of n?.columns ?? []) e.generatedAs === void 0 && (r[e.name] = e.type);
|
|
643
671
|
return r;
|
|
644
|
-
},
|
|
672
|
+
}, an = (e, t) => (e.tables.find((e) => e.name === t)?.columns ?? []).filter((e) => e.generatedAs !== void 0).map((e) => e.name), on = (e, t) => {
|
|
645
673
|
if (t.length === 0) return e;
|
|
646
674
|
let n = { ...e };
|
|
647
675
|
for (let e of t) delete n[e];
|
|
648
676
|
return n;
|
|
649
|
-
},
|
|
677
|
+
}, sn = (e, t) => {
|
|
650
678
|
let n = e.tables.find((e) => e.name === t);
|
|
651
679
|
if (n === void 0) return { refusal: `${t}: not in the schema snapshot, so its primary key is unknown.` };
|
|
652
680
|
let r = n.columns.find((e) => e.type === "id");
|
|
653
681
|
if (r !== void 0) return { column: r.name };
|
|
654
682
|
let i = n.indexes.find((e) => e.name === `${t}_pkey`)?.columns ?? n.primaryKey ?? [];
|
|
655
683
|
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.` };
|
|
656
|
-
},
|
|
684
|
+
}, cn = (e, t, r) => n.tryPromise({
|
|
657
685
|
try: async () => {
|
|
658
686
|
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: c, Transform: u } = await import("node:stream"), d = a("sha256"), f = 0, p = new u({ transform(e, t, n) {
|
|
659
687
|
d.update(e);
|
|
@@ -662,7 +690,7 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
662
690
|
n(null, e);
|
|
663
691
|
} }), m = c.fromWeb(l.toReadableStream(e)), h = `${t}.tmp-${o()}`;
|
|
664
692
|
try {
|
|
665
|
-
await s(m, p,
|
|
693
|
+
await s(m, p, vt(r), n(h)), await i.rename(h, t);
|
|
666
694
|
} catch (e) {
|
|
667
695
|
throw await i.rm(h, { force: !0 }).catch(() => {}), e;
|
|
668
696
|
}
|
|
@@ -675,7 +703,7 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
675
703
|
reason: `write table failed: ${Y(e)}`,
|
|
676
704
|
path: t
|
|
677
705
|
})
|
|
678
|
-
}),
|
|
706
|
+
}), ln = (e, t) => n.tryPromise({
|
|
679
707
|
try: async () => {
|
|
680
708
|
await (await import("node:fs/promises")).appendFile(e, `${t}\n`);
|
|
681
709
|
},
|
|
@@ -683,13 +711,13 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
683
711
|
reason: `append failed: ${Y(t)}`,
|
|
684
712
|
path: e
|
|
685
713
|
})
|
|
686
|
-
}),
|
|
714
|
+
}), un = (e, t) => n.gen(function* () {
|
|
687
715
|
let r = [], i = t.ordered.length;
|
|
688
716
|
for (let a = 0; a < t.ordered.length; a++) {
|
|
689
|
-
let o = t.ordered[a], s =
|
|
717
|
+
let o = t.ordered[a], s = rn(t.snapshot, o), c = sn(t.snapshot, o);
|
|
690
718
|
if ("refusal" in c) return yield* n.fail(new U({ reason: `export: ${c.refusal}` }));
|
|
691
|
-
let u = c.column, d = `${
|
|
692
|
-
if (
|
|
719
|
+
let u = c.column, d = `${Ot}/${o}.ndjson${_t(t.compression)}`;
|
|
720
|
+
if (Bt(t.ledger, o)) {
|
|
693
721
|
let e = t.ledger.tables[o];
|
|
694
722
|
q(t.onProgress, {
|
|
695
723
|
phase: "export",
|
|
@@ -723,7 +751,7 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
723
751
|
chunkSize: t.chunkSize,
|
|
724
752
|
retry: t.retry,
|
|
725
753
|
...f === void 0 ? {} : { where: f }
|
|
726
|
-
}), m =
|
|
754
|
+
}), m = an(t.snapshot, o), h = m.length === 0 ? p : p.pipe(l.map((e) => on(e, m))), g = t.mask ? h.pipe(l.map((e) => He(e, o, t.mask.actions, t.mask.seed, s))) : h, { rowCount: _, checksum: v } = yield* Lt("export", o, a, i, (e) => e.rowCount, cn($t(g, s), t.bundleFile(d), t.compression));
|
|
727
755
|
t.ledger.tables[o] = {
|
|
728
756
|
rowCount: _,
|
|
729
757
|
checksum: v
|
|
@@ -745,82 +773,81 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
745
773
|
});
|
|
746
774
|
}
|
|
747
775
|
return r;
|
|
748
|
-
}),
|
|
776
|
+
}), dn = (e) => ({
|
|
749
777
|
_tag: "RawSqlFragment",
|
|
750
778
|
strings: [e],
|
|
751
779
|
values: []
|
|
752
|
-
}),
|
|
780
|
+
}), fn = (t, i, a) => n.tryPromise({
|
|
753
781
|
try: () => t.transactional(async (t) => {
|
|
754
|
-
if (t.raw) for (let e of
|
|
782
|
+
if (t.raw) for (let e of we(i)) await t.raw(dn(e));
|
|
755
783
|
let o = await n.runPromiseExit(a(t));
|
|
756
784
|
if (r.isFailure(o)) throw Error(e.pretty(o.cause));
|
|
757
785
|
return o.value;
|
|
758
786
|
}),
|
|
759
787
|
catch: (e) => e instanceof H ? e : new H({ reason: `snapshot export failed: ${Y(e)}` })
|
|
760
|
-
}),
|
|
761
|
-
let t = e.scope ?? { kind: "all" }, r =
|
|
788
|
+
}), pn = (e) => n.gen(function* () {
|
|
789
|
+
let t = e.scope ?? { kind: "all" }, r = gt(e.compression), i = e.chunkSize ?? 1e3, a = e.retryTimes ?? 5, o = s.exponential("50 millis").pipe(s.jittered, s.intersect(s.recurs(a))), c = yield* n.promise(() => import("node:path")), l = (t) => c.join(e.outDir, t);
|
|
762
790
|
yield* n.tryPromise({
|
|
763
791
|
try: async () => {
|
|
764
792
|
let t = await import("node:fs/promises");
|
|
765
|
-
await t.mkdir(
|
|
793
|
+
await t.mkdir(l(Ot), { recursive: !0 }), e.assets && await t.mkdir(l(kt), { recursive: !0 });
|
|
766
794
|
},
|
|
767
795
|
catch: (t) => new H({
|
|
768
796
|
reason: `mkdir failed: ${Y(t)}`,
|
|
769
797
|
path: e.outDir
|
|
770
798
|
})
|
|
771
799
|
});
|
|
772
|
-
let
|
|
800
|
+
let u = l(jt), d = yield* zt(u), f = e.subset ? rt(yield* nt(e.store, e.snapshot, e.subset), e.snapshot) : yield* at(t, e.snapshot, {
|
|
773
801
|
store: e.store,
|
|
774
802
|
...e.tenantTables ? { tenantTables: e.tenantTables } : {}
|
|
775
|
-
}),
|
|
803
|
+
}), p = tn(f.tables, e.snapshot), m = e.consistency ?? "live", h, g;
|
|
776
804
|
if (e.masking) {
|
|
777
|
-
let t = e.classification ??
|
|
805
|
+
let t = e.classification ?? Ee(e.snapshot), r = ze(p.map((t) => ({
|
|
778
806
|
name: t,
|
|
779
807
|
columns: (e.snapshot.tables.find((e) => e.name === t)?.columns ?? []).map((e) => ({
|
|
780
808
|
name: e.name,
|
|
781
809
|
type: e.type
|
|
782
810
|
}))
|
|
783
811
|
})), t, e.masking);
|
|
784
|
-
if (r.unclassified.length > 0 || r.invalid.length > 0) return yield* n.fail(new
|
|
812
|
+
if (r.unclassified.length > 0 || r.invalid.length > 0) return yield* n.fail(new Qe({
|
|
785
813
|
unclassified: r.unclassified,
|
|
786
814
|
...r.invalid.length > 0 ? { invalidActions: r.invalid.map((e) => `${e.column}: ${JSON.stringify(e.action)}`) } : {}
|
|
787
815
|
}));
|
|
788
|
-
|
|
816
|
+
g = {
|
|
789
817
|
actions: r.actions,
|
|
790
818
|
seed: e.masking.seed
|
|
791
819
|
};
|
|
792
820
|
let i = Object.entries(r.actions).filter(([, e]) => e !== "keep").map(([e]) => e);
|
|
793
|
-
|
|
821
|
+
h = {
|
|
794
822
|
...e.masking.id ? { policyId: e.masking.id } : {},
|
|
795
823
|
transformed: i
|
|
796
824
|
};
|
|
797
825
|
}
|
|
798
|
-
let
|
|
826
|
+
let _ = {
|
|
799
827
|
snapshot: e.snapshot,
|
|
800
|
-
ordered:
|
|
801
|
-
resolved:
|
|
828
|
+
ordered: p,
|
|
829
|
+
resolved: f,
|
|
802
830
|
compression: r,
|
|
803
831
|
chunkSize: i,
|
|
804
832
|
retry: o,
|
|
805
|
-
ledger:
|
|
806
|
-
ledgerPath:
|
|
807
|
-
bundleFile:
|
|
808
|
-
...
|
|
833
|
+
ledger: d,
|
|
834
|
+
ledgerPath: u,
|
|
835
|
+
bundleFile: l,
|
|
836
|
+
...g ? { mask: g } : {},
|
|
809
837
|
...e.onProgress ? { onProgress: e.onProgress } : {}
|
|
810
|
-
},
|
|
838
|
+
}, v = m === "snapshot" ? yield* fn(e.store, e.dialect, (e) => un(e, _)) : yield* un(e.store, _), y = 0, b = 0;
|
|
811
839
|
if (e.assets) {
|
|
812
|
-
let t =
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
}))).pipe(n.mapError((e) => e instanceof H ? e : new H({ reason: `asset export failed: ${String(e)}` })));
|
|
840
|
+
let t = l(At), r = yield* ft({
|
|
841
|
+
source: e.assets,
|
|
842
|
+
assetsDir: l(kt),
|
|
843
|
+
progress: {
|
|
844
|
+
done: (e) => e in d.assets,
|
|
845
|
+
mark: (e, r) => (d.assets[e] = !0, ln(t, JSON.stringify(r)).pipe(n.andThen(J(u, d))))
|
|
846
|
+
}
|
|
847
|
+
});
|
|
848
|
+
y = r.count, b = r.totalBytes;
|
|
822
849
|
}
|
|
823
|
-
let
|
|
850
|
+
let x = {
|
|
824
851
|
formatVersion: 1,
|
|
825
852
|
createdAt: e.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
826
853
|
source: {
|
|
@@ -828,22 +855,22 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
828
855
|
schemaFingerprint: re(e.snapshot)
|
|
829
856
|
},
|
|
830
857
|
scope: t,
|
|
831
|
-
consistency:
|
|
858
|
+
consistency: m,
|
|
832
859
|
compression: r,
|
|
833
|
-
tables:
|
|
834
|
-
assets:
|
|
835
|
-
count:
|
|
836
|
-
totalBytes:
|
|
837
|
-
index:
|
|
860
|
+
tables: v,
|
|
861
|
+
assets: y > 0 ? {
|
|
862
|
+
count: y,
|
|
863
|
+
totalBytes: b,
|
|
864
|
+
index: At
|
|
838
865
|
} : {
|
|
839
866
|
count: 0,
|
|
840
867
|
totalBytes: 0
|
|
841
868
|
},
|
|
842
|
-
...
|
|
869
|
+
...h ? { masking: h } : {}
|
|
843
870
|
};
|
|
844
871
|
return yield* n.tryPromise({
|
|
845
872
|
try: async () => {
|
|
846
|
-
await (await import("node:fs/promises")).writeFile(
|
|
873
|
+
await (await import("node:fs/promises")).writeFile(l(K), Tt(x));
|
|
847
874
|
},
|
|
848
875
|
catch: (e) => new H({
|
|
849
876
|
reason: `manifest write failed: ${Y(e)}`,
|
|
@@ -852,14 +879,14 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
852
879
|
}), yield* n.tryPromise({
|
|
853
880
|
try: async () => {
|
|
854
881
|
let e = await import("node:fs/promises"), t = await import("node:os");
|
|
855
|
-
await e.writeFile(
|
|
882
|
+
await e.writeFile(l(Dt), Nt(x, { hostname: t.hostname() }));
|
|
856
883
|
},
|
|
857
884
|
catch: (e) => new H({
|
|
858
885
|
reason: `readme write failed: ${Y(e)}`,
|
|
859
|
-
path:
|
|
886
|
+
path: Dt
|
|
860
887
|
})
|
|
861
|
-
}),
|
|
862
|
-
}),
|
|
888
|
+
}), x;
|
|
889
|
+
}), mn = (e, t) => {
|
|
863
890
|
let n = new Set(t), r = [];
|
|
864
891
|
for (let t of e.tables) if (!n.has(t.name)) for (let e of t.columns) {
|
|
865
892
|
let i = e.references?.table;
|
|
@@ -870,7 +897,7 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
870
897
|
});
|
|
871
898
|
}
|
|
872
899
|
return r;
|
|
873
|
-
},
|
|
900
|
+
}, hn = (e) => {
|
|
874
901
|
let t = /* @__PURE__ */ new Map();
|
|
875
902
|
for (let n of e) t.set(n.from, [...t.get(n.from) ?? [], n]);
|
|
876
903
|
let n = [...t.entries()].map(([e, t]) => {
|
|
@@ -878,7 +905,7 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
878
905
|
return ` ${e} (${n} row${n === 1 ? "" : "s"}): ${r}`;
|
|
879
906
|
});
|
|
880
907
|
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.`;
|
|
881
|
-
},
|
|
908
|
+
}, gn = (e) => {
|
|
882
909
|
switch (e) {
|
|
883
910
|
case "id":
|
|
884
911
|
case "text":
|
|
@@ -897,12 +924,12 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
897
924
|
case "raw": return "raw";
|
|
898
925
|
default: return "string";
|
|
899
926
|
}
|
|
900
|
-
},
|
|
927
|
+
}, _n = {
|
|
901
928
|
vector: /* @__PURE__ */ new Set(["postgres"]),
|
|
902
929
|
array: /* @__PURE__ */ new Set(["postgres"]),
|
|
903
930
|
interval: /* @__PURE__ */ new Set(["postgres"])
|
|
904
|
-
},
|
|
905
|
-
let i =
|
|
931
|
+
}, vn = (e, t, n, r) => {
|
|
932
|
+
let i = gn(n);
|
|
906
933
|
if (i === "raw") return {
|
|
907
934
|
table: e,
|
|
908
935
|
column: t,
|
|
@@ -910,7 +937,7 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
910
937
|
severity: "block",
|
|
911
938
|
reason: `raw() is verbatim source-dialect SQL — it cannot be assumed valid on ${r}`
|
|
912
939
|
};
|
|
913
|
-
let a =
|
|
940
|
+
let a = _n[i];
|
|
914
941
|
return !a || a.has(r) ? null : i === "array" || i === "interval" ? {
|
|
915
942
|
table: e,
|
|
916
943
|
column: t,
|
|
@@ -924,15 +951,15 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
924
951
|
severity: "block",
|
|
925
952
|
reason: `${i} columns require a ${[...a].join("/")} target; ${r} has no compatible column type`
|
|
926
953
|
};
|
|
927
|
-
},
|
|
954
|
+
}, yn = (e, t, n) => {
|
|
928
955
|
if (t === n) return [];
|
|
929
956
|
let r = [];
|
|
930
957
|
for (let t of e) for (let [e, i] of Object.entries(t.columnTypes)) {
|
|
931
|
-
let a =
|
|
958
|
+
let a = vn(t.name, e, i, n);
|
|
932
959
|
a && r.push(a);
|
|
933
960
|
}
|
|
934
961
|
return r;
|
|
935
|
-
},
|
|
962
|
+
}, bn = (e) => `[${e.severity}] ${e.table}.${e.column} (${e.type}): ${e.reason}`, xn = (e) => {
|
|
936
963
|
let t = e.constraint ?? e.column, n = t === void 0 ? "" : ` ${t}`;
|
|
937
964
|
switch (e.kind) {
|
|
938
965
|
case "foreignKey": return `foreign key${n}: the referenced row does not exist (import it first, or check the bundle's table order)`;
|
|
@@ -941,35 +968,35 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
941
968
|
case "notNull": return `not-null constraint${n}: the column requires a value`;
|
|
942
969
|
case "check": return `check constraint${n}: the row does not satisfy it`;
|
|
943
970
|
}
|
|
944
|
-
},
|
|
971
|
+
}, Sn = (e) => {
|
|
945
972
|
let t = [e.dbCode, e.dbErrno ?? e.dbNumber].filter((e) => e !== void 0).map(String);
|
|
946
973
|
return t.length > 0 ? t.join("/") : void 0;
|
|
947
|
-
},
|
|
974
|
+
}, Cn = "Failed to execute statement", wn = (e) => {
|
|
948
975
|
let t = (e.split("\n", 1)[0] ?? "").trim();
|
|
949
976
|
for (; t.startsWith("(FiberFailure)");) t = t.slice(14).trim();
|
|
950
|
-
return t === `SqlError: ${
|
|
951
|
-
},
|
|
977
|
+
return t === `SqlError: ${Cn}` ? Cn : t;
|
|
978
|
+
}, Tn = (e) => {
|
|
952
979
|
let t = String(e?.stack ?? e), n = t.length <= 1200 ? t : `${t.slice(0, 1200)}…`, r = (e, t) => ({
|
|
953
980
|
reason: e.length <= 300 ? e : `${e.slice(0, 300)}…`,
|
|
954
981
|
unclassified: t,
|
|
955
982
|
raw: n
|
|
956
|
-
}), i = _(e), a = p(i), o =
|
|
957
|
-
if (a !== void 0) return r(`${
|
|
958
|
-
let c = typeof i.dbMessage == "string" ? i.dbMessage : void 0, l = e?.message, u =
|
|
959
|
-
return r(d ===
|
|
960
|
-
},
|
|
983
|
+
}), i = _(e), a = p(i), o = Sn(i), s = o === void 0 ? "" : ` [${o}]`;
|
|
984
|
+
if (a !== void 0) return r(`${xn(a)}${s}`, !1);
|
|
985
|
+
let c = typeof i.dbMessage == "string" ? i.dbMessage : void 0, l = e?.message, u = wn(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})` : "";
|
|
986
|
+
return r(d === Cn || d.length === 0 ? `the database refused the write and the driver gave no detail${s}${m}` : `${d}${s}`, d === Cn || d.length === 0);
|
|
987
|
+
}, En = (e) => Tn(e).reason, Dn = (e) => {
|
|
961
988
|
let t = _(e), n = p(t);
|
|
962
989
|
if (n?.kind === "foreignKeyInUse") {
|
|
963
|
-
let e = n.constraint ?? n.column, r = e === void 0 ? "" : ` ${e}`, i =
|
|
990
|
+
let e = n.constraint ?? n.column, r = e === void 0 ? "" : ` ${e}`, i = Sn(t);
|
|
964
991
|
return `foreign key${r}: rows elsewhere still reference this table${i === void 0 ? "" : ` [${i}]`}`;
|
|
965
992
|
}
|
|
966
|
-
return
|
|
967
|
-
},
|
|
993
|
+
return En(e);
|
|
994
|
+
}, On = "_voltro_staging_", X = (e) => `${On}${e}`, kn = (e) => typeof e.run == "function", An = (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
|
|
968
995
|
process that dies mid-load depends on the database rolling it back. The rollback capture
|
|
969
|
-
written before the first delete is what covers that case.`,
|
|
970
|
-
if (!
|
|
996
|
+
written before the first delete is what covers that case.`, jn = /^[a-zA-Z_][a-zA-Z0-9_]*$/, Z = (e, t) => {
|
|
997
|
+
if (!jn.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.`);
|
|
971
998
|
return t === "mysql" || t === "mariadb" ? `\`${e}\`` : t === "mssql" ? `[${e}]` : `"${e}"`;
|
|
972
|
-
},
|
|
999
|
+
}, Mn = (e, t) => {
|
|
973
1000
|
let n = Z(e, t), r = Z(X(e), t);
|
|
974
1001
|
switch (t) {
|
|
975
1002
|
case "postgres": return `CREATE TABLE ${r} (LIKE ${n})`;
|
|
@@ -978,10 +1005,10 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
978
1005
|
case "sqlite": return `CREATE TABLE ${r} AS SELECT * FROM ${n} WHERE 0`;
|
|
979
1006
|
case "mssql": return `SELECT * INTO ${r} FROM ${n} WHERE 1 = 0`;
|
|
980
1007
|
}
|
|
981
|
-
},
|
|
1008
|
+
}, Nn = (e, t) => {
|
|
982
1009
|
let n = Z(X(e), t);
|
|
983
1010
|
return t === "mssql" ? `IF OBJECT_ID('${X(e)}', 'U') IS NOT NULL DROP TABLE ${n}` : `DROP TABLE IF EXISTS ${n}`;
|
|
984
|
-
},
|
|
1011
|
+
}, Pn = (e, t) => {
|
|
985
1012
|
let n = t === "mysql" || t === "mariadb", r = [];
|
|
986
1013
|
n && r.push("SET FOREIGN_KEY_CHECKS = 0");
|
|
987
1014
|
for (let n of [...e].reverse()) r.push(`DELETE FROM ${Z(n.name, t)}`);
|
|
@@ -991,7 +1018,7 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
991
1018
|
r.push(`INSERT INTO ${Z(n.name, t)} (${e}) SELECT ${e} FROM ${Z(X(n.name), t)}`);
|
|
992
1019
|
}
|
|
993
1020
|
return n && r.push("SET FOREIGN_KEY_CHECKS = 1"), r;
|
|
994
|
-
},
|
|
1021
|
+
}, Fn = (e, t) => {
|
|
995
1022
|
let n = [], r = () => {
|
|
996
1023
|
for (let e of n) t.unregisterTable(e);
|
|
997
1024
|
};
|
|
@@ -1013,7 +1040,7 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
1013
1040
|
staged: n,
|
|
1014
1041
|
undo: r
|
|
1015
1042
|
};
|
|
1016
|
-
},
|
|
1043
|
+
}, In = (e, t) => {
|
|
1017
1044
|
let n = new Set(t), r = [];
|
|
1018
1045
|
for (let t of e.tables) {
|
|
1019
1046
|
if (!n.has(t.name)) continue;
|
|
@@ -1030,22 +1057,22 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
1030
1057
|
}
|
|
1031
1058
|
}
|
|
1032
1059
|
return r;
|
|
1033
|
-
},
|
|
1060
|
+
}, Ln = (e, t) => {
|
|
1034
1061
|
let n = Z(X(e.from), t), r = Z(X(e.to), t), i = Z(e.column, t), a = Z(e.toColumn, t);
|
|
1035
1062
|
return `SELECT ${Z(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})`;
|
|
1036
|
-
},
|
|
1063
|
+
}, Rn = (e) => {
|
|
1037
1064
|
if (e.length === 0) return;
|
|
1038
1065
|
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}`);
|
|
1039
1066
|
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.";
|
|
1040
|
-
},
|
|
1041
|
-
t.length !== 0 && await e.run(
|
|
1042
|
-
},
|
|
1043
|
-
let i =
|
|
1044
|
-
i.length !== 0 && await e.run(
|
|
1045
|
-
},
|
|
1067
|
+
}, zn = async (e, t) => {
|
|
1068
|
+
t.length !== 0 && await e.run(_e.SqlClient.pipe(n.flatMap((e) => n.forEach(t, (t) => e.unsafe(t), { discard: !0 }))));
|
|
1069
|
+
}, Bn = async (e, t, r) => {
|
|
1070
|
+
let i = Pn(t, r);
|
|
1071
|
+
i.length !== 0 && await e.run(_e.SqlClient.pipe(n.flatMap((e) => e.withTransaction(n.forEach(i, (t) => e.unsafe(t), { discard: !0 })))));
|
|
1072
|
+
}, Vn = async (e, t, r) => {
|
|
1046
1073
|
let i = [];
|
|
1047
1074
|
for (let a of t) {
|
|
1048
|
-
let t = await e.run(
|
|
1075
|
+
let t = await e.run(_e.SqlClient.pipe(n.flatMap((e) => e.unsafe(Ln(a, r)))));
|
|
1049
1076
|
for (let e of t) i.push({
|
|
1050
1077
|
table: a.from,
|
|
1051
1078
|
primaryKey: String(e.pk ?? ""),
|
|
@@ -1055,14 +1082,14 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
1055
1082
|
});
|
|
1056
1083
|
}
|
|
1057
1084
|
return i;
|
|
1058
|
-
},
|
|
1085
|
+
}, Hn = (e, t) => {
|
|
1059
1086
|
let n = new Map(t.map((e, t) => [e, t]));
|
|
1060
1087
|
return e.filter((e) => {
|
|
1061
1088
|
if (e.from === e.to) return !1;
|
|
1062
1089
|
let t = n.get(e.from), r = n.get(e.to);
|
|
1063
1090
|
return t === void 0 || r === void 0 ? !1 : r > t;
|
|
1064
1091
|
});
|
|
1065
|
-
},
|
|
1092
|
+
}, Un = (e) => e === "mysql" || e === "mariadb", Wn = (e) => {
|
|
1066
1093
|
if (e.mode !== "replace") return {
|
|
1067
1094
|
active: !1,
|
|
1068
1095
|
tables: [],
|
|
@@ -1112,7 +1139,7 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
1112
1139
|
tables: [],
|
|
1113
1140
|
blocker: "every table in the bundle is one that describes a deployment, so there is nothing for a staged swap to move."
|
|
1114
1141
|
};
|
|
1115
|
-
let s = o.map((e) => e.name), c =
|
|
1142
|
+
let s = o.map((e) => e.name), c = Un(t) ? [] : Hn(In(n, s), s);
|
|
1116
1143
|
return c.length > 0 ? {
|
|
1117
1144
|
active: !1,
|
|
1118
1145
|
tables: [],
|
|
@@ -1121,7 +1148,7 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
1121
1148
|
active: !0,
|
|
1122
1149
|
tables: o
|
|
1123
1150
|
};
|
|
1124
|
-
},
|
|
1151
|
+
}, Gn = (e) => n.gen(function* () {
|
|
1125
1152
|
if (e.mode !== "replace" || e.dir === void 0 || e.tables.length === 0) return;
|
|
1126
1153
|
if (e.snapshot === void 0) return yield* n.fail(new H({
|
|
1127
1154
|
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.",
|
|
@@ -1134,7 +1161,7 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
1134
1161
|
if (t.tables.length === 0) return;
|
|
1135
1162
|
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,
|
|
1136
1163
|
so it survives a run whose process does not.`);
|
|
1137
|
-
let r = yield*
|
|
1164
|
+
let r = yield* pn({
|
|
1138
1165
|
store: e.store,
|
|
1139
1166
|
snapshot: t,
|
|
1140
1167
|
dialect: e.dialect ?? "unknown",
|
|
@@ -1150,18 +1177,18 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
1150
1177
|
rows: r.tables.reduce((e, t) => e + t.rowCount, 0),
|
|
1151
1178
|
tables: r.tables.length
|
|
1152
1179
|
};
|
|
1153
|
-
}),
|
|
1180
|
+
}), Kn = (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`, qn = (e, t) => e.tables.find((e) => e.name === t), Jn = (e, t) => new Map((qn(e, t)?.columns ?? []).map((e) => [e.name, e])), Yn = /__dropped_\d+$/, Xn = (e, t) => {
|
|
1154
1181
|
let n = [];
|
|
1155
1182
|
for (let r of [...e.tables].sort((e, t) => e.name.localeCompare(t.name))) {
|
|
1156
|
-
if (
|
|
1183
|
+
if (qn(t, r.name) === void 0) {
|
|
1157
1184
|
n.push(`table '${r.name}' is in the bundle but MISSING from the target — its rows have nowhere to go`);
|
|
1158
1185
|
continue;
|
|
1159
1186
|
}
|
|
1160
|
-
let e =
|
|
1187
|
+
let e = Jn(t, r.name), i = new Map(r.columns.map((e) => [e.name, e]));
|
|
1161
1188
|
for (let [t, a] of i) {
|
|
1162
1189
|
let i = e.get(t);
|
|
1163
1190
|
if (i === void 0) {
|
|
1164
|
-
n.push(
|
|
1191
|
+
n.push(Yn.test(t) ? `${r.name}.${t}: 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` : `${r.name}.${t}: in the bundle, MISSING from the target — the value has nowhere to go`);
|
|
1165
1192
|
continue;
|
|
1166
1193
|
}
|
|
1167
1194
|
i.type !== a.type && n.push(`${r.name}.${t}: type '${a.type}' (bundle) vs '${i.type}' (target)`);
|
|
@@ -1169,7 +1196,7 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
1169
1196
|
for (let [t, a] of e) i.has(t) || !a.nullable && !a.hasDefault && n.push(`${r.name}.${t}: the target requires it (NOT NULL, no default) and the bundle carries no value — every row of this table would fail`);
|
|
1170
1197
|
}
|
|
1171
1198
|
return n.length > 30 ? [...n.slice(0, 30), `… and ${n.length - 30} more difference(s)`] : n;
|
|
1172
|
-
},
|
|
1199
|
+
}, Zn = (e, t, n) => {
|
|
1173
1200
|
let r = re(t);
|
|
1174
1201
|
if (r === e || n === void 0) return {
|
|
1175
1202
|
drifted: !1,
|
|
@@ -1177,14 +1204,14 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
1177
1204
|
targetFingerprint: r,
|
|
1178
1205
|
diff: []
|
|
1179
1206
|
};
|
|
1180
|
-
let i =
|
|
1207
|
+
let i = Xn(n, t);
|
|
1181
1208
|
return {
|
|
1182
1209
|
drifted: i.length > 0,
|
|
1183
1210
|
bundleFingerprint: e,
|
|
1184
1211
|
targetFingerprint: r,
|
|
1185
1212
|
diff: i
|
|
1186
1213
|
};
|
|
1187
|
-
},
|
|
1214
|
+
}, Qn = 2e3, $n = 200, er = /* @__PURE__ */ new Set([
|
|
1188
1215
|
"BundleError",
|
|
1189
1216
|
"CodecError",
|
|
1190
1217
|
"IntegrityError",
|
|
@@ -1193,29 +1220,29 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
1193
1220
|
"RowsRefusedError",
|
|
1194
1221
|
"SchemaDriftError",
|
|
1195
1222
|
"StorageError"
|
|
1196
|
-
]),
|
|
1223
|
+
]), tr = (t) => {
|
|
1197
1224
|
let n = o.isFiberFailure(t) ? [...e.failures(t[o.FiberFailureCauseId]), ...e.defects(t[o.FiberFailureCauseId])] : [t];
|
|
1198
1225
|
for (let e of n) {
|
|
1199
1226
|
let t = e?._tag;
|
|
1200
|
-
if (typeof t == "string" &&
|
|
1227
|
+
if (typeof t == "string" && er.has(t)) return e;
|
|
1201
1228
|
if (o.isFiberFailure(e)) {
|
|
1202
|
-
let t =
|
|
1229
|
+
let t = tr(e);
|
|
1203
1230
|
if (t) return t;
|
|
1204
1231
|
}
|
|
1205
1232
|
}
|
|
1206
|
-
},
|
|
1233
|
+
}, nr = (t) => [...e.failures(t)][0] ?? Error(e.pretty(t)), rr = (e) => n.tryPromise({
|
|
1207
1234
|
try: async () => {
|
|
1208
1235
|
let t = await import("node:fs/promises"), n = await import("node:path");
|
|
1209
|
-
return
|
|
1236
|
+
return Et(await t.readFile(n.join(e, K), "utf8"));
|
|
1210
1237
|
},
|
|
1211
1238
|
catch: (e) => new H({
|
|
1212
1239
|
reason: `manifest read/parse failed: ${String(e?.message ?? e)}`,
|
|
1213
1240
|
path: K
|
|
1214
1241
|
})
|
|
1215
|
-
}),
|
|
1242
|
+
}), ir = (e) => {
|
|
1216
1243
|
let t;
|
|
1217
1244
|
return n.gen(function* () {
|
|
1218
|
-
let i = yield* n.promise(() => import("node:path")), a = yield*
|
|
1245
|
+
let i = yield* n.promise(() => import("node:path")), a = yield* rr(e.bundleDir);
|
|
1219
1246
|
if (a.formatVersion !== 1) return yield* n.fail(new H({ reason: `unsupported bundle formatVersion ${a.formatVersion} (expected 1)` }));
|
|
1220
1247
|
if (e.tables !== void 0) {
|
|
1221
1248
|
let t = new Set(a.tables.map((e) => e.name)), r = e.tables.filter((e) => !t.has(e));
|
|
@@ -1236,9 +1263,9 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
1236
1263
|
unique: !1,
|
|
1237
1264
|
hasDefault: !1
|
|
1238
1265
|
}))
|
|
1239
|
-
})) }, r =
|
|
1266
|
+
})) }, r = Zn(s.source.schemaFingerprint, e.targetSnapshot, t);
|
|
1240
1267
|
if (r.drifted) {
|
|
1241
|
-
if (!e.force) return yield* n.fail(new
|
|
1268
|
+
if (!e.force) return yield* n.fail(new Ze({
|
|
1242
1269
|
bundleFingerprint: r.bundleFingerprint,
|
|
1243
1270
|
targetFingerprint: r.targetFingerprint,
|
|
1244
1271
|
diff: r.diff
|
|
@@ -1247,15 +1274,15 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
1247
1274
|
}
|
|
1248
1275
|
}
|
|
1249
1276
|
if (e.targetDialect && e.targetDialect !== s.source.dialect) {
|
|
1250
|
-
let t =
|
|
1251
|
-
if (t.length > 0 && !e.allowIncompatible) return yield* n.fail(new
|
|
1277
|
+
let t = yn(s.tables, s.source.dialect, e.targetDialect).filter((e) => e.severity === "block");
|
|
1278
|
+
if (t.length > 0 && !e.allowIncompatible) return yield* n.fail(new et({
|
|
1252
1279
|
sourceDialect: s.source.dialect,
|
|
1253
1280
|
targetDialect: e.targetDialect,
|
|
1254
|
-
blocking: t.map(
|
|
1281
|
+
blocking: t.map(bn)
|
|
1255
1282
|
}));
|
|
1256
1283
|
}
|
|
1257
|
-
let c = 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*
|
|
1258
|
-
if (p === "replace" && s.scope.kind !== "all") return yield* n.fail(new
|
|
1284
|
+
let c = 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* zt(y);
|
|
1285
|
+
if (p === "replace" && s.scope.kind !== "all") return yield* n.fail(new $e({
|
|
1259
1286
|
mode: p,
|
|
1260
1287
|
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.`
|
|
1261
1288
|
}));
|
|
@@ -1267,8 +1294,8 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
1267
1294
|
if (p === "replace") {
|
|
1268
1295
|
let t = s.tables.map((e) => e.name), r = e.store.incomingForeignKeys ? yield* n.tryPromise({
|
|
1269
1296
|
try: () => e.store.incomingForeignKeys(t),
|
|
1270
|
-
catch: (e) => new H({ reason: `foreign-key pre-flight failed: ${
|
|
1271
|
-
}) : e.targetSnapshot === void 0 ? [] :
|
|
1297
|
+
catch: (e) => new H({ reason: `foreign-key pre-flight failed: ${Dn(e)}` })
|
|
1298
|
+
}) : e.targetSnapshot === void 0 ? [] : mn(e.targetSnapshot, t), i = [], a = /* @__PURE__ */ new Map();
|
|
1272
1299
|
for (let t of r) {
|
|
1273
1300
|
if (!a.has(t.from)) {
|
|
1274
1301
|
let r = yield* n.tryPromise({
|
|
@@ -1290,9 +1317,9 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
1290
1317
|
rowCount: r
|
|
1291
1318
|
});
|
|
1292
1319
|
}
|
|
1293
|
-
if (i.length > 0) return yield* n.fail(new
|
|
1320
|
+
if (i.length > 0) return yield* n.fail(new $e({
|
|
1294
1321
|
mode: p,
|
|
1295
|
-
reason:
|
|
1322
|
+
reason: hn(i)
|
|
1296
1323
|
}));
|
|
1297
1324
|
}
|
|
1298
1325
|
if (e.dryRun === !0) return e.traceId !== void 0 && (yield* n.promise(() => e.store.update(u, e.traceId, {
|
|
@@ -1320,14 +1347,14 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
1320
1347
|
for (let e of n) e in t && (r ??= { ...t }, delete r[e]);
|
|
1321
1348
|
return r ?? t;
|
|
1322
1349
|
}, T = (e, t, n, r = !1) => {
|
|
1323
|
-
let i =
|
|
1350
|
+
let i = k ? X(t.name) : t.name, a = t.primaryKey, o = re(t.name, n);
|
|
1324
1351
|
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] });
|
|
1325
|
-
},
|
|
1326
|
-
r.unclassified && (
|
|
1327
|
-
},
|
|
1352
|
+
}, E = (e, t) => e.savepoint ? e.savepoint(t) : t(), D = 0, ie = (t, n, r) => {
|
|
1353
|
+
r.unclassified && (D++, !(D > 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}` + (D === 3 ? " (further unclassified refusals are counted, not logged)" : "")));
|
|
1354
|
+
}, ae = (e, t, r = !0) => n.tryPromise({
|
|
1328
1355
|
try: e,
|
|
1329
|
-
catch: (e) =>
|
|
1330
|
-
}).pipe(n.map(() => !0), n.catchAll((e) => n.sync(() => (r && (t.reason = e.reason,
|
|
1356
|
+
catch: (e) => Tn(e)
|
|
1357
|
+
}).pipe(n.map(() => !0), n.catchAll((e) => n.sync(() => (r && (t.reason = e.reason, ie(t.entry, t.row, e)), !1)))), oe = (e) => {
|
|
1331
1358
|
let t = Object.entries(e.entry.columnTypes).filter(([t, n]) => n === "reference" && e.row[t] !== null && e.row[t] !== void 0).map(([e]) => e);
|
|
1332
1359
|
if (t.length !== 0) return {
|
|
1333
1360
|
...e.row,
|
|
@@ -1351,7 +1378,7 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
1351
1378
|
};
|
|
1352
1379
|
t.refused++, e.derived || t.primary++, a.set(e.table, t);
|
|
1353
1380
|
}
|
|
1354
|
-
return new
|
|
1381
|
+
return new Xe({
|
|
1355
1382
|
rows: i.slice(0, 20),
|
|
1356
1383
|
primaryCount: r.filter((e) => !e.derived).length,
|
|
1357
1384
|
totalCount: e.length,
|
|
@@ -1361,38 +1388,38 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
1361
1388
|
primary: t.primary
|
|
1362
1389
|
})).sort((e, t) => t.refused - e.refused || e.table.localeCompare(t.table)).slice(0, 100)
|
|
1363
1390
|
});
|
|
1364
|
-
},
|
|
1391
|
+
}, ce = (e, t) => n.gen(function* () {
|
|
1365
1392
|
let r = t, i = [], a = !0;
|
|
1366
1393
|
for (; r.length > 0 && a;) {
|
|
1367
1394
|
a = !1;
|
|
1368
1395
|
let t = [];
|
|
1369
|
-
for (let n of r) (yield*
|
|
1396
|
+
for (let n of r) (yield* ae(() => E(e, () => T(e, n.entry, n.row, !0)), n)) ? a = !0 : t.push(n);
|
|
1370
1397
|
if (!a && t.length > 0) for (let n = t.length - 1; n >= 0; n--) {
|
|
1371
1398
|
let r = t[n], o = oe(r);
|
|
1372
|
-
o !== void 0 && (yield*
|
|
1399
|
+
o !== void 0 && (yield* ae(() => E(e, () => T(e, r.entry, o, !0)), r, !1)) && (i.push(r), t.splice(n, 1), a = !0);
|
|
1373
1400
|
}
|
|
1374
1401
|
r = t;
|
|
1375
1402
|
}
|
|
1376
|
-
for (let t of i) (yield*
|
|
1403
|
+
for (let t of i) (yield* ae(() => E(e, () => e.upsert(t.entry.name, t.row, { conflictColumns: [t.entry.primaryKey] })), t)) || (r = [...r, t]);
|
|
1377
1404
|
if (r.length > 0) return yield* n.fail(se(r));
|
|
1378
1405
|
}), O = (() => {
|
|
1379
1406
|
let t = e.targetDialect;
|
|
1380
1407
|
return t === "postgres" || t === "mysql" || t === "mariadb" || t === "sqlite" || t === "mssql" ? t : void 0;
|
|
1381
|
-
})(),
|
|
1408
|
+
})(), ue = Wn({
|
|
1382
1409
|
mode: p,
|
|
1383
1410
|
...e.targetDialect === void 0 ? {} : { targetDialect: e.targetDialect },
|
|
1384
1411
|
...e.targetSnapshot === void 0 ? {} : { targetSnapshot: e.targetSnapshot },
|
|
1385
1412
|
bundleTables: s.tables.map((e) => e.name),
|
|
1386
1413
|
isSkipped: V,
|
|
1387
|
-
canRunSql:
|
|
1388
|
-
}),
|
|
1389
|
-
if (p === "replace" && !
|
|
1414
|
+
canRunSql: kn(e.store)
|
|
1415
|
+
}), de = ue.tables, fe = s.tables.filter((e) => !V(e.name) && S(e.name)).map((e) => e.name), k = ue.active, pe = !1, A = 0, j = 0, he, M = 0, N = 0, ge = (t, r) => n.gen(function* () {
|
|
1416
|
+
if (p === "replace" && !k && !(r && x.truncated)) {
|
|
1390
1417
|
e.recordInterruption === !0 && (yield* n.tryPromise({
|
|
1391
1418
|
try: () => t.insert(d, {
|
|
1392
|
-
id: m("repl",
|
|
1393
|
-
startedAt:
|
|
1419
|
+
id: m("repl", _e),
|
|
1420
|
+
startedAt: P,
|
|
1394
1421
|
tables: String(s.tables.length),
|
|
1395
|
-
capture:
|
|
1422
|
+
capture: F?.dir ?? null,
|
|
1396
1423
|
via: e.via ?? "direct"
|
|
1397
1424
|
}),
|
|
1398
1425
|
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
|
|
@@ -1405,7 +1432,7 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
1405
1432
|
});
|
|
1406
1433
|
yield* n.tryPromise({
|
|
1407
1434
|
try: o,
|
|
1408
|
-
catch: (e) => new H({ reason: `emptying the target for --mode replace failed (nothing was deleted): ${
|
|
1435
|
+
catch: (e) => new H({ reason: `emptying the target for --mode replace failed (nothing was deleted): ${Dn(e)}` })
|
|
1409
1436
|
}), r && (x.truncated = !0, yield* J(y, x));
|
|
1410
1437
|
}
|
|
1411
1438
|
let a = [], o = [], u = [], f = [], h = s.tables.length;
|
|
@@ -1416,7 +1443,7 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
1416
1443
|
continue;
|
|
1417
1444
|
}
|
|
1418
1445
|
if (r && m.name in x.tables) {
|
|
1419
|
-
u.push(m.name),
|
|
1446
|
+
u.push(m.name), N++, M += x.tables[m.name].rowCount, q(e.onProgress, {
|
|
1420
1447
|
phase: "import",
|
|
1421
1448
|
table: m.name,
|
|
1422
1449
|
index: d,
|
|
@@ -1438,22 +1465,22 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
1438
1465
|
total: m.rowCount
|
|
1439
1466
|
});
|
|
1440
1467
|
let v = a.length, b = n.gen(function* () {
|
|
1441
|
-
let { createHash: r } = yield* n.promise(() => import("node:crypto")), i = r("sha256"), o = 0, c =
|
|
1468
|
+
let { createHash: r } = yield* n.promise(() => import("node:crypto")), i = r("sha256"), o = 0, c = me("bundle", () => le(_).pipe(yt(s.compression))).pipe(l.tap((e) => n.sync(() => i.update(e)))), u = async (e) => {
|
|
1442
1469
|
let n = {
|
|
1443
1470
|
entry: m,
|
|
1444
1471
|
row: e,
|
|
1445
1472
|
reason: ""
|
|
1446
1473
|
};
|
|
1447
1474
|
try {
|
|
1448
|
-
await
|
|
1475
|
+
await E(t, () => T(t, m, e));
|
|
1449
1476
|
} catch (t) {
|
|
1450
|
-
let r =
|
|
1451
|
-
n.reason = r.reason,
|
|
1477
|
+
let r = Tn(t);
|
|
1478
|
+
n.reason = r.reason, ie(m, e, r), a.push(n);
|
|
1452
1479
|
}
|
|
1453
1480
|
}, d = yield* n.promise(async () => {
|
|
1454
1481
|
let n = e.copyLoader;
|
|
1455
1482
|
if (!n || n.dialect !== "postgres") return !1;
|
|
1456
|
-
if (g) return
|
|
1483
|
+
if (g) return pe || (pe = !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;
|
|
1457
1484
|
if (p === "replace") return !0;
|
|
1458
1485
|
if (p !== "upsert") return !1;
|
|
1459
1486
|
try {
|
|
@@ -1495,7 +1522,7 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
1495
1522
|
for (let t of e) await u(t);
|
|
1496
1523
|
}
|
|
1497
1524
|
};
|
|
1498
|
-
return yield* l.runForEach(
|
|
1525
|
+
return yield* l.runForEach(en(c, m.name, m.columnTypes), (e) => n.promise(async () => {
|
|
1499
1526
|
if (o++, d) {
|
|
1500
1527
|
v.push(e), v.length >= h && await y();
|
|
1501
1528
|
return;
|
|
@@ -1509,14 +1536,14 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
1509
1536
|
rowCount: o,
|
|
1510
1537
|
checksum: i.digest("hex")
|
|
1511
1538
|
};
|
|
1512
|
-
}), { rowCount: S, checksum: C } = yield*
|
|
1539
|
+
}), { rowCount: S, checksum: C } = yield* Lt("import", m.name, d, h, (e) => e.rowCount, b);
|
|
1513
1540
|
if (c) {
|
|
1514
|
-
if (C !== m.checksum) return yield* n.fail(new
|
|
1541
|
+
if (C !== m.checksum) return yield* n.fail(new Je({
|
|
1515
1542
|
what: `table ${m.name} checksum`,
|
|
1516
1543
|
expected: m.checksum,
|
|
1517
1544
|
actual: C
|
|
1518
1545
|
}));
|
|
1519
|
-
if (S !== m.rowCount) return yield* n.fail(new
|
|
1546
|
+
if (S !== m.rowCount) return yield* n.fail(new Je({
|
|
1520
1547
|
what: `table ${m.name} rowCount`,
|
|
1521
1548
|
expected: String(m.rowCount),
|
|
1522
1549
|
actual: String(S)
|
|
@@ -1530,7 +1557,7 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
1530
1557
|
event: "done",
|
|
1531
1558
|
rowsDone: S,
|
|
1532
1559
|
total: m.rowCount
|
|
1533
|
-
}),
|
|
1560
|
+
}), j++, A += S, he?.(), r && (a.length > v ? o.push({
|
|
1534
1561
|
name: m.name,
|
|
1535
1562
|
rowCount: S
|
|
1536
1563
|
}) : (x.tables[m.name] = {
|
|
@@ -1538,10 +1565,10 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
1538
1565
|
checksum: m.checksum
|
|
1539
1566
|
}, yield* J(y, x)));
|
|
1540
1567
|
}
|
|
1541
|
-
if (a.length > 0 && (yield*
|
|
1568
|
+
if (a.length > 0 && (yield* ce(t, a)), p === "replace" && (yield* n.promise(() => t.deleteMany(d, { where: {
|
|
1542
1569
|
column: "startedAt",
|
|
1543
1570
|
op: "lte",
|
|
1544
|
-
value:
|
|
1571
|
+
value: P
|
|
1545
1572
|
} }).then(() => void 0, () => void 0))), r && o.length > 0) {
|
|
1546
1573
|
for (let e of o) {
|
|
1547
1574
|
let t = s.tables.find((t) => t.name === e.name);
|
|
@@ -1553,14 +1580,14 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
1553
1580
|
yield* J(y, x);
|
|
1554
1581
|
}
|
|
1555
1582
|
{
|
|
1556
|
-
let t =
|
|
1583
|
+
let t = Ke(f, "skipped");
|
|
1557
1584
|
t !== void 0 && e.onWarn?.(t);
|
|
1558
1585
|
}
|
|
1559
1586
|
if (u.length > 0) {
|
|
1560
1587
|
let t = u.length === s.tables.length;
|
|
1561
1588
|
e.onWarn?.(`resume: ${u.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: ${u.join(", ")}. Delete ${y} to force a full re-import.`);
|
|
1562
1589
|
}
|
|
1563
|
-
}),
|
|
1590
|
+
}), _e = `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8)}`, P = /* @__PURE__ */ new Date(), F = yield* Gn({
|
|
1564
1591
|
store: e.store,
|
|
1565
1592
|
mode: p,
|
|
1566
1593
|
dir: e.rollbackDir,
|
|
@@ -1569,16 +1596,16 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
1569
1596
|
tables: s.tables.map((e) => e.name).filter((e) => !V(e)),
|
|
1570
1597
|
onWarn: e.onWarn
|
|
1571
1598
|
});
|
|
1572
|
-
|
|
1573
|
-
try: () => e.onRollbackCaptured(
|
|
1599
|
+
F !== void 0 && (e.onRollbackCaptured !== void 0 && (yield* n.tryPromise({
|
|
1600
|
+
try: () => e.onRollbackCaptured(F),
|
|
1574
1601
|
catch: (e) => new H({
|
|
1575
1602
|
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.`,
|
|
1576
|
-
path:
|
|
1603
|
+
path: F.dir
|
|
1577
1604
|
})
|
|
1578
|
-
})), e.onWarn?.(
|
|
1579
|
-
let
|
|
1580
|
-
|
|
1581
|
-
id:
|
|
1605
|
+
})), e.onWarn?.(Kn(F)));
|
|
1606
|
+
let ve = (e) => p === "replace" ? ee(e) : e(), I = (e) => p === "replace" ? te(e) : e, L = e.traceId ?? m("dimp", `${Date.now()}-${s.source.schemaFingerprint ?? ""}`), ye = e.traceId !== void 0, R = (e.targetSnapshot?.tables.some((e) => e.name === u) ?? !1) || v(u) !== void 0;
|
|
1607
|
+
R && h(f), R && e.traceId === void 0 && (yield* n.promise(() => e.store.insert(u, {
|
|
1608
|
+
id: L,
|
|
1582
1609
|
mode: p,
|
|
1583
1610
|
via: e.via ?? "direct",
|
|
1584
1611
|
direction: "import",
|
|
@@ -1586,38 +1613,38 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
1586
1613
|
sourceFingerprint: s.source.schemaFingerprint ?? null,
|
|
1587
1614
|
tablesWritten: 0,
|
|
1588
1615
|
rowsWritten: 0,
|
|
1589
|
-
staged: p === "replace" ?
|
|
1616
|
+
staged: p === "replace" ? k : null
|
|
1590
1617
|
}).then(() => {
|
|
1591
|
-
|
|
1618
|
+
ye = !0;
|
|
1592
1619
|
}, (t) => {
|
|
1593
1620
|
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
|
|
1594
1621
|
\`voltro db apply\` so the next one is recorded.`);
|
|
1595
|
-
}))), e.traceId !== void 0 &&
|
|
1596
|
-
staged: p === "replace" ?
|
|
1622
|
+
}))), e.traceId !== void 0 && R && (yield* n.promise(() => e.store.update(u, L, {
|
|
1623
|
+
staged: p === "replace" ? k : null,
|
|
1597
1624
|
sourceFingerprint: s.source.schemaFingerprint ?? null
|
|
1598
1625
|
}).then(() => void 0, () => void 0)));
|
|
1599
1626
|
let be = 0, xe = !1;
|
|
1600
|
-
|
|
1601
|
-
if (!
|
|
1627
|
+
he = () => {
|
|
1628
|
+
if (!ye || xe) return;
|
|
1602
1629
|
let t = Date.now();
|
|
1603
|
-
t - be <
|
|
1604
|
-
tablesWritten:
|
|
1605
|
-
rowsWritten:
|
|
1630
|
+
t - be < Qn || (be = t, e.store.update(u, L, {
|
|
1631
|
+
tablesWritten: j,
|
|
1632
|
+
rowsWritten: A
|
|
1606
1633
|
}).then(() => void 0, () => {
|
|
1607
1634
|
xe = !0;
|
|
1608
1635
|
}));
|
|
1609
1636
|
};
|
|
1610
|
-
let Se = (t) =>
|
|
1637
|
+
let Se = (t) => ye ? e.store.update(u, L, {
|
|
1611
1638
|
finishedAt: t === null ? /* @__PURE__ */ new Date() : null,
|
|
1612
|
-
tablesWritten:
|
|
1613
|
-
rowsWritten:
|
|
1639
|
+
tablesWritten: j,
|
|
1640
|
+
rowsWritten: A,
|
|
1614
1641
|
failure: t
|
|
1615
1642
|
}).then(() => void 0, () => void 0) : Promise.resolve();
|
|
1616
|
-
if (t = (e) => Se(e), p === "replace" && !
|
|
1643
|
+
if (t = (e) => Se(e), p === "replace" && !k && ue.blocker !== void 0 && e.onWarn?.(An(ue.blocker)), p === "replace" && fe.length > 0 && e.onWarn?.(`write recorders are SUSPENDED for this replace (${fe.length} table(s) have one).\n A replace sets a state rather than changing rows, so a per-row history entry would describe
|
|
1617
1644
|
something that did not happen — and this path already writes through the raw store, without
|
|
1618
|
-
tenant scoping or \`audit()\` stamping, for the same reason.`),
|
|
1619
|
-
e.onWarn?.(`staging ${
|
|
1620
|
-
let t = e.store, r =
|
|
1645
|
+
tenant scoping or \`audit()\` stamping, for the same reason.`), k) {
|
|
1646
|
+
e.onWarn?.(`staging ${de.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.`);
|
|
1647
|
+
let t = e.store, r = de.map((e) => e.name), i = Fn(r, {
|
|
1621
1648
|
getTable: (e) => v(e),
|
|
1622
1649
|
registerTable: (e) => {
|
|
1623
1650
|
C(e);
|
|
@@ -1625,59 +1652,65 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
1625
1652
|
unregisterTable: ne
|
|
1626
1653
|
}), a = !1;
|
|
1627
1654
|
try {
|
|
1628
|
-
let i = g ? r : r.filter((e) => !
|
|
1655
|
+
let i = g ? r : r.filter((e) => !Bt(x, e));
|
|
1629
1656
|
yield* n.tryPromise({
|
|
1630
1657
|
try: async () => {
|
|
1631
|
-
await
|
|
1658
|
+
await zn(t, i.map((e) => Nn(e, O))), await zn(t, i.map((e) => Mn(e, O)));
|
|
1632
1659
|
},
|
|
1633
1660
|
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)}` })
|
|
1634
|
-
}), yield*
|
|
1635
|
-
try: () =>
|
|
1661
|
+
}), yield* I(ge(e.store, !g)), !g && x.truncated === !0 ? a = !0 : yield* n.tryPromise({
|
|
1662
|
+
try: () => Bn(t, de, O).then(() => {
|
|
1636
1663
|
a = !0;
|
|
1637
1664
|
}),
|
|
1638
1665
|
catch: (e) => new H({ reason: String(e?.message ?? e) })
|
|
1639
1666
|
}).pipe(n.catchAll((i) => n.gen(function* () {
|
|
1640
|
-
let a =
|
|
1667
|
+
let a = Rn(yield* n.promise(() => Vn(t, In(e.targetSnapshot, r), O).then((e) => e, () => [])));
|
|
1641
1668
|
return yield* n.fail(new H({ reason: a ?? `the swap failed and the target is UNCHANGED: ${i.reason}` }));
|
|
1642
1669
|
}))), !g && a && x.truncated !== !0 && (x.truncated = !0, yield* J(y, x));
|
|
1643
1670
|
} finally {
|
|
1644
|
-
i.undo(), a || g ? yield* n.promise(() =>
|
|
1671
|
+
i.undo(), a || g ? yield* n.promise(() => zn(t, r.map((e) => Nn(e, O))).then(() => void 0, () => void 0)) : 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");
|
|
1645
1672
|
}
|
|
1646
1673
|
} else g ? yield* n.tryPromise({
|
|
1647
1674
|
try: () => e.store.transactional(async (e) => {
|
|
1648
|
-
let t = await
|
|
1649
|
-
if (r.isFailure(t)) throw
|
|
1675
|
+
let t = await ve(() => n.runPromiseExit(ge(e, !1)));
|
|
1676
|
+
if (r.isFailure(t)) throw nr(t.cause);
|
|
1650
1677
|
}),
|
|
1651
|
-
catch: (e) =>
|
|
1652
|
-
}) : yield*
|
|
1678
|
+
catch: (e) => tr(e) ?? new H({ reason: `atomic import failed (rolled back): ${String(e?.message ?? e)}` })
|
|
1679
|
+
}) : yield* I(ge(e.store, !0));
|
|
1653
1680
|
if (e.assets && s.assets.index && !e.assetsAlreadyRestored) {
|
|
1654
|
-
let t = e.assets
|
|
1655
|
-
yield*
|
|
1656
|
-
|
|
1657
|
-
|
|
1681
|
+
let t = e.assets;
|
|
1682
|
+
yield* mt({
|
|
1683
|
+
sink: t,
|
|
1684
|
+
assetsDir: i.join(e.bundleDir, kt),
|
|
1685
|
+
indexPath: i.join(e.bundleDir, s.assets.index ?? "assets/index.ndjson"),
|
|
1686
|
+
progress: {
|
|
1687
|
+
done: (e) => e in x.assets,
|
|
1688
|
+
mark: (e) => (x.assets[e] = !0, J(y, x))
|
|
1689
|
+
}
|
|
1690
|
+
});
|
|
1658
1691
|
}
|
|
1659
1692
|
return yield* n.promise(() => Se(null)), {
|
|
1660
1693
|
manifest: s,
|
|
1661
|
-
tablesWritten:
|
|
1662
|
-
rowsWritten:
|
|
1663
|
-
tablesSkipped:
|
|
1664
|
-
rowsSkipped:
|
|
1665
|
-
fullyResumed:
|
|
1666
|
-
...
|
|
1694
|
+
tablesWritten: j,
|
|
1695
|
+
rowsWritten: A,
|
|
1696
|
+
tablesSkipped: N,
|
|
1697
|
+
rowsSkipped: M,
|
|
1698
|
+
fullyResumed: j === 0 && N > 0,
|
|
1699
|
+
...F === void 0 ? {} : { rollback: F }
|
|
1667
1700
|
};
|
|
1668
1701
|
}).pipe(n.tapError((e) => n.promise(() => t?.(e?.reason ?? e?._tag ?? "the import did not finish") ?? Promise.resolve())));
|
|
1669
|
-
},
|
|
1702
|
+
}, ar = (e) => e, or = (e) => {
|
|
1670
1703
|
if (e.allowLive || e.liveInstance === null || e.sameTarget === "different") return { refuse: !1 };
|
|
1671
1704
|
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.";
|
|
1672
1705
|
return {
|
|
1673
1706
|
refuse: !0,
|
|
1674
1707
|
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).`
|
|
1675
1708
|
};
|
|
1676
|
-
}, Q = Buffer.from("VBUNDLE2"),
|
|
1709
|
+
}, Q = Buffer.from("VBUNDLE2"), sr = 0, cr = 1, lr = 2, ur = 32, dr = async (e) => {
|
|
1677
1710
|
let t = [], n = async (r) => {
|
|
1678
1711
|
let i;
|
|
1679
1712
|
try {
|
|
1680
|
-
i = await
|
|
1713
|
+
i = await F(R(e, r), { withFileTypes: !0 });
|
|
1681
1714
|
} catch {
|
|
1682
1715
|
return;
|
|
1683
1716
|
}
|
|
@@ -1687,22 +1720,22 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
1687
1720
|
}
|
|
1688
1721
|
};
|
|
1689
1722
|
return await n(""), t.sort();
|
|
1690
|
-
},
|
|
1723
|
+
}, fr = (e, t) => {
|
|
1691
1724
|
let n = Buffer.from(e, "utf8"), r = Buffer.alloc(5 + n.length + 8);
|
|
1692
|
-
return r.writeUInt8(
|
|
1693
|
-
},
|
|
1725
|
+
return r.writeUInt8(cr, 0), r.writeUInt32BE(n.length, 1), n.copy(r, 5), r.writeBigUInt64BE(BigInt(t), 5 + n.length), r;
|
|
1726
|
+
}, pr = (e, t, n) => {
|
|
1694
1727
|
let r = Buffer.from(e, "utf8"), i = Buffer.from(t, "utf8"), a = Buffer.alloc(5 + r.length + 2 + i.length + 8), o = 0;
|
|
1695
|
-
return a.writeUInt8(
|
|
1728
|
+
return a.writeUInt8(lr, 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;
|
|
1696
1729
|
};
|
|
1697
|
-
async function*
|
|
1730
|
+
async function* mr(e) {
|
|
1698
1731
|
yield new Uint8Array(Q);
|
|
1699
|
-
for (let t of await
|
|
1700
|
-
let n =
|
|
1701
|
-
if (yield new Uint8Array(
|
|
1732
|
+
for (let t of await dr(e.dir)) {
|
|
1733
|
+
let n = R(e.dir, t), r = await ve(n);
|
|
1734
|
+
if (yield new Uint8Array(fr(t, r.size)), r.size > 0) for await (let e of le(n)) yield new Uint8Array(e);
|
|
1702
1735
|
}
|
|
1703
1736
|
if (e.blobs) for await (let t of e.blobs.list()) {
|
|
1704
|
-
yield new Uint8Array(
|
|
1705
|
-
let n =
|
|
1737
|
+
yield new Uint8Array(pr(t.key, t.contentType, t.size));
|
|
1738
|
+
let n = D("sha256"), r = 0;
|
|
1706
1739
|
for await (let i of e.blobs.open(t.key)) {
|
|
1707
1740
|
let e = Buffer.from(i);
|
|
1708
1741
|
n.update(e), r += e.length, yield new Uint8Array(e);
|
|
@@ -1710,11 +1743,11 @@ async function* or(e) {
|
|
|
1710
1743
|
if (r !== t.size) throw Error(`blob '${t.key}' size mismatch: header ${t.size}, streamed ${r}`);
|
|
1711
1744
|
yield new Uint8Array(n.digest());
|
|
1712
1745
|
}
|
|
1713
|
-
yield new Uint8Array(Buffer.from([
|
|
1746
|
+
yield new Uint8Array(Buffer.from([sr]));
|
|
1714
1747
|
}
|
|
1715
|
-
var
|
|
1716
|
-
let n =
|
|
1717
|
-
await
|
|
1748
|
+
var hr = (e, t) => new Promise((n, r) => e.write(t, (e) => e ? r(e) : n())), gr = (e) => new Promise((t) => e.end(t)), _r = async (e, t) => {
|
|
1749
|
+
let n = be(t.dir);
|
|
1750
|
+
await P(n, { recursive: !0 });
|
|
1718
1751
|
let r = Buffer.alloc(0), i = e[Symbol.asyncIterator](), a = !1, o = async () => {
|
|
1719
1752
|
let { value: e, done: t } = await i.next();
|
|
1720
1753
|
return t ? (a = !0, !1) : (r = r.length === 0 ? Buffer.from(e) : Buffer.concat([r, Buffer.from(e)]), !0);
|
|
@@ -1729,24 +1762,24 @@ var sr = (e, t) => new Promise((n, r) => e.write(t, (e) => e ? r(e) : n())), cr
|
|
|
1729
1762
|
for (;;) {
|
|
1730
1763
|
await s(1);
|
|
1731
1764
|
let e = c(1).readUInt8(0);
|
|
1732
|
-
if (e ===
|
|
1733
|
-
if (e ===
|
|
1765
|
+
if (e === sr) return;
|
|
1766
|
+
if (e === cr) {
|
|
1734
1767
|
await s(4);
|
|
1735
1768
|
let e = c(4).readUInt32BE(0);
|
|
1736
1769
|
await s(e);
|
|
1737
1770
|
let t = c(e).toString("utf8");
|
|
1738
1771
|
await s(8);
|
|
1739
|
-
let i = c(8).readBigUInt64BE(0), a =
|
|
1740
|
-
if (a !== n && !a.startsWith(n +
|
|
1741
|
-
await
|
|
1742
|
-
let o =
|
|
1772
|
+
let i = c(8).readBigUInt64BE(0), a = be(n, t);
|
|
1773
|
+
if (a !== n && !a.startsWith(n + xe)) throw Error(`archive path '${t}' escapes the destination`);
|
|
1774
|
+
await P(ye(a), { recursive: !0 });
|
|
1775
|
+
let o = O(a);
|
|
1743
1776
|
for (; i > 0n;) {
|
|
1744
1777
|
r.length === 0 && await s(1);
|
|
1745
1778
|
let e = i < BigInt(r.length) ? Number(i) : r.length;
|
|
1746
|
-
await
|
|
1779
|
+
await hr(o, c(e)), i -= BigInt(e);
|
|
1747
1780
|
}
|
|
1748
|
-
await
|
|
1749
|
-
} else if (e ===
|
|
1781
|
+
await gr(o);
|
|
1782
|
+
} else if (e === lr) {
|
|
1750
1783
|
await s(4);
|
|
1751
1784
|
let e = c(4).readUInt32BE(0);
|
|
1752
1785
|
await s(e);
|
|
@@ -1756,24 +1789,24 @@ var sr = (e, t) => new Promise((n, r) => e.write(t, (e) => e ? r(e) : n())), cr
|
|
|
1756
1789
|
await s(i);
|
|
1757
1790
|
let a = c(i).toString("utf8");
|
|
1758
1791
|
await s(8);
|
|
1759
|
-
let o = c(8).readBigUInt64BE(0), l = await t.blobs.has(n), u =
|
|
1792
|
+
let o = c(8).readBigUInt64BE(0), l = await t.blobs.has(n), u = D("sha256"), d = new N(), f = l ? Promise.resolve() : t.blobs.write(n, d, {
|
|
1760
1793
|
contentType: a,
|
|
1761
1794
|
size: Number(o)
|
|
1762
1795
|
}), p = o;
|
|
1763
1796
|
for (; p > 0n;) {
|
|
1764
1797
|
r.length === 0 && await s(1);
|
|
1765
1798
|
let e = p < BigInt(r.length) ? Number(p) : r.length, t = c(e);
|
|
1766
|
-
u.update(t), l || await
|
|
1799
|
+
u.update(t), l || await hr(d, t), p -= BigInt(e);
|
|
1767
1800
|
}
|
|
1768
|
-
l ? d.destroy() : await
|
|
1769
|
-
let m = c(
|
|
1801
|
+
l ? d.destroy() : await gr(d), await f, await s(ur);
|
|
1802
|
+
let m = c(ur);
|
|
1770
1803
|
if (!u.digest().equals(m)) throw Error(`blob '${n}' failed integrity (sha mismatch)`);
|
|
1771
1804
|
} else throw Error(`unknown bundle entry kind ${e}`);
|
|
1772
1805
|
}
|
|
1773
1806
|
} finally {
|
|
1774
1807
|
a || await Promise.resolve(i.return?.()).catch(() => void 0);
|
|
1775
1808
|
}
|
|
1776
|
-
},
|
|
1809
|
+
}, vr = async (e, t) => {
|
|
1777
1810
|
let n = new Set(t), r = /* @__PURE__ */ new Map(), i = Buffer.alloc(0), a = e[Symbol.asyncIterator](), o = async () => {
|
|
1778
1811
|
let { value: e, done: t } = await a.next();
|
|
1779
1812
|
return !t && (i = i.length === 0 ? Buffer.from(e) : Buffer.concat([i, Buffer.from(e)]), !0);
|
|
@@ -1795,8 +1828,8 @@ var sr = (e, t) => new Promise((n, r) => e.write(t, (e) => e ? r(e) : n())), cr
|
|
|
1795
1828
|
if (!await s(Q.length) || !c(Q.length).equals(Q)) throw Error("not a bundle archive (bad magic)");
|
|
1796
1829
|
for (; await s(1);) {
|
|
1797
1830
|
let e = c(1).readUInt8(0);
|
|
1798
|
-
if (e ===
|
|
1799
|
-
if (e !==
|
|
1831
|
+
if (e === sr || e === lr) break;
|
|
1832
|
+
if (e !== cr) throw Error(`unknown bundle entry kind ${e}`);
|
|
1800
1833
|
if (!await s(4)) break;
|
|
1801
1834
|
let t = c(4).readUInt32BE(0);
|
|
1802
1835
|
if (!await s(t)) break;
|
|
@@ -1806,22 +1839,22 @@ var sr = (e, t) => new Promise((n, r) => e.write(t, (e) => e ? r(e) : n())), cr
|
|
|
1806
1839
|
if (a === null || n.has(i) && (r.set(i, a), n.delete(i), n.size === 0)) break;
|
|
1807
1840
|
}
|
|
1808
1841
|
return r;
|
|
1809
|
-
},
|
|
1842
|
+
}, yr = (e, t) => _r(e, {
|
|
1810
1843
|
dir: t,
|
|
1811
|
-
blobs:
|
|
1812
|
-
}),
|
|
1844
|
+
blobs: br(t)
|
|
1845
|
+
}), br = (e) => {
|
|
1813
1846
|
let t = /* @__PURE__ */ new Set();
|
|
1814
1847
|
return {
|
|
1815
1848
|
has: async (e) => t.has(e),
|
|
1816
1849
|
write: async (n, r, i) => {
|
|
1817
|
-
let a = await import("node:fs/promises"), { pipeline: o } = await import("node:stream/promises"), { Transform: s } = await import("node:stream"), c =
|
|
1818
|
-
await
|
|
1819
|
-
let l =
|
|
1850
|
+
let a = await import("node:fs/promises"), { pipeline: o } = await import("node:stream/promises"), { Transform: s } = await import("node:stream"), c = R(e, "assets");
|
|
1851
|
+
await P(c, { recursive: !0 });
|
|
1852
|
+
let l = D("sha256"), u = 0, d = new s({ transform(e, t, n) {
|
|
1820
1853
|
l.update(e), u += e.length, n(null, e);
|
|
1821
|
-
} }), f =
|
|
1822
|
-
await o(r, d,
|
|
1854
|
+
} }), f = R(c, `.tmp-${Date.now()}-${t.size}`);
|
|
1855
|
+
await o(r, d, O(f));
|
|
1823
1856
|
let p = l.digest("hex");
|
|
1824
|
-
await a.rename(f,
|
|
1857
|
+
await a.rename(f, R(c, p)), await a.appendFile(R(c, "index.ndjson"), `${JSON.stringify({
|
|
1825
1858
|
key: n,
|
|
1826
1859
|
sha256: p,
|
|
1827
1860
|
size: u,
|
|
@@ -1829,33 +1862,33 @@ var sr = (e, t) => new Promise((n, r) => e.write(t, (e) => e ? r(e) : n())), cr
|
|
|
1829
1862
|
})}\n`), t.add(n);
|
|
1830
1863
|
}
|
|
1831
1864
|
};
|
|
1832
|
-
},
|
|
1865
|
+
}, xr = Se(se), Sr = Buffer.from("VENC1\0\0\0"), Cr = 32, wr = 16, Tr = 7, Er = 16, Dr = 262144, Or = {
|
|
1833
1866
|
N: 32768,
|
|
1834
1867
|
r: 8,
|
|
1835
1868
|
p: 1
|
|
1836
|
-
},
|
|
1869
|
+
}, kr = 134217728, Ar = 43, jr = (e, t, n) => {
|
|
1837
1870
|
let r = Buffer.alloc(12);
|
|
1838
|
-
return e.copy(r, 0, 0,
|
|
1871
|
+
return e.copy(r, 0, 0, Tr), r.writeUInt32BE(t >>> 0, Tr), r.writeUInt8(+!!n, 11), r;
|
|
1839
1872
|
};
|
|
1840
|
-
async function*
|
|
1873
|
+
async function* Mr(e, t) {
|
|
1841
1874
|
if (!t) throw Error("encryptStream: a non-empty passphrase is required");
|
|
1842
|
-
let n =
|
|
1843
|
-
...
|
|
1844
|
-
maxmem:
|
|
1845
|
-
}), a = Buffer.alloc(
|
|
1846
|
-
|
|
1875
|
+
let n = ae(wr), r = ae(Tr), i = await xr(t, n, Cr, {
|
|
1876
|
+
...Or,
|
|
1877
|
+
maxmem: kr
|
|
1878
|
+
}), a = Buffer.alloc(Ar);
|
|
1879
|
+
Sr.copy(a, 0), a.writeUInt32BE(Or.N, 8), a.writeUInt32BE(Or.r, 12), a.writeUInt32BE(Or.p, 16), n.copy(a, 20), r.copy(a, 36), yield new Uint8Array(a);
|
|
1847
1880
|
let o = 0, s = (e, t) => {
|
|
1848
|
-
let n = T("aes-256-gcm", i,
|
|
1881
|
+
let n = T("aes-256-gcm", i, jr(r, o, t));
|
|
1849
1882
|
o === 0 && n.setAAD(a);
|
|
1850
1883
|
let s = Buffer.concat([n.update(e), n.final()]), c = n.getAuthTag();
|
|
1851
1884
|
o++;
|
|
1852
|
-
let l = Buffer.alloc(5 + s.length +
|
|
1853
|
-
return l.writeUInt8(+!!t, 0), l.writeUInt32BE(s.length +
|
|
1885
|
+
let l = Buffer.alloc(5 + s.length + Er);
|
|
1886
|
+
return l.writeUInt8(+!!t, 0), l.writeUInt32BE(s.length + Er, 1), s.copy(l, 5), c.copy(l, 5 + s.length), new Uint8Array(l);
|
|
1854
1887
|
}, c = Buffer.alloc(0);
|
|
1855
|
-
for await (let t of e) for (c = c.length === 0 ? Buffer.from(t) : Buffer.concat([c, Buffer.from(t)]); c.length >
|
|
1888
|
+
for await (let t of e) for (c = c.length === 0 ? Buffer.from(t) : Buffer.concat([c, Buffer.from(t)]); c.length > Dr;) yield s(c.subarray(0, Dr), !1), c = c.subarray(Dr);
|
|
1856
1889
|
yield s(c, !0);
|
|
1857
1890
|
}
|
|
1858
|
-
async function*
|
|
1891
|
+
async function* Nr(e, t) {
|
|
1859
1892
|
if (!t) throw Error("decryptStream: a non-empty passphrase is required");
|
|
1860
1893
|
let n = e[Symbol.asyncIterator](), r = Buffer.alloc(0), i = !1, a = async () => {
|
|
1861
1894
|
let { value: e, done: t } = await n.next();
|
|
@@ -1864,16 +1897,16 @@ async function* Tr(e, t) {
|
|
|
1864
1897
|
for (; r.length < e && !i && await a(););
|
|
1865
1898
|
return r.length >= e;
|
|
1866
1899
|
};
|
|
1867
|
-
if (!await o(
|
|
1868
|
-
let s = r.subarray(0,
|
|
1869
|
-
if (!s.subarray(0, 8).equals(
|
|
1870
|
-
let c = s.readUInt32BE(8), l = s.readUInt32BE(12), u = s.readUInt32BE(16), d = Buffer.from(s.subarray(20, 36)), f = Buffer.from(s.subarray(36,
|
|
1900
|
+
if (!await o(Ar)) throw Error("encrypted bundle truncated (no header)");
|
|
1901
|
+
let s = r.subarray(0, Ar);
|
|
1902
|
+
if (!s.subarray(0, 8).equals(Sr)) throw Error("not an encrypted bundle (bad magic / wrong passphrase target)");
|
|
1903
|
+
let c = s.readUInt32BE(8), l = s.readUInt32BE(12), u = s.readUInt32BE(16), d = Buffer.from(s.subarray(20, 36)), f = Buffer.from(s.subarray(36, Ar)), p = await xr(t, d, Cr, {
|
|
1871
1904
|
N: c,
|
|
1872
1905
|
r: l,
|
|
1873
1906
|
p: u,
|
|
1874
|
-
maxmem:
|
|
1907
|
+
maxmem: kr
|
|
1875
1908
|
});
|
|
1876
|
-
r = r.subarray(
|
|
1909
|
+
r = r.subarray(Ar);
|
|
1877
1910
|
let m = 0, h = !1;
|
|
1878
1911
|
for (;;) {
|
|
1879
1912
|
if (!await o(5)) {
|
|
@@ -1882,11 +1915,11 @@ async function* Tr(e, t) {
|
|
|
1882
1915
|
}
|
|
1883
1916
|
if (h) throw Error("encrypted bundle has trailing data after the final frame");
|
|
1884
1917
|
let e = r.readUInt8(0) === 1, t = r.readUInt32BE(1);
|
|
1885
|
-
if (t <
|
|
1918
|
+
if (t < Er) throw Error("encrypted bundle corrupt (short frame)");
|
|
1886
1919
|
if (!await o(5 + t)) throw Error("encrypted bundle truncated (frame body)");
|
|
1887
|
-
let n = r.subarray(5, 5 + t -
|
|
1920
|
+
let n = r.subarray(5, 5 + t - Er), i = r.subarray(5 + t - Er, 5 + t);
|
|
1888
1921
|
r = r.subarray(5 + t);
|
|
1889
|
-
let a =
|
|
1922
|
+
let a = E("aes-256-gcm", p, jr(f, m, e));
|
|
1890
1923
|
m === 0 && a.setAAD(s), a.setAuthTag(i);
|
|
1891
1924
|
let c = Buffer.concat([a.update(n), a.final()]);
|
|
1892
1925
|
m++, e && (h = !0), c.length > 0 && (yield new Uint8Array(c));
|
|
@@ -1894,7 +1927,7 @@ async function* Tr(e, t) {
|
|
|
1894
1927
|
}
|
|
1895
1928
|
//#endregion
|
|
1896
1929
|
//#region src/blobStorage.ts
|
|
1897
|
-
var
|
|
1930
|
+
var Pr = (e, t) => ({
|
|
1898
1931
|
list: () => {
|
|
1899
1932
|
let e = t();
|
|
1900
1933
|
return { async *[Symbol.asyncIterator]() {
|
|
@@ -1907,29 +1940,32 @@ var Er = (e, t) => ({
|
|
|
1907
1940
|
} };
|
|
1908
1941
|
},
|
|
1909
1942
|
open: (t) => ({ async *[Symbol.asyncIterator]() {
|
|
1910
|
-
let r = await n.runPromise(
|
|
1943
|
+
let r = await n.runPromise(A(e, t)), i = await he(r.stream);
|
|
1911
1944
|
for await (let e of i) yield e instanceof Uint8Array ? e : new Uint8Array(e);
|
|
1912
1945
|
} })
|
|
1913
|
-
}),
|
|
1914
|
-
table:
|
|
1946
|
+
}), Fr = (e, t) => Pr(e, () => w(t, {
|
|
1947
|
+
table: pe,
|
|
1915
1948
|
chunkSize: 500
|
|
1916
1949
|
}).pipe(l.map((e) => ({
|
|
1917
1950
|
key: String(e.key),
|
|
1918
1951
|
contentType: String(e.contentType ?? "application/octet-stream"),
|
|
1919
1952
|
size: Number(e.size ?? 0)
|
|
1920
|
-
})))),
|
|
1953
|
+
})))), Ir = (e) => ({
|
|
1921
1954
|
has: (t) => n.runPromise(e.head(t).pipe(n.map((e) => e !== null))),
|
|
1922
1955
|
write: async (t, r, i) => {
|
|
1923
|
-
let a =
|
|
1924
|
-
await n.runPromise(
|
|
1956
|
+
let a = me("storage", () => ge.from(r));
|
|
1957
|
+
await n.runPromise(j(e, t, a, {
|
|
1925
1958
|
contentType: i.contentType,
|
|
1926
1959
|
totalSize: i.size
|
|
1927
1960
|
}));
|
|
1928
1961
|
}
|
|
1929
|
-
}),
|
|
1962
|
+
}), Lr = (e, t) => {
|
|
1930
1963
|
let n = t === "dump" ? "mariadb-dump" : "mariadb", r = t === "dump" ? "mysqldump" : "mysql";
|
|
1931
|
-
return e === "mariadb"
|
|
1932
|
-
|
|
1964
|
+
return e === "mariadb" ? Rr(n) ? { tool: n } : {
|
|
1965
|
+
tool: r,
|
|
1966
|
+
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.`
|
|
1967
|
+
} : { tool: r };
|
|
1968
|
+
}, Rr = (e) => (process.env.PATH ?? "").split(L).filter((e) => e !== "").some((t) => ue(R(t, e))), zr = (e) => {
|
|
1933
1969
|
switch (e) {
|
|
1934
1970
|
case "postgres": return "db.dump";
|
|
1935
1971
|
case "mysql":
|
|
@@ -1939,7 +1975,7 @@ var Er = (e, t) => ({
|
|
|
1939
1975
|
case "mssql": return "db.bacpac";
|
|
1940
1976
|
default: return "db.dump";
|
|
1941
1977
|
}
|
|
1942
|
-
},
|
|
1978
|
+
}, Br = (e) => e.filename ?? e.database ?? ((e.url ?? "").replace(/^file:/, "") || "app.sqlite"), $ = (e) => {
|
|
1943
1979
|
if (e.host || e.database) return {
|
|
1944
1980
|
host: e.host ?? "localhost",
|
|
1945
1981
|
port: e.port ?? 3306,
|
|
@@ -1955,7 +1991,7 @@ var Er = (e, t) => ({
|
|
|
1955
1991
|
password: decodeURIComponent(t.password) || "",
|
|
1956
1992
|
database: t.pathname.replace(/^\//, "") || ""
|
|
1957
1993
|
};
|
|
1958
|
-
},
|
|
1994
|
+
}, Vr = (e) => `Server=${e.host},${e.port};Database=${e.database};User ID=${e.username};Password=${e.password};TrustServerCertificate=True`, Hr = (e, t, n) => {
|
|
1959
1995
|
switch (e) {
|
|
1960
1996
|
case "postgres": {
|
|
1961
1997
|
if (t.url) return {
|
|
@@ -1965,6 +2001,7 @@ var Er = (e, t) => ({
|
|
|
1965
2001
|
"--format=custom",
|
|
1966
2002
|
"--no-owner",
|
|
1967
2003
|
"--no-privileges",
|
|
2004
|
+
...Ur,
|
|
1968
2005
|
"--dbname",
|
|
1969
2006
|
t.url,
|
|
1970
2007
|
"--file",
|
|
@@ -1983,6 +2020,7 @@ var Er = (e, t) => ({
|
|
|
1983
2020
|
"--format=custom",
|
|
1984
2021
|
"--no-owner",
|
|
1985
2022
|
"--no-privileges",
|
|
2023
|
+
...Ur,
|
|
1986
2024
|
"-h",
|
|
1987
2025
|
e.host,
|
|
1988
2026
|
"-p",
|
|
@@ -2002,13 +2040,14 @@ var Er = (e, t) => ({
|
|
|
2002
2040
|
let r = $(t);
|
|
2003
2041
|
return {
|
|
2004
2042
|
kind: "spawn",
|
|
2005
|
-
|
|
2043
|
+
...Lr(e, "dump"),
|
|
2006
2044
|
args: [
|
|
2007
2045
|
"--single-transaction",
|
|
2008
2046
|
"--routines",
|
|
2009
2047
|
"--triggers",
|
|
2010
2048
|
"--no-tablespaces",
|
|
2011
2049
|
"--protocol=TCP",
|
|
2050
|
+
`--ignore-table=${r.database}.${d}`,
|
|
2012
2051
|
"-h",
|
|
2013
2052
|
r.host,
|
|
2014
2053
|
"-P",
|
|
@@ -2024,7 +2063,7 @@ var Er = (e, t) => ({
|
|
|
2024
2063
|
case "sqlite":
|
|
2025
2064
|
case "turso": return {
|
|
2026
2065
|
kind: "copy",
|
|
2027
|
-
from:
|
|
2066
|
+
from: Br(t),
|
|
2028
2067
|
to: n
|
|
2029
2068
|
};
|
|
2030
2069
|
case "mssql": return {
|
|
@@ -2032,7 +2071,7 @@ var Er = (e, t) => ({
|
|
|
2032
2071
|
tool: "sqlpackage",
|
|
2033
2072
|
args: [
|
|
2034
2073
|
"/Action:Export",
|
|
2035
|
-
`/SourceConnectionString:${
|
|
2074
|
+
`/SourceConnectionString:${Vr($({
|
|
2036
2075
|
...t,
|
|
2037
2076
|
port: t.port ?? 1433
|
|
2038
2077
|
}))}`,
|
|
@@ -2045,7 +2084,7 @@ var Er = (e, t) => ({
|
|
|
2045
2084
|
reason: `no native backup for dialect '${e}'`
|
|
2046
2085
|
});
|
|
2047
2086
|
}
|
|
2048
|
-
},
|
|
2087
|
+
}, Ur = ["--exclude-table", d], Wr = (e, t, n) => {
|
|
2049
2088
|
switch (e) {
|
|
2050
2089
|
case "postgres": {
|
|
2051
2090
|
if (t.url) return {
|
|
@@ -2090,7 +2129,7 @@ var Er = (e, t) => ({
|
|
|
2090
2129
|
let r = $(t);
|
|
2091
2130
|
return {
|
|
2092
2131
|
kind: "spawn",
|
|
2093
|
-
|
|
2132
|
+
...Lr(e, "client"),
|
|
2094
2133
|
args: [
|
|
2095
2134
|
"--protocol=TCP",
|
|
2096
2135
|
"-h",
|
|
@@ -2109,14 +2148,14 @@ var Er = (e, t) => ({
|
|
|
2109
2148
|
case "turso": return {
|
|
2110
2149
|
kind: "copy",
|
|
2111
2150
|
from: n,
|
|
2112
|
-
to:
|
|
2151
|
+
to: Br(t)
|
|
2113
2152
|
};
|
|
2114
2153
|
case "mssql": return {
|
|
2115
2154
|
kind: "spawn",
|
|
2116
2155
|
tool: "sqlpackage",
|
|
2117
2156
|
args: [
|
|
2118
2157
|
"/Action:Import",
|
|
2119
|
-
`/TargetConnectionString:${
|
|
2158
|
+
`/TargetConnectionString:${Vr($({
|
|
2120
2159
|
...t,
|
|
2121
2160
|
port: t.port ?? 1433
|
|
2122
2161
|
}))}`,
|
|
@@ -2129,13 +2168,31 @@ var Er = (e, t) => ({
|
|
|
2129
2168
|
reason: `no native restore for dialect '${e}'`
|
|
2130
2169
|
});
|
|
2131
2170
|
}
|
|
2132
|
-
},
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2171
|
+
}, Gr = (e) => e.kind === "copy" ? n.async((t) => {
|
|
2172
|
+
let r = `${e.to}.restoring-${oe()}`, i = (e) => {
|
|
2173
|
+
try {
|
|
2174
|
+
k(r);
|
|
2175
|
+
} catch {}
|
|
2176
|
+
t(n.fail(new W({
|
|
2177
|
+
tool: "copy",
|
|
2178
|
+
reason: e
|
|
2179
|
+
})));
|
|
2180
|
+
};
|
|
2181
|
+
ce(e.from, r, (a) => {
|
|
2182
|
+
if (a) {
|
|
2183
|
+
i(`copy ${e.from} → ${e.to}: ${a.message}`);
|
|
2184
|
+
return;
|
|
2185
|
+
}
|
|
2186
|
+
de(r, e.to, (r) => {
|
|
2187
|
+
if (r) {
|
|
2188
|
+
i(`copy ${e.from} → ${e.to}: ${r.message}`);
|
|
2189
|
+
return;
|
|
2190
|
+
}
|
|
2191
|
+
t(n.void);
|
|
2192
|
+
});
|
|
2193
|
+
});
|
|
2137
2194
|
}) : n.async((t) => {
|
|
2138
|
-
let r =
|
|
2195
|
+
let r = Ce(e.tool, [...e.args], {
|
|
2139
2196
|
env: {
|
|
2140
2197
|
...process.env,
|
|
2141
2198
|
...e.env
|
|
@@ -2148,33 +2205,63 @@ var Er = (e, t) => ({
|
|
|
2148
2205
|
}), i = "";
|
|
2149
2206
|
r.stderr?.on("data", (e) => {
|
|
2150
2207
|
i += String(e);
|
|
2151
|
-
})
|
|
2152
|
-
let a = () => {
|
|
2208
|
+
});
|
|
2209
|
+
let a = !1, o = (e) => {
|
|
2210
|
+
a || (a = !0, t(e));
|
|
2211
|
+
}, s, c = (e) => {
|
|
2212
|
+
s ??= e;
|
|
2213
|
+
};
|
|
2214
|
+
if (e.stdoutFile && r.stdout) {
|
|
2215
|
+
let t = O(e.stdoutFile);
|
|
2216
|
+
t.on("error", (t) => c(`writing ${I(e.stdoutFile)}: ${t.message}`)), r.stdout.on("error", (e) => c(`reading the tool's output: ${e.message}`)), r.stdout.pipe(t);
|
|
2217
|
+
}
|
|
2218
|
+
let l = 0;
|
|
2219
|
+
if (e.stdinFile && r.stdin) {
|
|
2220
|
+
let t = le(e.stdinFile);
|
|
2221
|
+
t.on("data", (e) => {
|
|
2222
|
+
l += e.length;
|
|
2223
|
+
}), t.on("error", (t) => {
|
|
2224
|
+
c(`reading ${I(e.stdinFile)}: ${t.message}`), r.stdin?.end();
|
|
2225
|
+
}), r.stdin.on("error", (t) => {
|
|
2226
|
+
t.code !== "EPIPE" && c(`feeding ${I(e.stdinFile)} to ${e.tool}: ${t.message}`);
|
|
2227
|
+
}), t.pipe(r.stdin);
|
|
2228
|
+
}
|
|
2229
|
+
let u = () => {
|
|
2153
2230
|
if (e.stdoutFile !== void 0) try {
|
|
2154
|
-
|
|
2231
|
+
k(e.stdoutFile);
|
|
2155
2232
|
} catch {}
|
|
2156
2233
|
};
|
|
2157
|
-
r.on("error", (
|
|
2158
|
-
|
|
2234
|
+
r.on("error", (t) => {
|
|
2235
|
+
u(), o(n.fail(new W({
|
|
2159
2236
|
tool: e.tool,
|
|
2160
|
-
reason: `failed to launch (is '${e.tool}' installed + on PATH?): ${
|
|
2237
|
+
reason: `failed to launch (is '${e.tool}' installed + on PATH?): ${t.message}`,
|
|
2238
|
+
...e.hint === void 0 ? {} : { hint: e.hint }
|
|
2161
2239
|
})));
|
|
2162
|
-
}), r.on("close", (
|
|
2163
|
-
if (
|
|
2164
|
-
|
|
2240
|
+
}), r.on("close", (t) => {
|
|
2241
|
+
if (t === 0 && s === void 0) {
|
|
2242
|
+
o(n.void);
|
|
2165
2243
|
return;
|
|
2166
2244
|
}
|
|
2167
|
-
|
|
2245
|
+
u();
|
|
2246
|
+
let r = e.stdinFile === void 0 ? "" : ` — ${l} of ${Kr(e.stdinFile)} byte(s) of ${I(e.stdinFile)} had been fed to it`;
|
|
2247
|
+
o(n.fail(new W({
|
|
2168
2248
|
tool: e.tool,
|
|
2169
|
-
reason: `exited with code ${
|
|
2170
|
-
stderr: i.slice(-2e3)
|
|
2249
|
+
reason: (t === 0 ? `exited cleanly, but ${s}` : `exited with code ${t}${s === void 0 ? "" : ` (${s})`}`) + r + (e.stdoutFile === void 0 ? "" : ` — the partial ${I(e.stdoutFile)} was removed, so it cannot be mistaken for a backup`),
|
|
2250
|
+
stderr: i.trim() === "" ? `(${e.tool} printed nothing to stderr)` : i.slice(-2e3),
|
|
2251
|
+
...e.hint === void 0 ? {} : { hint: e.hint }
|
|
2171
2252
|
})));
|
|
2172
2253
|
});
|
|
2173
|
-
}),
|
|
2174
|
-
|
|
2254
|
+
}), Kr = (e) => {
|
|
2255
|
+
try {
|
|
2256
|
+
return String(fe(e).size);
|
|
2257
|
+
} catch {
|
|
2258
|
+
return "?";
|
|
2259
|
+
}
|
|
2260
|
+
}, qr = (e) => typeof e == "object" && e ? "fake" in e ? `fake:${e.fake}` : "custom" : String(e), Jr = (e) => n.gen(function* () {
|
|
2261
|
+
let t = yield* at(e.scope ?? { kind: "all" }, e.snapshot, {
|
|
2175
2262
|
store: e.store,
|
|
2176
2263
|
...e.tenantTables ? { tenantTables: e.tenantTables } : {}
|
|
2177
|
-
}), r =
|
|
2264
|
+
}), r = tn(t.tables, e.snapshot), i = e.classification ?? Ee(e.snapshot), a = e.sampleSize ?? 20, o = ze(r.map((t) => ({
|
|
2178
2265
|
name: t,
|
|
2179
2266
|
columns: (e.snapshot.tables.find((e) => e.name === t)?.columns ?? []).map((e) => ({
|
|
2180
2267
|
name: e.name,
|
|
@@ -2186,15 +2273,15 @@ var Er = (e, t) => ({
|
|
|
2186
2273
|
table: i,
|
|
2187
2274
|
chunkSize: a,
|
|
2188
2275
|
...r === void 0 ? {} : { where: r }
|
|
2189
|
-
}).pipe(l.take(a), l.runCollect, n.map((e) => [...e])), d = u.map((t) =>
|
|
2190
|
-
c.push(...
|
|
2276
|
+
}).pipe(l.take(a), l.runCollect, n.map((e) => [...e])), d = u.map((t) => He(t, i, o.actions, e.masking.seed));
|
|
2277
|
+
c.push(...Ve(d, i, o.actions));
|
|
2191
2278
|
let f = u[0], p = d[0], m = [];
|
|
2192
2279
|
if (f && p) for (let [e, t] of Object.entries(o.actions)) {
|
|
2193
2280
|
if (!e.startsWith(`${i}.`) || t === "keep") continue;
|
|
2194
2281
|
let n = e.slice(i.length + 1);
|
|
2195
2282
|
m.push({
|
|
2196
2283
|
column: n,
|
|
2197
|
-
action:
|
|
2284
|
+
action: qr(t),
|
|
2198
2285
|
before: f[n],
|
|
2199
2286
|
after: p[n]
|
|
2200
2287
|
});
|
|
@@ -2209,7 +2296,7 @@ var Er = (e, t) => ({
|
|
|
2209
2296
|
leaks: c,
|
|
2210
2297
|
unclassified: o.unclassified
|
|
2211
2298
|
};
|
|
2212
|
-
}),
|
|
2299
|
+
}), Yr = (e, t) => {
|
|
2213
2300
|
let n = new Map(t.map((e) => [e.name, e])), r = [], i = {};
|
|
2214
2301
|
for (let t of e) {
|
|
2215
2302
|
let e = n.get(t.name);
|
|
@@ -2264,7 +2351,7 @@ var Er = (e, t) => ({
|
|
|
2264
2351
|
skipColumns: i,
|
|
2265
2352
|
refuse: r.some((e) => e.verdict === "refuse")
|
|
2266
2353
|
};
|
|
2267
|
-
},
|
|
2354
|
+
}, Xr = (e) => {
|
|
2268
2355
|
if (e.findings.length === 0) return [" schema matches the bundle"];
|
|
2269
2356
|
let t = {
|
|
2270
2357
|
safe: "✓",
|
|
@@ -2275,4 +2362,4 @@ var Er = (e, t) => ({
|
|
|
2275
2362
|
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;
|
|
2276
2363
|
};
|
|
2277
2364
|
//#endregion
|
|
2278
|
-
export {
|
|
2365
|
+
export { kt as ASSETS_DIR, At as ASSET_INDEX_FILE, H as BundleError, qe as CodecError, Ye as CompressionError, et as CrossDialectError, Ot as DATA_DIR, Te as DEFAULT_CLASS_ACTIONS, Ue as ENVIRONMENT_LOCAL_TABLES, We as ENVIRONMENT_PORTABLE_TABLES, bt as FORMAT_VERSION, $e as ImportModeError, Je as IntegrityError, jt as LEDGER_FILE, K as MANIFEST_FILE, Qe as MaskingError, W as NativeToolError, Xe as RowsRefusedError, $n as SAVEPOINT_BATCH_SIZE, On as STAGING_PREFIX, Ze as SchemaDriftError, U as ScopeError, Le as applyAction, tr as asImportError, or as assessLiveImport, Vt as assetDone, zr as backupArtifactName, Hr as backupCommand, gn as canonicalType, ft as captureAssetsToCas, yn as checkPortability, Zn as checkSchemaDrift, Ee as classificationFromSnapshot, Yr as classifyImportDrift, _t as compressionExt, nt as computeSubsetIds, Wn as decideStaging, Et as decodeManifest, Zt as decodeRow, Yt as decodeValue, Nr as decryptStream, ar as defineDataProfile, Xn as diffSnapshots, br as dirBlobSink, Nn as dropStagingSql, Rt as emptyLedger, Tt as encodeManifest, Xt as encodeRow, qt as encodeValue, Mr as encryptStream, Ke as environmentLocalNotice, Ge as environmentLocalReason, zn as execStatements, Xr as formatImportDrift, bn as formatIssue, ht as hasZstd, V as isEnvironmentLocal, vt as makeCompressor, yt as makeDecompressor, He as maskRow, en as ndjsonToRows, ut as noAssetProgress, hn as outsideReferenceRefusal, mn as outsideReferencesInto, mr as packBundle, vr as peekBundle, gt as pickCompression, ze as planMasking, Jr as previewMasking, pt as readAssetIndex, zt as readLedger, De as resolveAction, at as resolveScope, lt as restoreAssetFromCas, mt as restoreAssetsFromCas, Wr as restoreCommand, $t as rowsToNdjson, pn as runExport, ir as runImport, Gr as runNativeStep, Ve as scanForLeaks, X as stagingNameFor, st as storageAssetSink, ot as storageAssetSource, Ir as storageBlobSink, Pr as storageBlobSource, Fr as storageRefsBlobSource, rt as subsetToScope, Bt as tableDone, tn as topoSortTables, _r as unpackBundle, yr as unpackBundleToDir, ct as writeAssetToCas, J as writeLedger };
|