@voltro/data-transfer 0.40.0 → 0.42.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +171 -0
- package/dist/index.d.ts +65 -7
- package/dist/index.js +511 -422
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { Cause as e, Data as t, Effect as n, Exit as r, Metric as i, MetricBoundaries as a, Schedule as o, Schema as s, Stream as c } from "effect";
|
|
2
|
-
import {
|
|
3
|
-
import { fingerprintSchema as
|
|
4
|
-
import { createCipheriv as
|
|
5
|
-
import { STORAGE_REFS_TABLE as
|
|
2
|
+
import { classifyConstraintViolation as l, eq as u, extractDbCause as d, inSet as f, isNotNull as p, or as m, streamTable as h } from "@voltro/database";
|
|
3
|
+
import { fingerprintSchema as g } from "@voltro/database/sql";
|
|
4
|
+
import { createCipheriv as _, createDecipheriv as v, createHash as y, createHmac as ee, randomBytes as b, scrypt as x } from "node:crypto";
|
|
5
|
+
import { STORAGE_REFS_TABLE as S, fromNodeReadable as C, getObjectStream as te, putObjectStream as ne, toNodeReadable as re } from "@voltro/plugin-storage";
|
|
6
6
|
import * as w from "node:zlib";
|
|
7
|
-
import { PassThrough as
|
|
8
|
-
import { copyFile as
|
|
9
|
-
import { mkdir as
|
|
10
|
-
import { basename as
|
|
11
|
-
import { promisify as
|
|
12
|
-
import { spawn as
|
|
7
|
+
import { PassThrough as ie, Readable as ae } from "node:stream";
|
|
8
|
+
import { copyFile as oe, createReadStream as T, createWriteStream as E, existsSync as se, unlinkSync as ce } from "node:fs";
|
|
9
|
+
import { mkdir as D, readdir as le, stat as ue } from "node:fs/promises";
|
|
10
|
+
import { basename as de, delimiter as fe, dirname as pe, join as O, resolve as me, sep as he } from "node:path";
|
|
11
|
+
import { promisify as ge } from "node:util";
|
|
12
|
+
import { spawn as _e } from "node:child_process";
|
|
13
13
|
//#region src/consistency.ts
|
|
14
|
-
var
|
|
14
|
+
var ve = (e) => {
|
|
15
15
|
switch (e) {
|
|
16
16
|
case "postgres": return ["SET TRANSACTION ISOLATION LEVEL REPEATABLE READ"];
|
|
17
17
|
case "mysql":
|
|
@@ -21,7 +21,7 @@ var ge = (e) => {
|
|
|
21
21
|
case "turso": return [];
|
|
22
22
|
default: return [];
|
|
23
23
|
}
|
|
24
|
-
},
|
|
24
|
+
}, ye = {
|
|
25
25
|
email: { fake: "email" },
|
|
26
26
|
fullName: { fake: "fullName" },
|
|
27
27
|
firstName: { fake: "firstName" },
|
|
@@ -36,7 +36,7 @@ var ge = (e) => {
|
|
|
36
36
|
date: "dateShift",
|
|
37
37
|
secret: "null",
|
|
38
38
|
freeText: "redact"
|
|
39
|
-
},
|
|
39
|
+
}, k = (e) => {
|
|
40
40
|
let t = {};
|
|
41
41
|
for (let n of e.tables) {
|
|
42
42
|
let e = {};
|
|
@@ -48,15 +48,15 @@ var ge = (e) => {
|
|
|
48
48
|
t[n.name] = e;
|
|
49
49
|
}
|
|
50
50
|
return t;
|
|
51
|
-
},
|
|
51
|
+
}, be = (e, t, n, r) => {
|
|
52
52
|
let i = r.columns?.[`${e}.${t}`];
|
|
53
53
|
if (i) return i;
|
|
54
54
|
if (n?.sensitive) {
|
|
55
55
|
let e = n.sensitive.class;
|
|
56
|
-
return r.classes?.[e] ??
|
|
56
|
+
return r.classes?.[e] ?? ye[e] ?? "redact";
|
|
57
57
|
}
|
|
58
58
|
return n?.safe ? r.onSafe ?? "keep" : n?.columnType === "id" || n?.columnType === "reference" ? "keep" : r.onUnclassified ?? "error";
|
|
59
|
-
},
|
|
59
|
+
}, A = (e, t) => ee("sha256", e).update(t == null ? "∅" : String(t)).digest(), j = (e, t) => e.readUInt32BE(t * 4 % (e.length - 4)), M = [
|
|
60
60
|
"Ada",
|
|
61
61
|
"Alan",
|
|
62
62
|
"Grace",
|
|
@@ -69,7 +69,7 @@ var ge = (e) => {
|
|
|
69
69
|
"Guido",
|
|
70
70
|
"Margaret",
|
|
71
71
|
"Bjarne"
|
|
72
|
-
],
|
|
72
|
+
], xe = [
|
|
73
73
|
"Lovelace",
|
|
74
74
|
"Turing",
|
|
75
75
|
"Hopper",
|
|
@@ -82,7 +82,7 @@ var ge = (e) => {
|
|
|
82
82
|
"Rossum",
|
|
83
83
|
"Hamilton",
|
|
84
84
|
"Stroustrup"
|
|
85
|
-
],
|
|
85
|
+
], Se = [
|
|
86
86
|
"Maple",
|
|
87
87
|
"Oak",
|
|
88
88
|
"Cedar",
|
|
@@ -91,7 +91,7 @@ var ge = (e) => {
|
|
|
91
91
|
"Birch",
|
|
92
92
|
"Willow",
|
|
93
93
|
"Ash"
|
|
94
|
-
],
|
|
94
|
+
], Ce = [
|
|
95
95
|
"Initech",
|
|
96
96
|
"Hooli",
|
|
97
97
|
"Umbrella",
|
|
@@ -100,23 +100,23 @@ var ge = (e) => {
|
|
|
100
100
|
"Soylent",
|
|
101
101
|
"Stark",
|
|
102
102
|
"Wayne"
|
|
103
|
-
], N = (e, t, n) => e[
|
|
104
|
-
let r =
|
|
103
|
+
], N = (e, t, n) => e[j(t, n) % e.length], we = (e, t, n) => {
|
|
104
|
+
let r = A(n, `${e}:${String(t)}`), i = j(r, 1) % 1e4;
|
|
105
105
|
switch (e) {
|
|
106
|
-
case "email": return `${N(
|
|
107
|
-
case "fullName": return `${N(
|
|
108
|
-
case "firstName": return N(
|
|
109
|
-
case "lastName": return N(
|
|
110
|
-
case "username": return `${N(
|
|
111
|
-
case "phone": return `+1${String(
|
|
112
|
-
case "address": return `${
|
|
113
|
-
case "company": return N(
|
|
106
|
+
case "email": return `${N(M, r, 0).toLowerCase()}.${N(xe, r, 2).toLowerCase()}${i}@example.com`;
|
|
107
|
+
case "fullName": return `${N(M, r, 0)} ${N(xe, r, 1)}`;
|
|
108
|
+
case "firstName": return N(M, r, 0);
|
|
109
|
+
case "lastName": return N(xe, r, 1);
|
|
110
|
+
case "username": return `${N(M, r, 0).toLowerCase()}${i}`;
|
|
111
|
+
case "phone": return `+1${String(j(r, 0) % 1e10).padStart(10, "0")}`;
|
|
112
|
+
case "address": return `${j(r, 0) % 9899 + 100} ${N(Se, r, 1)} St`;
|
|
113
|
+
case "company": return N(Ce, r, 0);
|
|
114
114
|
case "url": return `https://example.com/${r.toString("hex").slice(0, 12)}`;
|
|
115
|
-
case "ip": return `${
|
|
116
|
-
case "creditCard": return
|
|
115
|
+
case "ip": return `${j(r, 0) % 256}.${j(r, 1) % 256}.${j(r, 2) % 256}.${j(r, 3) % 254 + 1}`;
|
|
116
|
+
case "creditCard": return Te(`4${r.toString("hex").replace(/\D/g, "").padEnd(14, "0").slice(0, 14)}`);
|
|
117
117
|
default: return `${e}_${r.toString("hex").slice(0, 8)}`;
|
|
118
118
|
}
|
|
119
|
-
},
|
|
119
|
+
}, Te = (e) => {
|
|
120
120
|
let t = e.slice(0, 15).split("").map(Number), n = 0;
|
|
121
121
|
t.forEach((e, t) => {
|
|
122
122
|
let r = e;
|
|
@@ -124,26 +124,45 @@ var ge = (e) => {
|
|
|
124
124
|
});
|
|
125
125
|
let r = (10 - n % 10) % 10;
|
|
126
126
|
return e.slice(0, 15) + String(r);
|
|
127
|
-
},
|
|
127
|
+
}, Ee = 864e5, De = (e, t) => {
|
|
128
128
|
let n = e instanceof Date ? e.getTime() : Date.parse(String(e));
|
|
129
129
|
if (Number.isNaN(n)) return e;
|
|
130
|
-
let r = A(
|
|
130
|
+
let r = j(A(t, "date-shift-offset"), 0) % 730 - 365, i = new Date(n + r * Ee);
|
|
131
131
|
return e instanceof Date ? i : i.toISOString();
|
|
132
|
-
},
|
|
132
|
+
}, Oe = (e, t) => {
|
|
133
133
|
if (t.value === null || t.value === void 0) return null;
|
|
134
134
|
if (e === "error") throw Error(`masking: unresolved 'error' action for ${t.table}.${t.column}`);
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
135
|
+
if (e === "keep") return t.value;
|
|
136
|
+
if (e === "null") return null;
|
|
137
|
+
if (e === "redact") return t.columnType === "text" || typeof t.value == "string" ? "[redacted]" : null;
|
|
138
|
+
if (e === "hash") return A(t.seed, t.value).toString("hex").slice(0, 16);
|
|
139
|
+
if (e === "dateShift") return De(t.value, t.seed);
|
|
140
|
+
if (typeof e == "object" && "fake" in e) return we(e.fake, t.value, t.seed);
|
|
141
|
+
if (typeof e == "object" && "custom" in e) return e.custom(t);
|
|
142
|
+
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 }.`);
|
|
143
|
+
}, ke = (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", Ae = (e, t, n) => {
|
|
144
|
+
let r = {}, i = [], a = [];
|
|
145
|
+
for (let o of e) for (let e of o.columns) {
|
|
146
|
+
let s = `${o.name}.${e.name}`, c = be(o.name, e.name, t[o.name]?.[e.name], n);
|
|
147
|
+
if (c === "error") {
|
|
148
|
+
i.push(s);
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
if (!ke(c)) {
|
|
152
|
+
a.push({
|
|
153
|
+
column: s,
|
|
154
|
+
action: c
|
|
155
|
+
});
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
r[s] = c;
|
|
141
159
|
}
|
|
142
160
|
return {
|
|
143
161
|
actions: r,
|
|
144
|
-
unclassified: i
|
|
162
|
+
unclassified: i,
|
|
163
|
+
invalid: a
|
|
145
164
|
};
|
|
146
|
-
},
|
|
165
|
+
}, je = [
|
|
147
166
|
{
|
|
148
167
|
name: "email",
|
|
149
168
|
re: /[\w.+-]+@[\w-]+\.[\w.-]+/
|
|
@@ -160,7 +179,7 @@ var ge = (e) => {
|
|
|
160
179
|
name: "creditCard",
|
|
161
180
|
re: /\b(?:\d[ -]?){13,16}\b/
|
|
162
181
|
}
|
|
163
|
-
],
|
|
182
|
+
], Me = (e, t, n) => {
|
|
164
183
|
let r = [], i = /* @__PURE__ */ new Set();
|
|
165
184
|
for (let [a, o] of Object.entries(n)) {
|
|
166
185
|
if (!a.startsWith(`${t}.`) || o !== "keep") continue;
|
|
@@ -168,7 +187,7 @@ var ge = (e) => {
|
|
|
168
187
|
for (let a of e) {
|
|
169
188
|
let e = a[n];
|
|
170
189
|
if (typeof e != "string" || e.length === 0) continue;
|
|
171
|
-
let o =
|
|
190
|
+
let o = je.find((t) => t.re.test(e));
|
|
172
191
|
if (o && !i.has(n)) {
|
|
173
192
|
i.add(n), r.push({
|
|
174
193
|
table: t,
|
|
@@ -181,9 +200,9 @@ var ge = (e) => {
|
|
|
181
200
|
}
|
|
182
201
|
}
|
|
183
202
|
return r;
|
|
184
|
-
},
|
|
203
|
+
}, Ne = (e, t, n, r, i) => {
|
|
185
204
|
let a = {};
|
|
186
|
-
for (let [o, s] of Object.entries(e)) a[o] =
|
|
205
|
+
for (let [o, s] of Object.entries(e)) a[o] = Oe(n[`${t}.${o}`] ?? "keep", {
|
|
187
206
|
value: s,
|
|
188
207
|
table: t,
|
|
189
208
|
column: o,
|
|
@@ -191,24 +210,24 @@ var ge = (e) => {
|
|
|
191
210
|
...i?.[o] ? { columnType: i[o] } : {}
|
|
192
211
|
});
|
|
193
212
|
return a;
|
|
194
|
-
},
|
|
213
|
+
}, P = class extends s.TaggedError()("BundleError", {
|
|
195
214
|
reason: s.String,
|
|
196
215
|
path: s.optional(s.String)
|
|
197
|
-
}) {},
|
|
216
|
+
}) {}, Pe = class extends s.TaggedError()("CodecError", {
|
|
198
217
|
table: s.String,
|
|
199
218
|
reason: s.String
|
|
200
|
-
}) {},
|
|
219
|
+
}) {}, F = class extends s.TaggedError()("IntegrityError", {
|
|
201
220
|
what: s.String,
|
|
202
221
|
expected: s.String,
|
|
203
222
|
actual: s.String
|
|
204
|
-
}) {},
|
|
223
|
+
}) {}, Fe = class extends t.TaggedError("CompressionError") {}, I = class extends s.TaggedError()("ScopeError", { reason: s.String }) {}, Ie = class extends s.TaggedError()("DanglingReferenceError", {
|
|
205
224
|
rows: s.Array(s.Struct({
|
|
206
225
|
table: s.String,
|
|
207
226
|
id: s.String,
|
|
208
227
|
reason: s.String
|
|
209
228
|
})),
|
|
210
229
|
totalCount: s.Number
|
|
211
|
-
}) {},
|
|
230
|
+
}) {}, Le = class extends s.TaggedError()("SchemaDriftError", {
|
|
212
231
|
bundleFingerprint: s.String,
|
|
213
232
|
targetFingerprint: s.String,
|
|
214
233
|
diff: s.Array(s.String)
|
|
@@ -217,36 +236,39 @@ var ge = (e) => {
|
|
|
217
236
|
let e = this.stderr?.trim();
|
|
218
237
|
return `${this.tool}: ${this.reason}${e ? `\n${e}` : ""}`;
|
|
219
238
|
}
|
|
220
|
-
},
|
|
239
|
+
}, Re = class extends s.TaggedError()("MaskingError", {
|
|
240
|
+
unclassified: s.Array(s.String),
|
|
241
|
+
invalidActions: s.optional(s.Array(s.String))
|
|
242
|
+
}) {}, ze = class extends s.TaggedError()("ImportModeError", {
|
|
221
243
|
mode: s.String,
|
|
222
244
|
reason: s.String
|
|
223
|
-
}) {},
|
|
245
|
+
}) {}, Be = class extends s.TaggedError()("CrossDialectError", {
|
|
224
246
|
sourceDialect: s.String,
|
|
225
247
|
targetDialect: s.String,
|
|
226
248
|
blocking: s.Array(s.String)
|
|
227
|
-
}) {}, R = (e, t) => e.tables.find((e) => e.name === t)?.columns.find((e) => e.type === "id")?.name ?? "id",
|
|
249
|
+
}) {}, R = (e, t) => e.tables.find((e) => e.name === t)?.columns.find((e) => e.type === "id")?.name ?? "id", Ve = (e, t) => (e.tables.find((e) => e.name === t)?.columns ?? []).filter((e) => e.references).map((e) => ({
|
|
228
250
|
col: e.name,
|
|
229
251
|
refTable: e.references.table
|
|
230
252
|
})), z = (e, t, r) => n.gen(function* () {
|
|
231
|
-
let i = r.maxDepth ?? 12, a = new Set(r.pinned ?? []), o = /* @__PURE__ */ new Map(), s = /* @__PURE__ */ new Map(), l = /* @__PURE__ */ new Map(),
|
|
253
|
+
let i = r.maxDepth ?? 12, a = new Set(r.pinned ?? []), o = /* @__PURE__ */ new Map(), s = /* @__PURE__ */ new Map(), l = /* @__PURE__ */ new Map(), u = (e) => {
|
|
232
254
|
let t = s.get(e) ?? /* @__PURE__ */ new Set();
|
|
233
255
|
return s.set(e, t), t;
|
|
234
|
-
},
|
|
256
|
+
}, d = (e, t) => {
|
|
235
257
|
if (t == null || a.has(e) || s.get(e)?.has(t)) return;
|
|
236
258
|
let n = l.get(e) ?? /* @__PURE__ */ new Set();
|
|
237
259
|
n.add(t), l.set(e, n);
|
|
238
|
-
},
|
|
239
|
-
let r =
|
|
260
|
+
}, p = (e, t, n) => {
|
|
261
|
+
let r = u(e), i = a.has(e) ? void 0 : o.get(e) ?? (() => {
|
|
240
262
|
let t = /* @__PURE__ */ new Set();
|
|
241
263
|
return o.set(e, t), t;
|
|
242
264
|
})();
|
|
243
265
|
return (e) => {
|
|
244
266
|
r.add(e[t]), i?.add(e[t]);
|
|
245
|
-
for (let t of n)
|
|
267
|
+
for (let t of n) d(t.refTable, e[t.col]);
|
|
246
268
|
};
|
|
247
269
|
}, g = (r, i) => {
|
|
248
|
-
let a =
|
|
249
|
-
return c.runForEach(
|
|
270
|
+
let a = p(r, R(t, r), Ve(t, r));
|
|
271
|
+
return c.runForEach(h(e, {
|
|
250
272
|
table: r,
|
|
251
273
|
pk: R(t, r),
|
|
252
274
|
...i === void 0 ? {} : { where: i }
|
|
@@ -257,7 +279,7 @@ var ge = (e) => {
|
|
|
257
279
|
l.clear();
|
|
258
280
|
for (let [n, r] of e) {
|
|
259
281
|
let e = [...r].filter((e) => !s.get(n)?.has(e));
|
|
260
|
-
e.length !== 0 && (yield* g(n,
|
|
282
|
+
e.length !== 0 && (yield* g(n, f(R(t, n), e)));
|
|
261
283
|
}
|
|
262
284
|
}
|
|
263
285
|
});
|
|
@@ -272,58 +294,60 @@ var ge = (e) => {
|
|
|
272
294
|
refTable: e.references?.table
|
|
273
295
|
})).filter((t) => !!t.refTable && e.has(t.refTable) && (s.get(t.refTable)?.size ?? 0) > 0);
|
|
274
296
|
if (a.length === 0) continue;
|
|
275
|
-
let o = s.get(t)?.size ?? 0, c = a.map((e) =>
|
|
276
|
-
yield* g(t, c.length === 1 ? c[0] :
|
|
297
|
+
let o = s.get(t)?.size ?? 0, c = a.map((e) => f(e.col, [...s.get(e.refTable)]));
|
|
298
|
+
yield* g(t, c.length === 1 ? c[0] : m(...c)), (s.get(t)?.size ?? 0) > o && !e.has(t) && r.push(t), (s.get(t)?.size ?? 0) > o && (n = !0);
|
|
277
299
|
}
|
|
278
300
|
for (let t of r) e.add(t);
|
|
279
301
|
if (yield* _, !n) break;
|
|
280
302
|
}
|
|
281
303
|
}
|
|
282
304
|
return o;
|
|
283
|
-
}),
|
|
305
|
+
}), He = (e, t) => ({
|
|
284
306
|
tables: [...e.keys()],
|
|
285
307
|
predicateFor: (n) => {
|
|
286
308
|
let r = e.get(n);
|
|
287
|
-
return r ?
|
|
309
|
+
return r ? f(R(t, n), [...r]) : void 0;
|
|
288
310
|
}
|
|
289
|
-
}), B = "tenantId",
|
|
311
|
+
}), B = "tenantId", V = (e, t, r) => n.gen(function* () {
|
|
290
312
|
let i = t.tables.map((e) => e.name), a = new Map(t.tables.map((e) => [e.name, e]));
|
|
291
313
|
if (e.kind === "all") return {
|
|
292
314
|
tables: i,
|
|
293
315
|
predicateFor: () => void 0
|
|
294
316
|
};
|
|
295
317
|
if (e.kind === "tables") {
|
|
296
|
-
let t =
|
|
318
|
+
let t = e.tables ?? [], r = new Set(i), a = t.filter((e) => !r.has(e));
|
|
319
|
+
if (a.length > 0) return yield* n.fail(new I({ reason: `scope { kind: 'tables' } names ${a.length} table(s) that are not in the schema: ${a.join(", ")}. Refusing to write a bundle that silently omits them. Known tables: ${i.length}.` }));
|
|
320
|
+
let o = new Set(t);
|
|
297
321
|
return {
|
|
298
|
-
tables: i.filter((e) =>
|
|
322
|
+
tables: i.filter((e) => o.has(e)),
|
|
299
323
|
predicateFor: () => void 0
|
|
300
324
|
};
|
|
301
325
|
}
|
|
302
326
|
let o = e.tenantId;
|
|
303
|
-
if (o === void 0 || o === "") return yield* n.fail(new
|
|
327
|
+
if (o === void 0 || o === "") return yield* n.fail(new I({ reason: "scope { kind: 'tenant' } requires a tenantId — pass --tenant <id> (CLI) or scope.tenantId (API). Refusing to fall back to an unfiltered export." }));
|
|
304
328
|
let s = r.tenantTables ? r.tenantTables.filter((e) => a.has(e)) : i.filter((e) => a.get(e)?.columns.some((e) => e.name === B) ?? !1);
|
|
305
|
-
if (s.length === 0) return yield* n.fail(new
|
|
329
|
+
if (s.length === 0) return yield* n.fail(new I({ reason: r.tenantTables ? "tenant scope: none of the provided tenantTables exist in the schema — nothing to filter." : `tenant scope: no table carries a '${B}' column — nothing to filter. A tenant export of a schema without tenant-scoped tables would be the full database; refusing.` }));
|
|
306
330
|
let c = yield* z(r.store, t, {
|
|
307
|
-
seeds: Object.fromEntries(s.map((e) => [e,
|
|
331
|
+
seeds: Object.fromEntries(s.map((e) => [e, u(B, o)])),
|
|
308
332
|
pinned: s,
|
|
309
333
|
children: { roots: s }
|
|
310
|
-
}),
|
|
334
|
+
}), l = [...c.entries()].filter(([, e]) => e.size > 0).map(([e]) => e), d = new Set(s);
|
|
311
335
|
return {
|
|
312
|
-
tables: [...s, ...
|
|
313
|
-
predicateFor: (e) =>
|
|
336
|
+
tables: [...s, ...l],
|
|
337
|
+
predicateFor: (e) => d.has(e) ? u(B, o) : c.has(e) ? f(R(t, e), [...c.get(e)]) : void 0
|
|
314
338
|
};
|
|
315
|
-
}),
|
|
339
|
+
}), Ue = (e, t) => ({
|
|
316
340
|
list: t,
|
|
317
|
-
open: (t) =>
|
|
318
|
-
}),
|
|
319
|
-
write: (t, n, r) =>
|
|
341
|
+
open: (t) => te(e, t)
|
|
342
|
+
}), We = (e) => ({
|
|
343
|
+
write: (t, n, r) => ne(e, t, n, r),
|
|
320
344
|
has: (t) => e.head(t).pipe(n.map((e) => e !== null))
|
|
321
|
-
}),
|
|
345
|
+
}), Ge = (e, t) => n.tryPromise({
|
|
322
346
|
try: async () => {
|
|
323
347
|
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) {
|
|
324
348
|
u.update(e), d += e.length, n(null, e);
|
|
325
349
|
} });
|
|
326
|
-
await o(await
|
|
350
|
+
await o(await re(e.stream), f, n(l));
|
|
327
351
|
let p = u.digest("hex"), m = c.join(t, p);
|
|
328
352
|
try {
|
|
329
353
|
await r.access(m), await r.rm(l, { force: !0 });
|
|
@@ -336,23 +360,23 @@ var ge = (e) => {
|
|
|
336
360
|
contentType: e.contentType
|
|
337
361
|
};
|
|
338
362
|
},
|
|
339
|
-
catch: (e) => new
|
|
363
|
+
catch: (e) => new P({
|
|
340
364
|
reason: `asset write failed: ${String(e?.message ?? e)}`,
|
|
341
365
|
path: t
|
|
342
366
|
})
|
|
343
|
-
}),
|
|
344
|
-
let i = yield* n.promise(() => import("node:path")), { createReadStream: a } = yield* n.promise(() => import("node:fs")), { createHash: o } = yield* n.promise(() => import("node:crypto")), s = i.join(t, r.sha256), l = o("sha256"), u =
|
|
367
|
+
}), Ke = (e, t, r) => n.gen(function* () {
|
|
368
|
+
let i = yield* n.promise(() => import("node:path")), { createReadStream: a } = yield* n.promise(() => import("node:fs")), { createHash: o } = yield* n.promise(() => import("node:crypto")), s = i.join(t, r.sha256), l = o("sha256"), u = C("bundle", () => a(s)).pipe(c.tap((e) => n.sync(() => l.update(e))));
|
|
345
369
|
yield* e.write(r.key, u, {
|
|
346
370
|
contentType: r.contentType,
|
|
347
371
|
totalSize: r.size
|
|
348
372
|
});
|
|
349
373
|
let d = l.digest("hex");
|
|
350
|
-
if (d !== r.sha256) return yield* n.fail(new
|
|
374
|
+
if (d !== r.sha256) return yield* n.fail(new F({
|
|
351
375
|
what: `asset ${r.key}`,
|
|
352
376
|
expected: r.sha256,
|
|
353
377
|
actual: d
|
|
354
378
|
}));
|
|
355
|
-
}),
|
|
379
|
+
}), qe = () => typeof w.createZstdCompress == "function", Je = (e) => e === "none" ? "none" : e === "gzip" ? "gzip" : qe() ? "zstd" : "gzip", Ye = (e) => e === "zstd" ? ".zst" : e === "gzip" ? ".gz" : "", Xe = (e) => e === "zstd" ? w.createZstdCompress() : e === "gzip" ? w.createGzip() : new ie(), Ze = (e) => e === "zstd" ? w.createZstdDecompress() : e === "gzip" ? w.createGunzip() : new ie(), Qe = 1, $e = s.Struct({
|
|
356
380
|
name: s.String,
|
|
357
381
|
file: s.String,
|
|
358
382
|
rowCount: s.Number,
|
|
@@ -362,11 +386,11 @@ var ge = (e) => {
|
|
|
362
386
|
value: s.String
|
|
363
387
|
}),
|
|
364
388
|
primaryKey: s.String
|
|
365
|
-
}),
|
|
389
|
+
}), et = s.Struct({
|
|
366
390
|
kind: s.Literal("all", "tenant", "tables"),
|
|
367
391
|
tenantId: s.optional(s.String),
|
|
368
392
|
tables: s.optional(s.Array(s.String))
|
|
369
|
-
}),
|
|
393
|
+
}), tt = s.Struct({
|
|
370
394
|
count: s.Number,
|
|
371
395
|
totalBytes: s.Number,
|
|
372
396
|
index: s.optional(s.String)
|
|
@@ -377,23 +401,23 @@ s.Struct({
|
|
|
377
401
|
size: s.Number,
|
|
378
402
|
contentType: s.String
|
|
379
403
|
});
|
|
380
|
-
var
|
|
404
|
+
var nt = s.Struct({
|
|
381
405
|
formatVersion: s.Number,
|
|
382
406
|
createdAt: s.String,
|
|
383
407
|
source: s.Struct({
|
|
384
408
|
dialect: s.String,
|
|
385
409
|
schemaFingerprint: s.String
|
|
386
410
|
}),
|
|
387
|
-
scope:
|
|
411
|
+
scope: et,
|
|
388
412
|
consistency: s.Literal("live", "snapshot"),
|
|
389
413
|
compression: s.Literal("zstd", "gzip", "none"),
|
|
390
|
-
tables: s.Array(
|
|
391
|
-
assets:
|
|
414
|
+
tables: s.Array($e),
|
|
415
|
+
assets: tt,
|
|
392
416
|
masking: s.optional(s.Struct({
|
|
393
417
|
policyId: s.optional(s.String),
|
|
394
418
|
transformed: s.Array(s.String)
|
|
395
419
|
}))
|
|
396
|
-
}),
|
|
420
|
+
}), rt = s.encodeSync(s.parseJson(nt)), it = s.decodeUnknownSync(s.parseJson(nt)), H = "manifest.json", at = "README.md", U = "data", W = "assets", G = "assets/index.ndjson", ot = ".ledger.json", st = (e) => e.kind === "tenant" ? `tenant \`${e.tenantId}\`` : e.kind === "tables" ? `tables \`${(e.tables ?? []).join(", ")}\`` : "all tables", ct = (e, t) => {
|
|
397
421
|
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");
|
|
398
422
|
return `# Voltro data export
|
|
399
423
|
|
|
@@ -407,7 +431,7 @@ The machine-readable manifest is \`manifest.json\`; this file is a human summary
|
|
|
407
431
|
| **Format version** | ${e.formatVersion} |
|
|
408
432
|
| **Consistency** | ${e.consistency} |
|
|
409
433
|
| **Compression** | ${e.compression} |
|
|
410
|
-
| **Scope** | ${
|
|
434
|
+
| **Scope** | ${st(e.scope)} |
|
|
411
435
|
| **Tables** | ${e.tables.length} |
|
|
412
436
|
| **Rows (total)** | ${n} |
|
|
413
437
|
| **Assets** | ${a} |
|
|
@@ -432,39 +456,39 @@ voltro data import <this-directory>
|
|
|
432
456
|
voltro data import <this-directory> --target api --api-url <url> --token <secret>
|
|
433
457
|
\`\`\`
|
|
434
458
|
`;
|
|
435
|
-
},
|
|
459
|
+
}, lt = i.counter("voltro_data_transfer_rows", { description: "Rows exported/imported by @voltro/data-transfer, tagged by phase + table." }), ut = a.exponential({
|
|
436
460
|
start: .01,
|
|
437
461
|
factor: 2,
|
|
438
462
|
count: 16
|
|
439
|
-
}),
|
|
463
|
+
}), dt = i.histogram("voltro_data_transfer_table_seconds", ut, "Per-table export/import duration in seconds, tagged by phase."), K = (e, t) => {
|
|
440
464
|
if (e) try {
|
|
441
465
|
e(t);
|
|
442
466
|
} catch {}
|
|
443
|
-
},
|
|
467
|
+
}, ft = (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(dt.pipe(i.tagged("phase", e), i.update((s - r) / 1e3)), lt.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: {
|
|
444
468
|
table: t,
|
|
445
469
|
index: r,
|
|
446
470
|
tableCount: a,
|
|
447
471
|
phase: e
|
|
448
|
-
} })),
|
|
472
|
+
} })), pt = () => ({
|
|
449
473
|
tables: {},
|
|
450
474
|
assets: {}
|
|
451
|
-
}),
|
|
475
|
+
}), mt = (e) => n.tryPromise(async () => {
|
|
452
476
|
let t = await (await import("node:fs/promises")).readFile(e, "utf8");
|
|
453
477
|
return JSON.parse(t);
|
|
454
|
-
}).pipe(n.orElseSucceed(
|
|
478
|
+
}).pipe(n.orElseSucceed(pt)), q = (e, t) => n.tryPromise({
|
|
455
479
|
try: async () => {
|
|
456
480
|
let n = await import("node:fs/promises"), { randomUUID: r } = await import("node:crypto"), i = `${e}.tmp-${r()}`;
|
|
457
481
|
await n.writeFile(i, JSON.stringify(t)), await n.rename(i, e);
|
|
458
482
|
},
|
|
459
|
-
catch: (t) => new
|
|
483
|
+
catch: (t) => new P({
|
|
460
484
|
reason: `ledger write failed: ${String(t?.message ?? t)}`,
|
|
461
485
|
path: e
|
|
462
486
|
})
|
|
463
|
-
}),
|
|
487
|
+
}), ht = (e, t) => t in e.tables, gt = (e, t) => t in e.assets, _t = /* @__PURE__ */ new Set([
|
|
464
488
|
"json",
|
|
465
489
|
"array",
|
|
466
490
|
"vector"
|
|
467
|
-
]),
|
|
491
|
+
]), vt = (e) => typeof e == "object" && !!e && !Array.isArray(e) && typeof e.$v == "string", yt = (e) => Buffer.from(e).toString("base64"), bt = (e) => new Uint8Array(Buffer.from(e, "base64")), xt = (e) => e instanceof Date ? e.toISOString() : typeof e == "number" ? new Date(e).toISOString() : String(e), St = (e, t) => e == null ? null : t !== void 0 && _t.has(t) ? e : t === "bytes" ? yt(e) : t === "timestamp" || t === "date" ? xt(e) : typeof e == "bigint" ? {
|
|
468
492
|
$v: "bigint",
|
|
469
493
|
d: e.toString()
|
|
470
494
|
} : typeof e == "number" && !Number.isFinite(e) ? {
|
|
@@ -475,30 +499,30 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
475
499
|
d: e.toISOString()
|
|
476
500
|
} : e instanceof Uint8Array ? {
|
|
477
501
|
$v: "bytes",
|
|
478
|
-
d:
|
|
479
|
-
} : e,
|
|
502
|
+
d: yt(e)
|
|
503
|
+
} : e, Ct = (e) => {
|
|
480
504
|
switch (e.$v) {
|
|
481
505
|
case "bigint": return BigInt(e.d);
|
|
482
506
|
case "number": return Number(e.d);
|
|
483
507
|
case "date": return new Date(e.d);
|
|
484
|
-
case "bytes": return
|
|
508
|
+
case "bytes": return bt(e.d);
|
|
485
509
|
default: return e;
|
|
486
510
|
}
|
|
487
|
-
},
|
|
511
|
+
}, wt = (e, t) => e == null ? null : t !== void 0 && _t.has(t) ? e : vt(e) ? Ct(e) : t === "bytes" ? bt(e) : t === "timestamp" || t === "date" ? new Date(e) : e, Tt = (e, t) => {
|
|
488
512
|
let n = {};
|
|
489
|
-
for (let [r, i] of Object.entries(e)) n[r] =
|
|
513
|
+
for (let [r, i] of Object.entries(e)) n[r] = St(i, t[r]);
|
|
490
514
|
return n;
|
|
491
|
-
},
|
|
515
|
+
}, Et = (e, t) => {
|
|
492
516
|
let n = {};
|
|
493
|
-
for (let [r, i] of Object.entries(e)) n[r] =
|
|
517
|
+
for (let [r, i] of Object.entries(e)) n[r] = wt(i, t[r]);
|
|
494
518
|
return n;
|
|
495
|
-
},
|
|
496
|
-
try: () =>
|
|
497
|
-
catch: (e) => new
|
|
519
|
+
}, Dt = new TextEncoder(), Ot = (e, t) => e.pipe(c.map((e) => Dt.encode(`${JSON.stringify(Tt(e, t))}\n`))), kt = (e, t, r) => e.pipe(c.decodeText(), c.splitLines, c.filter((e) => e.trim().length > 0), c.mapEffect((e) => n.try({
|
|
520
|
+
try: () => Et(JSON.parse(e), r),
|
|
521
|
+
catch: (e) => new Pe({
|
|
498
522
|
table: t,
|
|
499
523
|
reason: String(e?.message ?? e)
|
|
500
524
|
})
|
|
501
|
-
}))),
|
|
525
|
+
}))), At = (e, t) => {
|
|
502
526
|
let n = new Set(e), r = /* @__PURE__ */ new Map();
|
|
503
527
|
for (let e of t.tables) {
|
|
504
528
|
if (!n.has(e.name)) continue;
|
|
@@ -518,11 +542,34 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
518
542
|
};
|
|
519
543
|
for (let t of e) n.has(t) && s(t);
|
|
520
544
|
return i;
|
|
521
|
-
},
|
|
545
|
+
}, J = (e) => {
|
|
546
|
+
let t = e, n = [];
|
|
547
|
+
typeof t?._tag == "string" && t._tag !== "" && n.push(t._tag);
|
|
548
|
+
let r = typeof t?.message == "string" ? t.message.trim() : "";
|
|
549
|
+
if (r === "" ? t?.cause !== void 0 && t.cause !== null && n.push(J(t.cause)) : n.push(r), n.length === 0) {
|
|
550
|
+
let t = String(e ?? "").trim();
|
|
551
|
+
return t === "" || t === "[object Object]" ? jt(e) : t;
|
|
552
|
+
}
|
|
553
|
+
return n.join(": ");
|
|
554
|
+
}, jt = (e) => {
|
|
555
|
+
try {
|
|
556
|
+
let t = JSON.stringify(e);
|
|
557
|
+
return t === void 0 || t === "{}" ? "unknown error (no message, cause or tag)" : t;
|
|
558
|
+
} catch {
|
|
559
|
+
return "unknown error (not serialisable)";
|
|
560
|
+
}
|
|
561
|
+
}, Mt = (e, t) => {
|
|
522
562
|
let n = e.tables.find((e) => e.name === t), r = {};
|
|
523
563
|
for (let e of n?.columns ?? []) r[e.name] = e.type;
|
|
524
564
|
return r;
|
|
525
|
-
},
|
|
565
|
+
}, Nt = (e, t) => {
|
|
566
|
+
let n = e.tables.find((e) => e.name === t);
|
|
567
|
+
if (n === void 0) return { refusal: `${t}: not in the schema snapshot, so its primary key is unknown.` };
|
|
568
|
+
let r = n.columns.find((e) => e.type === "id");
|
|
569
|
+
if (r !== void 0) return { column: r.name };
|
|
570
|
+
let i = n.indexes.find((e) => e.name === `${t}_pkey`)?.columns ?? n.primaryKey ?? [];
|
|
571
|
+
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.` };
|
|
572
|
+
}, Pt = (e, t, r) => n.tryPromise({
|
|
526
573
|
try: async () => {
|
|
527
574
|
let { createWriteStream: n } = await import("node:fs"), i = await import("node:fs/promises"), { createHash: a, randomUUID: o } = await import("node:crypto"), { pipeline: s } = await import("node:stream/promises"), { Readable: l, Transform: u } = await import("node:stream"), d = a("sha256"), f = 0, p = new u({ transform(e, t, n) {
|
|
528
575
|
d.update(e);
|
|
@@ -531,7 +578,7 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
531
578
|
n(null, e);
|
|
532
579
|
} }), m = l.fromWeb(c.toReadableStream(e)), h = `${t}.tmp-${o()}`;
|
|
533
580
|
try {
|
|
534
|
-
await s(m, p,
|
|
581
|
+
await s(m, p, Xe(r), n(h)), await i.rename(h, t);
|
|
535
582
|
} catch (e) {
|
|
536
583
|
throw await i.rm(h, { force: !0 }).catch(() => {}), e;
|
|
537
584
|
}
|
|
@@ -540,115 +587,120 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
540
587
|
checksum: d.digest("hex")
|
|
541
588
|
};
|
|
542
589
|
},
|
|
543
|
-
catch: (e) => new
|
|
544
|
-
reason: `write table failed: ${
|
|
590
|
+
catch: (e) => new P({
|
|
591
|
+
reason: `write table failed: ${J(e)}`,
|
|
545
592
|
path: t
|
|
546
593
|
})
|
|
547
|
-
}),
|
|
594
|
+
}), Ft = (e, t) => n.tryPromise({
|
|
548
595
|
try: async () => {
|
|
549
596
|
await (await import("node:fs/promises")).appendFile(e, `${t}\n`);
|
|
550
597
|
},
|
|
551
|
-
catch: (t) => new
|
|
552
|
-
reason: `append failed: ${
|
|
598
|
+
catch: (t) => new P({
|
|
599
|
+
reason: `append failed: ${J(t)}`,
|
|
553
600
|
path: e
|
|
554
601
|
})
|
|
555
|
-
}),
|
|
556
|
-
let
|
|
557
|
-
for (let
|
|
558
|
-
let
|
|
559
|
-
if (
|
|
560
|
-
|
|
561
|
-
|
|
602
|
+
}), It = (e, t) => n.gen(function* () {
|
|
603
|
+
let r = [], i = t.ordered.length;
|
|
604
|
+
for (let a = 0; a < t.ordered.length; a++) {
|
|
605
|
+
let o = t.ordered[a], s = Mt(t.snapshot, o), l = Nt(t.snapshot, o);
|
|
606
|
+
if ("refusal" in l) return yield* n.fail(new I({ reason: `export: ${l.refusal}` }));
|
|
607
|
+
let u = l.column, d = `${U}/${o}.ndjson${Ye(t.compression)}`;
|
|
608
|
+
if (ht(t.ledger, o)) {
|
|
609
|
+
let e = t.ledger.tables[o];
|
|
610
|
+
K(t.onProgress, {
|
|
562
611
|
phase: "export",
|
|
563
|
-
table:
|
|
564
|
-
index:
|
|
565
|
-
tableCount:
|
|
612
|
+
table: o,
|
|
613
|
+
index: a,
|
|
614
|
+
tableCount: i,
|
|
566
615
|
event: "done",
|
|
567
616
|
rowsDone: e.rowCount,
|
|
568
617
|
total: e.rowCount
|
|
569
|
-
}),
|
|
570
|
-
name:
|
|
571
|
-
file:
|
|
618
|
+
}), r.push({
|
|
619
|
+
name: o,
|
|
620
|
+
file: d,
|
|
572
621
|
rowCount: e.rowCount,
|
|
573
622
|
checksum: e.checksum,
|
|
574
|
-
columnTypes:
|
|
575
|
-
primaryKey:
|
|
623
|
+
columnTypes: s,
|
|
624
|
+
primaryKey: u
|
|
576
625
|
});
|
|
577
626
|
continue;
|
|
578
627
|
}
|
|
579
|
-
|
|
628
|
+
K(t.onProgress, {
|
|
580
629
|
phase: "export",
|
|
581
|
-
table:
|
|
582
|
-
index:
|
|
583
|
-
tableCount:
|
|
630
|
+
table: o,
|
|
631
|
+
index: a,
|
|
632
|
+
tableCount: i,
|
|
584
633
|
event: "start",
|
|
585
634
|
rowsDone: 0
|
|
586
635
|
});
|
|
587
|
-
let
|
|
588
|
-
table:
|
|
589
|
-
pk:
|
|
636
|
+
let f = t.resolved.predicateFor(o), p = h(e, {
|
|
637
|
+
table: o,
|
|
638
|
+
pk: u,
|
|
590
639
|
chunkSize: t.chunkSize,
|
|
591
640
|
retry: t.retry,
|
|
592
|
-
...
|
|
593
|
-
}),
|
|
594
|
-
t.ledger.tables[
|
|
595
|
-
rowCount:
|
|
596
|
-
checksum:
|
|
597
|
-
}, yield*
|
|
641
|
+
...f === void 0 ? {} : { where: f }
|
|
642
|
+
}), m = t.mask ? p.pipe(c.map((e) => Ne(e, o, t.mask.actions, t.mask.seed, s))) : p, { rowCount: g, checksum: _ } = yield* ft("export", o, a, i, (e) => e.rowCount, Pt(Ot(m, s), t.bundleFile(d), t.compression));
|
|
643
|
+
t.ledger.tables[o] = {
|
|
644
|
+
rowCount: g,
|
|
645
|
+
checksum: _
|
|
646
|
+
}, yield* q(t.ledgerPath, t.ledger), K(t.onProgress, {
|
|
598
647
|
phase: "export",
|
|
599
|
-
table:
|
|
600
|
-
index:
|
|
601
|
-
tableCount:
|
|
648
|
+
table: o,
|
|
649
|
+
index: a,
|
|
650
|
+
tableCount: i,
|
|
602
651
|
event: "done",
|
|
603
|
-
rowsDone:
|
|
604
|
-
total:
|
|
605
|
-
}),
|
|
606
|
-
name:
|
|
607
|
-
file:
|
|
608
|
-
rowCount:
|
|
609
|
-
checksum:
|
|
610
|
-
columnTypes:
|
|
611
|
-
primaryKey:
|
|
652
|
+
rowsDone: g,
|
|
653
|
+
total: g
|
|
654
|
+
}), r.push({
|
|
655
|
+
name: o,
|
|
656
|
+
file: d,
|
|
657
|
+
rowCount: g,
|
|
658
|
+
checksum: _,
|
|
659
|
+
columnTypes: s,
|
|
660
|
+
primaryKey: u
|
|
612
661
|
});
|
|
613
662
|
}
|
|
614
|
-
return
|
|
615
|
-
}),
|
|
663
|
+
return r;
|
|
664
|
+
}), Lt = (e) => ({
|
|
616
665
|
_tag: "RawSqlFragment",
|
|
617
666
|
strings: [e],
|
|
618
667
|
values: []
|
|
619
|
-
}),
|
|
668
|
+
}), Rt = (t, i, a) => n.tryPromise({
|
|
620
669
|
try: () => t.transactional(async (t) => {
|
|
621
|
-
if (t.raw) for (let e of
|
|
670
|
+
if (t.raw) for (let e of ve(i)) await t.raw(Lt(e));
|
|
622
671
|
let o = await n.runPromiseExit(a(t));
|
|
623
672
|
if (r.isFailure(o)) throw Error(e.pretty(o.cause));
|
|
624
673
|
return o.value;
|
|
625
674
|
}),
|
|
626
|
-
catch: (e) => e instanceof
|
|
627
|
-
}),
|
|
628
|
-
let t = e.scope ?? { kind: "all" }, r =
|
|
675
|
+
catch: (e) => e instanceof P ? e : new P({ reason: `snapshot export failed: ${J(e)}` })
|
|
676
|
+
}), zt = (e) => n.gen(function* () {
|
|
677
|
+
let t = e.scope ?? { kind: "all" }, r = Je(e.compression), i = e.chunkSize ?? 1e3, a = e.retryTimes ?? 5, s = o.exponential("50 millis").pipe(o.jittered, o.intersect(o.recurs(a))), l = yield* n.promise(() => import("node:path")), u = (t) => l.join(e.outDir, t);
|
|
629
678
|
yield* n.tryPromise({
|
|
630
679
|
try: async () => {
|
|
631
680
|
let t = await import("node:fs/promises");
|
|
632
|
-
await t.mkdir(u(
|
|
681
|
+
await t.mkdir(u(U), { recursive: !0 }), e.assets && await t.mkdir(u(W), { recursive: !0 });
|
|
633
682
|
},
|
|
634
|
-
catch: (t) => new
|
|
635
|
-
reason: `mkdir failed: ${
|
|
683
|
+
catch: (t) => new P({
|
|
684
|
+
reason: `mkdir failed: ${J(t)}`,
|
|
636
685
|
path: e.outDir
|
|
637
686
|
})
|
|
638
687
|
});
|
|
639
|
-
let d = u(
|
|
688
|
+
let d = u(ot), f = yield* mt(d), p = e.subset ? He(yield* z(e.store, e.snapshot, e.subset), e.snapshot) : yield* V(t, e.snapshot, {
|
|
640
689
|
store: e.store,
|
|
641
690
|
...e.tenantTables ? { tenantTables: e.tenantTables } : {}
|
|
642
|
-
}),
|
|
691
|
+
}), m = At(p.tables, e.snapshot), h = e.consistency ?? "live", _, v;
|
|
643
692
|
if (e.masking) {
|
|
644
|
-
let t = e.classification ??
|
|
693
|
+
let t = e.classification ?? k(e.snapshot), r = Ae(m.map((t) => ({
|
|
645
694
|
name: t,
|
|
646
695
|
columns: (e.snapshot.tables.find((e) => e.name === t)?.columns ?? []).map((e) => ({
|
|
647
696
|
name: e.name,
|
|
648
697
|
type: e.type
|
|
649
698
|
}))
|
|
650
699
|
})), t, e.masking);
|
|
651
|
-
if (r.unclassified.length > 0) return yield* n.fail(new
|
|
700
|
+
if (r.unclassified.length > 0 || r.invalid.length > 0) return yield* n.fail(new Re({
|
|
701
|
+
unclassified: r.unclassified,
|
|
702
|
+
...r.invalid.length > 0 ? { invalidActions: r.invalid.map((e) => `${e.column}: ${JSON.stringify(e.action)}`) } : {}
|
|
703
|
+
}));
|
|
652
704
|
v = {
|
|
653
705
|
actions: r.actions,
|
|
654
706
|
seed: e.masking.seed
|
|
@@ -661,7 +713,7 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
661
713
|
}
|
|
662
714
|
let y = {
|
|
663
715
|
snapshot: e.snapshot,
|
|
664
|
-
ordered:
|
|
716
|
+
ordered: m,
|
|
665
717
|
resolved: p,
|
|
666
718
|
compression: r,
|
|
667
719
|
chunkSize: i,
|
|
@@ -671,34 +723,34 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
671
723
|
bundleFile: u,
|
|
672
724
|
...v ? { mask: v } : {},
|
|
673
725
|
...e.onProgress ? { onProgress: e.onProgress } : {}
|
|
674
|
-
},
|
|
726
|
+
}, ee = h === "snapshot" ? yield* Rt(e.store, e.dialect, (e) => It(e, y)) : yield* It(e.store, y), b = 0, x = 0;
|
|
675
727
|
if (e.assets) {
|
|
676
|
-
let t = u(
|
|
677
|
-
yield* c.runForEach(e.assets.list(), (r) => r.key in f.assets ? n.void : e.assets.open(r.key).pipe(n.flatMap((e) =>
|
|
728
|
+
let t = u(G);
|
|
729
|
+
yield* c.runForEach(e.assets.list(), (r) => r.key in f.assets ? n.void : e.assets.open(r.key).pipe(n.flatMap((e) => Ge(e, u(W))), n.flatMap((e) => {
|
|
678
730
|
let i = {
|
|
679
731
|
key: r.key,
|
|
680
732
|
sha256: e.sha256,
|
|
681
733
|
size: e.size,
|
|
682
734
|
contentType: e.contentType
|
|
683
735
|
};
|
|
684
|
-
return
|
|
685
|
-
}))).pipe(n.mapError((e) => e instanceof
|
|
736
|
+
return b++, x += e.size, f.assets[r.key] = !0, Ft(t, JSON.stringify(i)).pipe(n.andThen(q(d, f)));
|
|
737
|
+
}))).pipe(n.mapError((e) => e instanceof P ? e : new P({ reason: `asset export failed: ${String(e)}` })));
|
|
686
738
|
}
|
|
687
|
-
let
|
|
739
|
+
let S = {
|
|
688
740
|
formatVersion: 1,
|
|
689
741
|
createdAt: e.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
690
742
|
source: {
|
|
691
743
|
dialect: e.dialect,
|
|
692
|
-
schemaFingerprint:
|
|
744
|
+
schemaFingerprint: g(e.snapshot)
|
|
693
745
|
},
|
|
694
746
|
scope: t,
|
|
695
|
-
consistency:
|
|
747
|
+
consistency: h,
|
|
696
748
|
compression: r,
|
|
697
|
-
tables:
|
|
698
|
-
assets:
|
|
699
|
-
count:
|
|
700
|
-
totalBytes:
|
|
701
|
-
index:
|
|
749
|
+
tables: ee,
|
|
750
|
+
assets: b > 0 ? {
|
|
751
|
+
count: b,
|
|
752
|
+
totalBytes: x,
|
|
753
|
+
index: G
|
|
702
754
|
} : {
|
|
703
755
|
count: 0,
|
|
704
756
|
totalBytes: 0
|
|
@@ -707,23 +759,23 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
707
759
|
};
|
|
708
760
|
return yield* n.tryPromise({
|
|
709
761
|
try: async () => {
|
|
710
|
-
await (await import("node:fs/promises")).writeFile(u(
|
|
762
|
+
await (await import("node:fs/promises")).writeFile(u(H), rt(S));
|
|
711
763
|
},
|
|
712
|
-
catch: (e) => new
|
|
713
|
-
reason: `manifest write failed: ${
|
|
714
|
-
path:
|
|
764
|
+
catch: (e) => new P({
|
|
765
|
+
reason: `manifest write failed: ${J(e)}`,
|
|
766
|
+
path: H
|
|
715
767
|
})
|
|
716
768
|
}), yield* n.tryPromise({
|
|
717
769
|
try: async () => {
|
|
718
770
|
let e = await import("node:fs/promises"), t = await import("node:os");
|
|
719
|
-
await e.writeFile(u(
|
|
771
|
+
await e.writeFile(u(at), ct(S, { hostname: t.hostname() }));
|
|
720
772
|
},
|
|
721
|
-
catch: (e) => new
|
|
722
|
-
reason: `readme write failed: ${
|
|
723
|
-
path:
|
|
773
|
+
catch: (e) => new P({
|
|
774
|
+
reason: `readme write failed: ${J(e)}`,
|
|
775
|
+
path: at
|
|
724
776
|
})
|
|
725
|
-
}),
|
|
726
|
-
}),
|
|
777
|
+
}), S;
|
|
778
|
+
}), Bt = (e) => {
|
|
727
779
|
switch (e) {
|
|
728
780
|
case "id":
|
|
729
781
|
case "text":
|
|
@@ -742,12 +794,12 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
742
794
|
case "raw": return "raw";
|
|
743
795
|
default: return "string";
|
|
744
796
|
}
|
|
745
|
-
},
|
|
797
|
+
}, Vt = {
|
|
746
798
|
vector: /* @__PURE__ */ new Set(["postgres"]),
|
|
747
799
|
array: /* @__PURE__ */ new Set(["postgres"]),
|
|
748
800
|
interval: /* @__PURE__ */ new Set(["postgres"])
|
|
749
|
-
},
|
|
750
|
-
let i =
|
|
801
|
+
}, Ht = (e, t, n, r) => {
|
|
802
|
+
let i = Bt(n);
|
|
751
803
|
if (i === "raw") return {
|
|
752
804
|
table: e,
|
|
753
805
|
column: t,
|
|
@@ -755,7 +807,7 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
755
807
|
severity: "block",
|
|
756
808
|
reason: `raw() is verbatim source-dialect SQL — it cannot be assumed valid on ${r}`
|
|
757
809
|
};
|
|
758
|
-
let a =
|
|
810
|
+
let a = Vt[i];
|
|
759
811
|
return !a || a.has(r) ? null : i === "array" || i === "interval" ? {
|
|
760
812
|
table: e,
|
|
761
813
|
column: t,
|
|
@@ -769,55 +821,87 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
769
821
|
severity: "block",
|
|
770
822
|
reason: `${i} columns require a ${[...a].join("/")} target; ${r} has no compatible column type`
|
|
771
823
|
};
|
|
772
|
-
},
|
|
824
|
+
}, Ut = (e, t, n) => {
|
|
773
825
|
if (t === n) return [];
|
|
774
826
|
let r = [];
|
|
775
827
|
for (let t of e) for (let [e, i] of Object.entries(t.columnTypes)) {
|
|
776
|
-
let a =
|
|
828
|
+
let a = Ht(t.name, e, i, n);
|
|
777
829
|
a && r.push(a);
|
|
778
830
|
}
|
|
779
831
|
return r;
|
|
780
|
-
},
|
|
781
|
-
let
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
832
|
+
}, Wt = (e) => `[${e.severity}] ${e.table}.${e.column} (${e.type}): ${e.reason}`, Gt = (e) => {
|
|
833
|
+
let t = e.constraint ?? e.column, n = t === void 0 ? "" : ` ${t}`;
|
|
834
|
+
switch (e.kind) {
|
|
835
|
+
case "foreignKey": return `foreign key${n}: the referenced row does not exist (import it first, or check the bundle's table order)`;
|
|
836
|
+
case "foreignKeyInUse": return `foreign key${n}: the row is still referenced by other rows`;
|
|
837
|
+
case "unique": return `unique constraint${n}: a row with this value already exists`;
|
|
838
|
+
case "notNull": return `not-null constraint${n}: the column requires a value`;
|
|
839
|
+
case "check": return `check constraint${n}: the row does not satisfy it`;
|
|
840
|
+
}
|
|
841
|
+
}, Kt = (e) => {
|
|
842
|
+
let t = [e.dbCode, e.dbErrno ?? e.dbNumber].filter((e) => e !== void 0).map(String);
|
|
843
|
+
return t.length > 0 ? t.join("/") : void 0;
|
|
844
|
+
}, qt = (e) => {
|
|
845
|
+
let t = d(e), n = l(t), r = Kt(t), i = r === void 0 ? "" : ` [${r}]`;
|
|
846
|
+
if (n !== void 0) return `${Gt(n)}${i}`;
|
|
847
|
+
let a = typeof t.dbMessage == "string" ? t.dbMessage : void 0, o = e?.message, s = typeof o == "string" && o.length > 0 ? o : String(e), c = a !== void 0 && a !== s && !a.startsWith("SqlError:") ? a : s, u = c === "Failed to execute statement" ? `the database refused the write and the driver gave no detail${i}` : `${c}${i}`;
|
|
848
|
+
return u.length <= 300 ? u : `${u.slice(0, 300)}…`;
|
|
849
|
+
}, Jt = (e, t) => e.tables.find((e) => e.name === t), Yt = (e, t) => new Map((Jt(e, t)?.columns ?? []).map((e) => [e.name, e])), Xt = (e, t) => {
|
|
850
|
+
let n = [];
|
|
851
|
+
for (let r of [...e.tables].sort((e, t) => e.name.localeCompare(t.name))) {
|
|
852
|
+
if (Jt(t, r.name) === void 0) {
|
|
853
|
+
n.push(`table '${r.name}' is in the bundle but MISSING from the target — its rows have nowhere to go`);
|
|
854
|
+
continue;
|
|
791
855
|
}
|
|
856
|
+
let e = Yt(t, r.name), i = new Map(r.columns.map((e) => [e.name, e]));
|
|
857
|
+
for (let [t, a] of i) {
|
|
858
|
+
let i = e.get(t);
|
|
859
|
+
if (i === void 0) {
|
|
860
|
+
n.push(`${r.name}.${t}: in the bundle, MISSING from the target — the value has nowhere to go`);
|
|
861
|
+
continue;
|
|
862
|
+
}
|
|
863
|
+
i.type !== a.type && n.push(`${r.name}.${t}: type '${a.type}' (bundle) vs '${i.type}' (target)`);
|
|
864
|
+
}
|
|
865
|
+
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`);
|
|
792
866
|
}
|
|
793
|
-
return n.length > 30 ? [...n.slice(0, 30), `… and ${n.length - 30} more difference(s)`] : n
|
|
794
|
-
},
|
|
795
|
-
let r =
|
|
796
|
-
|
|
867
|
+
return n.length > 30 ? [...n.slice(0, 30), `… and ${n.length - 30} more difference(s)`] : n;
|
|
868
|
+
}, Zt = (e, t, n) => {
|
|
869
|
+
let r = g(t);
|
|
870
|
+
if (r === e || n === void 0) return {
|
|
797
871
|
drifted: !1,
|
|
798
872
|
bundleFingerprint: e,
|
|
799
873
|
targetFingerprint: r,
|
|
800
874
|
diff: []
|
|
801
|
-
}
|
|
802
|
-
|
|
875
|
+
};
|
|
876
|
+
let i = Xt(n, t);
|
|
877
|
+
return {
|
|
878
|
+
drifted: i.length > 0,
|
|
803
879
|
bundleFingerprint: e,
|
|
804
880
|
targetFingerprint: r,
|
|
805
|
-
diff:
|
|
881
|
+
diff: i
|
|
806
882
|
};
|
|
807
|
-
},
|
|
883
|
+
}, Qt = (e) => n.tryPromise({
|
|
808
884
|
try: async () => {
|
|
809
885
|
let t = await import("node:fs/promises"), n = await import("node:path");
|
|
810
|
-
return
|
|
886
|
+
return it(await t.readFile(n.join(e, H), "utf8"));
|
|
811
887
|
},
|
|
812
|
-
catch: (e) => new
|
|
888
|
+
catch: (e) => new P({
|
|
813
889
|
reason: `manifest read/parse failed: ${String(e?.message ?? e)}`,
|
|
814
|
-
path:
|
|
890
|
+
path: H
|
|
815
891
|
})
|
|
816
|
-
}),
|
|
817
|
-
let i = yield* n.promise(() => import("node:path")), a = yield*
|
|
818
|
-
if (a.formatVersion !== 1) return yield* n.fail(new
|
|
892
|
+
}), $t = (t) => n.gen(function* () {
|
|
893
|
+
let i = yield* n.promise(() => import("node:path")), a = yield* Qt(t.bundleDir);
|
|
894
|
+
if (a.formatVersion !== 1) return yield* n.fail(new P({ reason: `unsupported bundle formatVersion ${a.formatVersion} (expected 1)` }));
|
|
895
|
+
if (t.tables !== void 0) {
|
|
896
|
+
let e = new Set(a.tables.map((e) => e.name)), r = t.tables.filter((t) => !e.has(t));
|
|
897
|
+
if (r.length > 0) return yield* n.fail(new P({ reason: `--tables names ${r.length} table(s) this bundle does not carry: ${r.join(", ")}. The bundle carries: ${[...e].sort().join(", ")}.` }));
|
|
898
|
+
}
|
|
899
|
+
let o = t.tables === void 0 ? void 0 : new Set(t.tables), s = o === void 0 ? a : {
|
|
900
|
+
...a,
|
|
901
|
+
tables: a.tables.filter((e) => o.has(e.name))
|
|
902
|
+
};
|
|
819
903
|
if (t.targetSnapshot) {
|
|
820
|
-
let e = { tables:
|
|
904
|
+
let e = { tables: s.tables.map((e) => ({
|
|
821
905
|
name: e.name,
|
|
822
906
|
indexes: [],
|
|
823
907
|
columns: Object.entries(e.columnTypes).map(([e, t]) => ({
|
|
@@ -827,9 +911,9 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
827
911
|
unique: !1,
|
|
828
912
|
hasDefault: !1
|
|
829
913
|
}))
|
|
830
|
-
})) }, r =
|
|
914
|
+
})) }, r = Zt(s.source.schemaFingerprint, t.targetSnapshot, e);
|
|
831
915
|
if (r.drifted) {
|
|
832
|
-
if (!t.force) return yield* n.fail(new
|
|
916
|
+
if (!t.force) return yield* n.fail(new Le({
|
|
833
917
|
bundleFingerprint: r.bundleFingerprint,
|
|
834
918
|
targetFingerprint: r.targetFingerprint,
|
|
835
919
|
diff: r.diff
|
|
@@ -837,45 +921,46 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
837
921
|
t.onWarn?.(`schema drift: the target schema (fingerprint ${r.targetFingerprint.slice(0, 16)}) differs from the bundle's source (${r.bundleFingerprint.slice(0, 16)}); importing anyway (--force). ${r.diff.join("; ")}`);
|
|
838
922
|
}
|
|
839
923
|
}
|
|
840
|
-
if (t.targetDialect && t.targetDialect !==
|
|
841
|
-
let e =
|
|
842
|
-
if (e.length > 0 && !t.allowIncompatible) return yield* n.fail(new
|
|
843
|
-
sourceDialect:
|
|
924
|
+
if (t.targetDialect && t.targetDialect !== s.source.dialect) {
|
|
925
|
+
let e = Ut(s.tables, s.source.dialect, t.targetDialect).filter((e) => e.severity === "block");
|
|
926
|
+
if (e.length > 0 && !t.allowIncompatible) return yield* n.fail(new Be({
|
|
927
|
+
sourceDialect: s.source.dialect,
|
|
844
928
|
targetDialect: t.targetDialect,
|
|
845
|
-
blocking: e.map(
|
|
929
|
+
blocking: e.map(Wt)
|
|
846
930
|
}));
|
|
847
931
|
}
|
|
848
|
-
let
|
|
849
|
-
if (
|
|
850
|
-
mode:
|
|
851
|
-
reason: `replace refuses a partial bundle (scope='${
|
|
932
|
+
let l = t.verify ?? !0, u = t.mode ?? "upsert", d = t.onConflict ?? "skip", f = t.ledgerPath ?? i.join(t.bundleDir, ".import..ledger.json"), m = yield* mt(f);
|
|
933
|
+
if (u === "replace" && s.scope.kind !== "all") return yield* n.fail(new ze({
|
|
934
|
+
mode: u,
|
|
935
|
+
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.`
|
|
852
936
|
}));
|
|
853
|
-
|
|
937
|
+
if (t.dryRun === !0) return s;
|
|
938
|
+
let h = (e, t, n) => {
|
|
854
939
|
let r = t.name, i = t.primaryKey;
|
|
855
|
-
return
|
|
856
|
-
},
|
|
940
|
+
return u === "append" ? d === "fail" ? e.insert(r, n) : e.insertIgnore(r, n, { conflictColumns: [i] }) : u === "replace" ? e.insert(r, n) : e.upsert(r, n, { conflictColumns: [i] });
|
|
941
|
+
}, g = (e, t, r = !0) => n.tryPromise({
|
|
857
942
|
try: e,
|
|
858
|
-
catch: (e) =>
|
|
859
|
-
}).pipe(n.map(() => !0), n.catchAll((e) => n.sync(() => (t.reason = e, !1)))),
|
|
943
|
+
catch: (e) => qt(e)
|
|
944
|
+
}).pipe(n.map(() => !0), n.catchAll((e) => n.sync(() => (r && (t.reason = e), !1)))), _ = (e) => {
|
|
860
945
|
let t = Object.entries(e.entry.columnTypes).filter(([t, n]) => n === "reference" && e.row[t] !== null && e.row[t] !== void 0).map(([e]) => e);
|
|
861
946
|
if (t.length !== 0) return {
|
|
862
947
|
...e.row,
|
|
863
948
|
...Object.fromEntries(t.map((e) => [e, null]))
|
|
864
949
|
};
|
|
865
|
-
},
|
|
950
|
+
}, v = (e, t) => n.gen(function* () {
|
|
866
951
|
let r = t, i = [], a = !0;
|
|
867
952
|
for (; r.length > 0 && a;) {
|
|
868
953
|
a = !1;
|
|
869
954
|
let t = [];
|
|
870
|
-
for (let n of r) (yield*
|
|
955
|
+
for (let n of r) (yield* g(() => h(e, n.entry, n.row), n)) ? a = !0 : t.push(n);
|
|
871
956
|
if (!a && t.length > 0) for (let n = t.length - 1; n >= 0; n--) {
|
|
872
|
-
let r = t[n], o =
|
|
873
|
-
o !== void 0 && (yield*
|
|
957
|
+
let r = t[n], o = _(r);
|
|
958
|
+
o !== void 0 && (yield* g(() => h(e, r.entry, o), r, !1)) && (i.push(r), t.splice(n, 1), a = !0);
|
|
874
959
|
}
|
|
875
960
|
r = t;
|
|
876
961
|
}
|
|
877
|
-
for (let t of i) (yield*
|
|
878
|
-
if (r.length > 0) return yield* n.fail(new
|
|
962
|
+
for (let t of i) (yield* g(() => e.upsert(t.entry.name, t.row, { conflictColumns: [t.entry.primaryKey] }), t)) || (r = [...r, t]);
|
|
963
|
+
if (r.length > 0) return yield* n.fail(new Ie({
|
|
879
964
|
rows: r.slice(0, 20).map((e) => ({
|
|
880
965
|
table: e.entry.name,
|
|
881
966
|
id: String(e.row[e.entry.primaryKey]),
|
|
@@ -883,59 +968,59 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
883
968
|
})),
|
|
884
969
|
totalCount: r.length
|
|
885
970
|
}));
|
|
886
|
-
}),
|
|
887
|
-
if (
|
|
888
|
-
for (let t of [...
|
|
889
|
-
try: () => e.deleteMany(t.name, { where:
|
|
890
|
-
catch: (e) => new
|
|
971
|
+
}), y = (e, r) => n.gen(function* () {
|
|
972
|
+
if (u === "replace" && !(r && m.truncated)) {
|
|
973
|
+
for (let t of [...s.tables].reverse()) yield* n.tryPromise({
|
|
974
|
+
try: () => e.deleteMany(t.name, { where: p(t.primaryKey) }),
|
|
975
|
+
catch: (e) => new P({ reason: `truncate ${t.name} failed: ${String(e?.message ?? e)}` })
|
|
891
976
|
});
|
|
892
|
-
r && (
|
|
977
|
+
r && (m.truncated = !0, yield* q(f, m));
|
|
893
978
|
}
|
|
894
|
-
let
|
|
895
|
-
for (let
|
|
896
|
-
let
|
|
897
|
-
if (r &&
|
|
898
|
-
|
|
979
|
+
let a = [], o = [], d = [], g = s.tables.length;
|
|
980
|
+
for (let p = 0; p < s.tables.length; p++) {
|
|
981
|
+
let _ = s.tables[p];
|
|
982
|
+
if (r && _.name in m.tables) {
|
|
983
|
+
d.push(_.name), K(t.onProgress, {
|
|
899
984
|
phase: "import",
|
|
900
|
-
table:
|
|
901
|
-
index:
|
|
902
|
-
tableCount:
|
|
985
|
+
table: _.name,
|
|
986
|
+
index: p,
|
|
987
|
+
tableCount: g,
|
|
903
988
|
event: "done",
|
|
904
|
-
rowsDone:
|
|
905
|
-
total:
|
|
989
|
+
rowsDone: m.tables[_.name].rowCount,
|
|
990
|
+
total: _.rowCount
|
|
906
991
|
});
|
|
907
992
|
continue;
|
|
908
993
|
}
|
|
909
|
-
let
|
|
910
|
-
|
|
994
|
+
let v = i.join(t.bundleDir, _.file);
|
|
995
|
+
K(t.onProgress, {
|
|
911
996
|
phase: "import",
|
|
912
|
-
table:
|
|
913
|
-
index:
|
|
914
|
-
tableCount:
|
|
997
|
+
table: _.name,
|
|
998
|
+
index: p,
|
|
999
|
+
tableCount: g,
|
|
915
1000
|
event: "start",
|
|
916
1001
|
rowsDone: 0,
|
|
917
|
-
total:
|
|
1002
|
+
total: _.rowCount
|
|
918
1003
|
});
|
|
919
|
-
let
|
|
920
|
-
let { createHash: r } = yield* n.promise(() => import("node:crypto")), i = r("sha256"), o = 0,
|
|
1004
|
+
let y = a.length, ee = n.gen(function* () {
|
|
1005
|
+
let { createHash: r } = yield* n.promise(() => import("node:crypto")), i = r("sha256"), o = 0, l = C("bundle", () => T(v).pipe(Ze(s.compression))).pipe(c.tap((e) => n.sync(() => i.update(e)))), d = async (t) => {
|
|
921
1006
|
let n = {
|
|
922
|
-
entry:
|
|
1007
|
+
entry: _,
|
|
923
1008
|
row: t,
|
|
924
1009
|
reason: ""
|
|
925
1010
|
};
|
|
926
1011
|
try {
|
|
927
|
-
await
|
|
1012
|
+
await h(e, _, t);
|
|
928
1013
|
} catch (e) {
|
|
929
|
-
n.reason =
|
|
1014
|
+
n.reason = qt(e), a.push(n);
|
|
930
1015
|
}
|
|
931
1016
|
}, f = yield* n.promise(async () => {
|
|
932
1017
|
let n = t.copyLoader;
|
|
933
1018
|
if (!n || n.dialect !== "postgres" || t.atomic) return !1;
|
|
934
|
-
if (
|
|
935
|
-
if (
|
|
1019
|
+
if (u === "replace") return !0;
|
|
1020
|
+
if (u !== "upsert") return !1;
|
|
936
1021
|
try {
|
|
937
1022
|
return (await e.query({
|
|
938
|
-
table:
|
|
1023
|
+
table: _.name,
|
|
939
1024
|
predicate: void 0,
|
|
940
1025
|
order: [],
|
|
941
1026
|
take: 1,
|
|
@@ -945,25 +1030,25 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
945
1030
|
} catch {
|
|
946
1031
|
return !1;
|
|
947
1032
|
}
|
|
948
|
-
}),
|
|
949
|
-
if (
|
|
950
|
-
let e =
|
|
951
|
-
|
|
1033
|
+
}), p = Object.keys(_.columnTypes), m = Math.max(1, t.copyBatchSize ?? 5e3), g = [], y = async () => {
|
|
1034
|
+
if (g.length === 0) return;
|
|
1035
|
+
let e = g;
|
|
1036
|
+
g = [];
|
|
952
1037
|
try {
|
|
953
1038
|
await t.copyLoader.copyInto({
|
|
954
|
-
table:
|
|
955
|
-
columns:
|
|
956
|
-
columnTypes:
|
|
1039
|
+
table: _.name,
|
|
1040
|
+
columns: p,
|
|
1041
|
+
columnTypes: _.columnTypes,
|
|
957
1042
|
rows: e
|
|
958
1043
|
});
|
|
959
1044
|
} catch (n) {
|
|
960
|
-
f = !1, t.onWarn?.(`bulk COPY into ${
|
|
1045
|
+
f = !1, t.onWarn?.(`bulk COPY into ${_.name} failed (${String(n?.message ?? n)}); falling back to per-row writes for this table`);
|
|
961
1046
|
for (let t of e) await d(t);
|
|
962
1047
|
}
|
|
963
1048
|
};
|
|
964
|
-
return yield* c.runForEach(
|
|
1049
|
+
return yield* c.runForEach(kt(l, _.name, _.columnTypes), (e) => n.promise(async () => {
|
|
965
1050
|
if (o++, f) {
|
|
966
|
-
|
|
1051
|
+
g.push(e), g.length >= m && await y();
|
|
967
1052
|
return;
|
|
968
1053
|
}
|
|
969
1054
|
await d(e);
|
|
@@ -971,71 +1056,75 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
971
1056
|
rowCount: o,
|
|
972
1057
|
checksum: i.digest("hex")
|
|
973
1058
|
};
|
|
974
|
-
}), { rowCount: b, checksum: x } = yield*
|
|
975
|
-
if (
|
|
976
|
-
if (x !==
|
|
977
|
-
what: `table ${
|
|
978
|
-
expected:
|
|
1059
|
+
}), { rowCount: b, checksum: x } = yield* ft("import", _.name, p, g, (e) => e.rowCount, ee);
|
|
1060
|
+
if (l) {
|
|
1061
|
+
if (x !== _.checksum) return yield* n.fail(new F({
|
|
1062
|
+
what: `table ${_.name} checksum`,
|
|
1063
|
+
expected: _.checksum,
|
|
979
1064
|
actual: x
|
|
980
1065
|
}));
|
|
981
|
-
if (b !==
|
|
982
|
-
what: `table ${
|
|
983
|
-
expected: String(
|
|
1066
|
+
if (b !== _.rowCount) return yield* n.fail(new F({
|
|
1067
|
+
what: `table ${_.name} rowCount`,
|
|
1068
|
+
expected: String(_.rowCount),
|
|
984
1069
|
actual: String(b)
|
|
985
1070
|
}));
|
|
986
1071
|
}
|
|
987
|
-
|
|
1072
|
+
K(t.onProgress, {
|
|
988
1073
|
phase: "import",
|
|
989
|
-
table:
|
|
990
|
-
index:
|
|
991
|
-
tableCount:
|
|
1074
|
+
table: _.name,
|
|
1075
|
+
index: p,
|
|
1076
|
+
tableCount: g,
|
|
992
1077
|
event: "done",
|
|
993
1078
|
rowsDone: b,
|
|
994
|
-
total:
|
|
995
|
-
}), r && (
|
|
996
|
-
name:
|
|
1079
|
+
total: _.rowCount
|
|
1080
|
+
}), r && (a.length > y ? o.push({
|
|
1081
|
+
name: _.name,
|
|
997
1082
|
rowCount: b
|
|
998
|
-
}) : (
|
|
1083
|
+
}) : (m.tables[_.name] = {
|
|
999
1084
|
rowCount: b,
|
|
1000
|
-
checksum:
|
|
1001
|
-
}, yield*
|
|
1085
|
+
checksum: _.checksum
|
|
1086
|
+
}, yield* q(f, m)));
|
|
1002
1087
|
}
|
|
1003
|
-
if (
|
|
1004
|
-
for (let e of
|
|
1005
|
-
let t =
|
|
1006
|
-
|
|
1088
|
+
if (a.length > 0 && (yield* v(e, a)), r && o.length > 0) {
|
|
1089
|
+
for (let e of o) {
|
|
1090
|
+
let t = s.tables.find((t) => t.name === e.name);
|
|
1091
|
+
m.tables[e.name] = {
|
|
1007
1092
|
rowCount: e.rowCount,
|
|
1008
1093
|
checksum: t.checksum
|
|
1009
1094
|
};
|
|
1010
1095
|
}
|
|
1011
|
-
yield*
|
|
1096
|
+
yield* q(f, m);
|
|
1097
|
+
}
|
|
1098
|
+
if (d.length > 0) {
|
|
1099
|
+
let e = d.length === s.tables.length;
|
|
1100
|
+
t.onWarn?.(`resume: ${d.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${e ? " — that is every table in the bundle, so nothing was written at all" : ""}. Tables: ${d.join(", ")}. Delete ${f} to force a full re-import.`);
|
|
1012
1101
|
}
|
|
1013
1102
|
});
|
|
1014
1103
|
if (t.atomic ? yield* n.tryPromise({
|
|
1015
1104
|
try: () => t.store.transactional(async (t) => {
|
|
1016
|
-
let i = await n.runPromiseExit(
|
|
1105
|
+
let i = await n.runPromiseExit(y(t, !1));
|
|
1017
1106
|
if (r.isFailure(i)) throw Error(e.pretty(i.cause));
|
|
1018
1107
|
}),
|
|
1019
|
-
catch: (e) => new
|
|
1020
|
-
}) : yield*
|
|
1021
|
-
let e = t.assets, r = i.join(t.bundleDir,
|
|
1022
|
-
yield* c.runForEach(
|
|
1023
|
-
|
|
1024
|
-
})), n.andThen(
|
|
1108
|
+
catch: (e) => new P({ reason: `atomic import failed (rolled back): ${String(e?.message ?? e)}` })
|
|
1109
|
+
}) : yield* y(t.store, !0), t.assets && s.assets.index && !t.assetsAlreadyRestored) {
|
|
1110
|
+
let e = t.assets, r = i.join(t.bundleDir, W), a = i.join(t.bundleDir, s.assets.index ?? "assets/index.ndjson"), o = C("bundle", () => T(a)).pipe(c.decodeText(), c.splitLines, c.filter((e) => e.trim().length > 0), c.map((e) => JSON.parse(e)));
|
|
1111
|
+
yield* c.runForEach(o, (t) => t.key in m.assets ? n.void : Ke(e, r, t).pipe(n.andThen(n.sync(() => {
|
|
1112
|
+
m.assets[t.key] = !0;
|
|
1113
|
+
})), n.andThen(q(f, m))));
|
|
1025
1114
|
}
|
|
1026
|
-
return
|
|
1027
|
-
}),
|
|
1115
|
+
return s;
|
|
1116
|
+
}), en = (e) => e, tn = (e) => {
|
|
1028
1117
|
if (e.allowLive || e.liveInstance === null || e.sameTarget === "different") return { refuse: !1 };
|
|
1029
1118
|
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.";
|
|
1030
1119
|
return {
|
|
1031
1120
|
refuse: !0,
|
|
1032
1121
|
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).`
|
|
1033
1122
|
};
|
|
1034
|
-
},
|
|
1123
|
+
}, Y = Buffer.from("VBUNDLE2"), nn = 0, rn = 1, an = 2, on = 32, sn = async (e) => {
|
|
1035
1124
|
let t = [], n = async (r) => {
|
|
1036
1125
|
let i;
|
|
1037
1126
|
try {
|
|
1038
|
-
i = await
|
|
1127
|
+
i = await le(O(e, r), { withFileTypes: !0 });
|
|
1039
1128
|
} catch {
|
|
1040
1129
|
return;
|
|
1041
1130
|
}
|
|
@@ -1045,22 +1134,22 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
|
|
|
1045
1134
|
}
|
|
1046
1135
|
};
|
|
1047
1136
|
return await n(""), t.sort();
|
|
1048
|
-
},
|
|
1137
|
+
}, cn = (e, t) => {
|
|
1049
1138
|
let n = Buffer.from(e, "utf8"), r = Buffer.alloc(5 + n.length + 8);
|
|
1050
|
-
return r.writeUInt8(
|
|
1051
|
-
},
|
|
1139
|
+
return r.writeUInt8(rn, 0), r.writeUInt32BE(n.length, 1), n.copy(r, 5), r.writeBigUInt64BE(BigInt(t), 5 + n.length), r;
|
|
1140
|
+
}, ln = (e, t, n) => {
|
|
1052
1141
|
let r = Buffer.from(e, "utf8"), i = Buffer.from(t, "utf8"), a = Buffer.alloc(5 + r.length + 2 + i.length + 8), o = 0;
|
|
1053
|
-
return a.writeUInt8(
|
|
1142
|
+
return a.writeUInt8(an, 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;
|
|
1054
1143
|
};
|
|
1055
|
-
async function*
|
|
1056
|
-
yield new Uint8Array(
|
|
1057
|
-
for (let t of await
|
|
1058
|
-
let n = O(e.dir, t), r = await
|
|
1059
|
-
if (yield new Uint8Array(
|
|
1144
|
+
async function* un(e) {
|
|
1145
|
+
yield new Uint8Array(Y);
|
|
1146
|
+
for (let t of await sn(e.dir)) {
|
|
1147
|
+
let n = O(e.dir, t), r = await ue(n);
|
|
1148
|
+
if (yield new Uint8Array(cn(t, r.size)), r.size > 0) for await (let e of T(n)) yield new Uint8Array(e);
|
|
1060
1149
|
}
|
|
1061
1150
|
if (e.blobs) for await (let t of e.blobs.list()) {
|
|
1062
|
-
yield new Uint8Array(
|
|
1063
|
-
let n =
|
|
1151
|
+
yield new Uint8Array(ln(t.key, t.contentType, t.size));
|
|
1152
|
+
let n = y("sha256"), r = 0;
|
|
1064
1153
|
for await (let i of e.blobs.open(t.key)) {
|
|
1065
1154
|
let e = Buffer.from(i);
|
|
1066
1155
|
n.update(e), r += e.length, yield new Uint8Array(e);
|
|
@@ -1068,11 +1157,11 @@ async function* en(e) {
|
|
|
1068
1157
|
if (r !== t.size) throw Error(`blob '${t.key}' size mismatch: header ${t.size}, streamed ${r}`);
|
|
1069
1158
|
yield new Uint8Array(n.digest());
|
|
1070
1159
|
}
|
|
1071
|
-
yield new Uint8Array(Buffer.from([
|
|
1160
|
+
yield new Uint8Array(Buffer.from([nn]));
|
|
1072
1161
|
}
|
|
1073
|
-
var
|
|
1074
|
-
let n =
|
|
1075
|
-
await
|
|
1162
|
+
var dn = (e, t) => new Promise((n, r) => e.write(t, (e) => e ? r(e) : n())), fn = (e) => new Promise((t) => e.end(t)), pn = async (e, t) => {
|
|
1163
|
+
let n = me(t.dir);
|
|
1164
|
+
await D(n, { recursive: !0 });
|
|
1076
1165
|
let r = Buffer.alloc(0), i = e[Symbol.asyncIterator](), a = !1, o = async () => {
|
|
1077
1166
|
let { value: e, done: t } = await i.next();
|
|
1078
1167
|
return t ? (a = !0, !1) : (r = r.length === 0 ? Buffer.from(e) : Buffer.concat([r, Buffer.from(e)]), !0);
|
|
@@ -1083,28 +1172,28 @@ var tn = (e, t) => new Promise((n, r) => e.write(t, (e) => e ? r(e) : n())), nn
|
|
|
1083
1172
|
return r = r.subarray(e), t;
|
|
1084
1173
|
};
|
|
1085
1174
|
try {
|
|
1086
|
-
if (await s(
|
|
1175
|
+
if (await s(Y.length), !c(Y.length).equals(Y)) throw Error("not a bundle archive (bad magic)");
|
|
1087
1176
|
for (;;) {
|
|
1088
1177
|
await s(1);
|
|
1089
1178
|
let e = c(1).readUInt8(0);
|
|
1090
|
-
if (e ===
|
|
1091
|
-
if (e ===
|
|
1179
|
+
if (e === nn) return;
|
|
1180
|
+
if (e === rn) {
|
|
1092
1181
|
await s(4);
|
|
1093
1182
|
let e = c(4).readUInt32BE(0);
|
|
1094
1183
|
await s(e);
|
|
1095
1184
|
let t = c(e).toString("utf8");
|
|
1096
1185
|
await s(8);
|
|
1097
|
-
let i = c(8).readBigUInt64BE(0), a =
|
|
1098
|
-
if (a !== n && !a.startsWith(n +
|
|
1099
|
-
await
|
|
1100
|
-
let o =
|
|
1186
|
+
let i = c(8).readBigUInt64BE(0), a = me(n, t);
|
|
1187
|
+
if (a !== n && !a.startsWith(n + he)) throw Error(`archive path '${t}' escapes the destination`);
|
|
1188
|
+
await D(pe(a), { recursive: !0 });
|
|
1189
|
+
let o = E(a);
|
|
1101
1190
|
for (; i > 0n;) {
|
|
1102
1191
|
r.length === 0 && await s(1);
|
|
1103
1192
|
let e = i < BigInt(r.length) ? Number(i) : r.length;
|
|
1104
|
-
await
|
|
1193
|
+
await dn(o, c(e)), i -= BigInt(e);
|
|
1105
1194
|
}
|
|
1106
|
-
await
|
|
1107
|
-
} else if (e ===
|
|
1195
|
+
await fn(o);
|
|
1196
|
+
} else if (e === an) {
|
|
1108
1197
|
await s(4);
|
|
1109
1198
|
let e = c(4).readUInt32BE(0);
|
|
1110
1199
|
await s(e);
|
|
@@ -1114,24 +1203,24 @@ var tn = (e, t) => new Promise((n, r) => e.write(t, (e) => e ? r(e) : n())), nn
|
|
|
1114
1203
|
await s(i);
|
|
1115
1204
|
let a = c(i).toString("utf8");
|
|
1116
1205
|
await s(8);
|
|
1117
|
-
let o = c(8).readBigUInt64BE(0), l = await t.blobs.has(n), u =
|
|
1206
|
+
let o = c(8).readBigUInt64BE(0), l = await t.blobs.has(n), u = y("sha256"), d = new ie(), f = l ? Promise.resolve() : t.blobs.write(n, d, {
|
|
1118
1207
|
contentType: a,
|
|
1119
1208
|
size: Number(o)
|
|
1120
1209
|
}), p = o;
|
|
1121
1210
|
for (; p > 0n;) {
|
|
1122
1211
|
r.length === 0 && await s(1);
|
|
1123
1212
|
let e = p < BigInt(r.length) ? Number(p) : r.length, t = c(e);
|
|
1124
|
-
u.update(t), l || await
|
|
1213
|
+
u.update(t), l || await dn(d, t), p -= BigInt(e);
|
|
1125
1214
|
}
|
|
1126
|
-
l ? d.destroy() : await
|
|
1127
|
-
let m = c(
|
|
1215
|
+
l ? d.destroy() : await fn(d), await f, await s(on);
|
|
1216
|
+
let m = c(on);
|
|
1128
1217
|
if (!u.digest().equals(m)) throw Error(`blob '${n}' failed integrity (sha mismatch)`);
|
|
1129
1218
|
} else throw Error(`unknown bundle entry kind ${e}`);
|
|
1130
1219
|
}
|
|
1131
1220
|
} finally {
|
|
1132
1221
|
a || await Promise.resolve(i.return?.()).catch(() => void 0);
|
|
1133
1222
|
}
|
|
1134
|
-
},
|
|
1223
|
+
}, mn = async (e, t) => {
|
|
1135
1224
|
let n = new Set(t), r = /* @__PURE__ */ new Map(), i = Buffer.alloc(0), a = e[Symbol.asyncIterator](), o = async () => {
|
|
1136
1225
|
let { value: e, done: t } = await a.next();
|
|
1137
1226
|
return !t && (i = i.length === 0 ? Buffer.from(e) : Buffer.concat([i, Buffer.from(e)]), !0);
|
|
@@ -1150,11 +1239,11 @@ var tn = (e, t) => new Promise((n, r) => e.write(t, (e) => e ? r(e) : n())), nn
|
|
|
1150
1239
|
}
|
|
1151
1240
|
return Buffer.concat(t);
|
|
1152
1241
|
};
|
|
1153
|
-
if (!await s(
|
|
1242
|
+
if (!await s(Y.length) || !c(Y.length).equals(Y)) throw Error("not a bundle archive (bad magic)");
|
|
1154
1243
|
for (; await s(1);) {
|
|
1155
1244
|
let e = c(1).readUInt8(0);
|
|
1156
|
-
if (e ===
|
|
1157
|
-
if (e !==
|
|
1245
|
+
if (e === nn || e === an) break;
|
|
1246
|
+
if (e !== rn) throw Error(`unknown bundle entry kind ${e}`);
|
|
1158
1247
|
if (!await s(4)) break;
|
|
1159
1248
|
let t = c(4).readUInt32BE(0);
|
|
1160
1249
|
if (!await s(t)) break;
|
|
@@ -1164,20 +1253,20 @@ var tn = (e, t) => new Promise((n, r) => e.write(t, (e) => e ? r(e) : n())), nn
|
|
|
1164
1253
|
if (a === null || n.has(i) && (r.set(i, a), n.delete(i), n.size === 0)) break;
|
|
1165
1254
|
}
|
|
1166
1255
|
return r;
|
|
1167
|
-
},
|
|
1256
|
+
}, hn = (e, t) => pn(e, {
|
|
1168
1257
|
dir: t,
|
|
1169
|
-
blobs:
|
|
1170
|
-
}),
|
|
1258
|
+
blobs: gn(t)
|
|
1259
|
+
}), gn = (e) => {
|
|
1171
1260
|
let t = /* @__PURE__ */ new Set();
|
|
1172
1261
|
return {
|
|
1173
1262
|
has: async (e) => t.has(e),
|
|
1174
1263
|
write: async (n, r, i) => {
|
|
1175
1264
|
let a = await import("node:fs/promises"), { pipeline: o } = await import("node:stream/promises"), { Transform: s } = await import("node:stream"), c = O(e, "assets");
|
|
1176
|
-
await
|
|
1177
|
-
let l =
|
|
1265
|
+
await D(c, { recursive: !0 });
|
|
1266
|
+
let l = y("sha256"), u = 0, d = new s({ transform(e, t, n) {
|
|
1178
1267
|
l.update(e), u += e.length, n(null, e);
|
|
1179
1268
|
} }), f = O(c, `.tmp-${Date.now()}-${t.size}`);
|
|
1180
|
-
await o(r, d,
|
|
1269
|
+
await o(r, d, E(f));
|
|
1181
1270
|
let p = l.digest("hex");
|
|
1182
1271
|
await a.rename(f, O(c, p)), await a.appendFile(O(c, "index.ndjson"), `${JSON.stringify({
|
|
1183
1272
|
key: n,
|
|
@@ -1187,33 +1276,33 @@ var tn = (e, t) => new Promise((n, r) => e.write(t, (e) => e ? r(e) : n())), nn
|
|
|
1187
1276
|
})}\n`), t.add(n);
|
|
1188
1277
|
}
|
|
1189
1278
|
};
|
|
1190
|
-
},
|
|
1279
|
+
}, _n = ge(x), vn = Buffer.from("VENC1\0\0\0"), yn = 32, bn = 16, xn = 7, X = 16, Sn = 262144, Z = {
|
|
1191
1280
|
N: 32768,
|
|
1192
1281
|
r: 8,
|
|
1193
1282
|
p: 1
|
|
1194
|
-
},
|
|
1283
|
+
}, Cn = 134217728, Q = 43, wn = (e, t, n) => {
|
|
1195
1284
|
let r = Buffer.alloc(12);
|
|
1196
|
-
return e.copy(r, 0, 0,
|
|
1285
|
+
return e.copy(r, 0, 0, xn), r.writeUInt32BE(t >>> 0, xn), r.writeUInt8(+!!n, 11), r;
|
|
1197
1286
|
};
|
|
1198
|
-
async function*
|
|
1287
|
+
async function* Tn(e, t) {
|
|
1199
1288
|
if (!t) throw Error("encryptStream: a non-empty passphrase is required");
|
|
1200
|
-
let n =
|
|
1289
|
+
let n = b(bn), r = b(xn), i = await _n(t, n, yn, {
|
|
1201
1290
|
...Z,
|
|
1202
|
-
maxmem:
|
|
1291
|
+
maxmem: Cn
|
|
1203
1292
|
}), a = Buffer.alloc(Q);
|
|
1204
|
-
|
|
1293
|
+
vn.copy(a, 0), a.writeUInt32BE(Z.N, 8), a.writeUInt32BE(Z.r, 12), a.writeUInt32BE(Z.p, 16), n.copy(a, 20), r.copy(a, 36), yield new Uint8Array(a);
|
|
1205
1294
|
let o = 0, s = (e, t) => {
|
|
1206
|
-
let n =
|
|
1295
|
+
let n = _("aes-256-gcm", i, wn(r, o, t));
|
|
1207
1296
|
o === 0 && n.setAAD(a);
|
|
1208
1297
|
let s = Buffer.concat([n.update(e), n.final()]), c = n.getAuthTag();
|
|
1209
1298
|
o++;
|
|
1210
1299
|
let l = Buffer.alloc(5 + s.length + X);
|
|
1211
1300
|
return l.writeUInt8(+!!t, 0), l.writeUInt32BE(s.length + X, 1), s.copy(l, 5), c.copy(l, 5 + s.length), new Uint8Array(l);
|
|
1212
1301
|
}, c = Buffer.alloc(0);
|
|
1213
|
-
for await (let t of e) for (c = c.length === 0 ? Buffer.from(t) : Buffer.concat([c, Buffer.from(t)]); c.length >
|
|
1302
|
+
for await (let t of e) for (c = c.length === 0 ? Buffer.from(t) : Buffer.concat([c, Buffer.from(t)]); c.length > Sn;) yield s(c.subarray(0, Sn), !1), c = c.subarray(Sn);
|
|
1214
1303
|
yield s(c, !0);
|
|
1215
1304
|
}
|
|
1216
|
-
async function*
|
|
1305
|
+
async function* En(e, t) {
|
|
1217
1306
|
if (!t) throw Error("decryptStream: a non-empty passphrase is required");
|
|
1218
1307
|
let n = e[Symbol.asyncIterator](), r = Buffer.alloc(0), i = !1, a = async () => {
|
|
1219
1308
|
let { value: e, done: t } = await n.next();
|
|
@@ -1224,12 +1313,12 @@ async function* _n(e, t) {
|
|
|
1224
1313
|
};
|
|
1225
1314
|
if (!await o(Q)) throw Error("encrypted bundle truncated (no header)");
|
|
1226
1315
|
let s = r.subarray(0, Q);
|
|
1227
|
-
if (!s.subarray(0, 8).equals(
|
|
1228
|
-
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, Q)), p = await
|
|
1316
|
+
if (!s.subarray(0, 8).equals(vn)) throw Error("not an encrypted bundle (bad magic / wrong passphrase target)");
|
|
1317
|
+
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, Q)), p = await _n(t, d, yn, {
|
|
1229
1318
|
N: c,
|
|
1230
1319
|
r: l,
|
|
1231
1320
|
p: u,
|
|
1232
|
-
maxmem:
|
|
1321
|
+
maxmem: Cn
|
|
1233
1322
|
});
|
|
1234
1323
|
r = r.subarray(Q);
|
|
1235
1324
|
let m = 0, h = !1;
|
|
@@ -1244,7 +1333,7 @@ async function* _n(e, t) {
|
|
|
1244
1333
|
if (!await o(5 + t)) throw Error("encrypted bundle truncated (frame body)");
|
|
1245
1334
|
let n = r.subarray(5, 5 + t - X), i = r.subarray(5 + t - X, 5 + t);
|
|
1246
1335
|
r = r.subarray(5 + t);
|
|
1247
|
-
let a =
|
|
1336
|
+
let a = v("aes-256-gcm", p, wn(f, m, e));
|
|
1248
1337
|
m === 0 && a.setAAD(s), a.setAuthTag(i);
|
|
1249
1338
|
let c = Buffer.concat([a.update(n), a.final()]);
|
|
1250
1339
|
m++, e && (h = !0), c.length > 0 && (yield new Uint8Array(c));
|
|
@@ -1252,7 +1341,7 @@ async function* _n(e, t) {
|
|
|
1252
1341
|
}
|
|
1253
1342
|
//#endregion
|
|
1254
1343
|
//#region src/blobStorage.ts
|
|
1255
|
-
var
|
|
1344
|
+
var Dn = (e, t) => ({
|
|
1256
1345
|
list: () => {
|
|
1257
1346
|
let e = t();
|
|
1258
1347
|
return { async *[Symbol.asyncIterator]() {
|
|
@@ -1265,29 +1354,29 @@ var vn = (e, t) => ({
|
|
|
1265
1354
|
} };
|
|
1266
1355
|
},
|
|
1267
1356
|
open: (t) => ({ async *[Symbol.asyncIterator]() {
|
|
1268
|
-
let r = await n.runPromise(
|
|
1357
|
+
let r = await n.runPromise(te(e, t)), i = await re(r.stream);
|
|
1269
1358
|
for await (let e of i) yield e instanceof Uint8Array ? e : new Uint8Array(e);
|
|
1270
1359
|
} })
|
|
1271
|
-
}),
|
|
1272
|
-
table:
|
|
1360
|
+
}), On = (e, t) => Dn(e, () => h(t, {
|
|
1361
|
+
table: S,
|
|
1273
1362
|
chunkSize: 500
|
|
1274
1363
|
}).pipe(c.map((e) => ({
|
|
1275
1364
|
key: String(e.key),
|
|
1276
1365
|
contentType: String(e.contentType ?? "application/octet-stream"),
|
|
1277
1366
|
size: Number(e.size ?? 0)
|
|
1278
|
-
})))),
|
|
1367
|
+
})))), kn = (e) => ({
|
|
1279
1368
|
has: (t) => n.runPromise(e.head(t).pipe(n.map((e) => e !== null))),
|
|
1280
1369
|
write: async (t, r, i) => {
|
|
1281
|
-
let a =
|
|
1282
|
-
await n.runPromise(
|
|
1370
|
+
let a = C("storage", () => ae.from(r));
|
|
1371
|
+
await n.runPromise(ne(e, t, a, {
|
|
1283
1372
|
contentType: i.contentType,
|
|
1284
1373
|
totalSize: i.size
|
|
1285
1374
|
}));
|
|
1286
1375
|
}
|
|
1287
|
-
}),
|
|
1376
|
+
}), An = (e, t) => {
|
|
1288
1377
|
let n = t === "dump" ? "mariadb-dump" : "mariadb", r = t === "dump" ? "mysqldump" : "mysql";
|
|
1289
|
-
return e === "mariadb" &&
|
|
1290
|
-
},
|
|
1378
|
+
return e === "mariadb" && jn(n) ? n : r;
|
|
1379
|
+
}, jn = (e) => (process.env.PATH ?? "").split(fe).filter((e) => e !== "").some((t) => se(O(t, e))), Mn = (e) => {
|
|
1291
1380
|
switch (e) {
|
|
1292
1381
|
case "postgres": return "db.dump";
|
|
1293
1382
|
case "mysql":
|
|
@@ -1297,7 +1386,7 @@ var vn = (e, t) => ({
|
|
|
1297
1386
|
case "mssql": return "db.bacpac";
|
|
1298
1387
|
default: return "db.dump";
|
|
1299
1388
|
}
|
|
1300
|
-
},
|
|
1389
|
+
}, Nn = (e) => e.filename ?? e.database ?? ((e.url ?? "").replace(/^file:/, "") || "app.sqlite"), $ = (e) => {
|
|
1301
1390
|
if (e.host || e.database) return {
|
|
1302
1391
|
host: e.host ?? "localhost",
|
|
1303
1392
|
port: e.port ?? 3306,
|
|
@@ -1313,7 +1402,7 @@ var vn = (e, t) => ({
|
|
|
1313
1402
|
password: decodeURIComponent(t.password) || "",
|
|
1314
1403
|
database: t.pathname.replace(/^\//, "") || ""
|
|
1315
1404
|
};
|
|
1316
|
-
},
|
|
1405
|
+
}, Pn = (e) => `Server=${e.host},${e.port};Database=${e.database};User ID=${e.username};Password=${e.password};TrustServerCertificate=True`, Fn = (e, t, n) => {
|
|
1317
1406
|
switch (e) {
|
|
1318
1407
|
case "postgres": {
|
|
1319
1408
|
if (t.url) return {
|
|
@@ -1360,7 +1449,7 @@ var vn = (e, t) => ({
|
|
|
1360
1449
|
let r = $(t);
|
|
1361
1450
|
return {
|
|
1362
1451
|
kind: "spawn",
|
|
1363
|
-
tool:
|
|
1452
|
+
tool: An(e, "dump"),
|
|
1364
1453
|
args: [
|
|
1365
1454
|
"--single-transaction",
|
|
1366
1455
|
"--routines",
|
|
@@ -1382,7 +1471,7 @@ var vn = (e, t) => ({
|
|
|
1382
1471
|
case "sqlite":
|
|
1383
1472
|
case "turso": return {
|
|
1384
1473
|
kind: "copy",
|
|
1385
|
-
from:
|
|
1474
|
+
from: Nn(t),
|
|
1386
1475
|
to: n
|
|
1387
1476
|
};
|
|
1388
1477
|
case "mssql": return {
|
|
@@ -1390,7 +1479,7 @@ var vn = (e, t) => ({
|
|
|
1390
1479
|
tool: "sqlpackage",
|
|
1391
1480
|
args: [
|
|
1392
1481
|
"/Action:Export",
|
|
1393
|
-
`/SourceConnectionString:${
|
|
1482
|
+
`/SourceConnectionString:${Pn($({
|
|
1394
1483
|
...t,
|
|
1395
1484
|
port: t.port ?? 1433
|
|
1396
1485
|
}))}`,
|
|
@@ -1403,7 +1492,7 @@ var vn = (e, t) => ({
|
|
|
1403
1492
|
reason: `no native backup for dialect '${e}'`
|
|
1404
1493
|
});
|
|
1405
1494
|
}
|
|
1406
|
-
},
|
|
1495
|
+
}, In = (e, t, n) => {
|
|
1407
1496
|
switch (e) {
|
|
1408
1497
|
case "postgres": {
|
|
1409
1498
|
if (t.url) return {
|
|
@@ -1448,7 +1537,7 @@ var vn = (e, t) => ({
|
|
|
1448
1537
|
let r = $(t);
|
|
1449
1538
|
return {
|
|
1450
1539
|
kind: "spawn",
|
|
1451
|
-
tool:
|
|
1540
|
+
tool: An(e, "client"),
|
|
1452
1541
|
args: [
|
|
1453
1542
|
"--protocol=TCP",
|
|
1454
1543
|
"-h",
|
|
@@ -1467,14 +1556,14 @@ var vn = (e, t) => ({
|
|
|
1467
1556
|
case "turso": return {
|
|
1468
1557
|
kind: "copy",
|
|
1469
1558
|
from: n,
|
|
1470
|
-
to:
|
|
1559
|
+
to: Nn(t)
|
|
1471
1560
|
};
|
|
1472
1561
|
case "mssql": return {
|
|
1473
1562
|
kind: "spawn",
|
|
1474
1563
|
tool: "sqlpackage",
|
|
1475
1564
|
args: [
|
|
1476
1565
|
"/Action:Import",
|
|
1477
|
-
`/TargetConnectionString:${
|
|
1566
|
+
`/TargetConnectionString:${Pn($({
|
|
1478
1567
|
...t,
|
|
1479
1568
|
port: t.port ?? 1433
|
|
1480
1569
|
}))}`,
|
|
@@ -1487,13 +1576,13 @@ var vn = (e, t) => ({
|
|
|
1487
1576
|
reason: `no native restore for dialect '${e}'`
|
|
1488
1577
|
});
|
|
1489
1578
|
}
|
|
1490
|
-
},
|
|
1491
|
-
|
|
1579
|
+
}, Ln = (e) => e.kind === "copy" ? n.async((t) => {
|
|
1580
|
+
oe(e.from, e.to, (r) => t(r ? n.fail(new L({
|
|
1492
1581
|
tool: "copy",
|
|
1493
1582
|
reason: `copy ${e.from} → ${e.to}: ${r.message}`
|
|
1494
1583
|
})) : n.void));
|
|
1495
1584
|
}) : n.async((t) => {
|
|
1496
|
-
let r =
|
|
1585
|
+
let r = _e(e.tool, [...e.args], {
|
|
1497
1586
|
env: {
|
|
1498
1587
|
...process.env,
|
|
1499
1588
|
...e.env
|
|
@@ -1506,10 +1595,10 @@ var vn = (e, t) => ({
|
|
|
1506
1595
|
}), i = "";
|
|
1507
1596
|
r.stderr?.on("data", (e) => {
|
|
1508
1597
|
i += String(e);
|
|
1509
|
-
}), e.stdoutFile && r.stdout && r.stdout.pipe(
|
|
1598
|
+
}), e.stdoutFile && r.stdout && r.stdout.pipe(E(e.stdoutFile)), e.stdinFile && r.stdin && T(e.stdinFile).pipe(r.stdin);
|
|
1510
1599
|
let a = () => {
|
|
1511
1600
|
if (e.stdoutFile !== void 0) try {
|
|
1512
|
-
|
|
1601
|
+
ce(e.stdoutFile);
|
|
1513
1602
|
} catch {}
|
|
1514
1603
|
};
|
|
1515
1604
|
r.on("error", (r) => {
|
|
@@ -1524,15 +1613,15 @@ var vn = (e, t) => ({
|
|
|
1524
1613
|
}
|
|
1525
1614
|
a(), t(n.fail(new L({
|
|
1526
1615
|
tool: e.tool,
|
|
1527
|
-
reason: `exited with code ${r}` + (e.stdoutFile === void 0 ? "" : ` — the partial ${
|
|
1616
|
+
reason: `exited with code ${r}` + (e.stdoutFile === void 0 ? "" : ` — the partial ${de(e.stdoutFile)} was removed, so it cannot be mistaken for a backup`),
|
|
1528
1617
|
stderr: i.slice(-2e3)
|
|
1529
1618
|
})));
|
|
1530
1619
|
});
|
|
1531
|
-
}),
|
|
1532
|
-
let t = yield*
|
|
1620
|
+
}), Rn = (e) => typeof e == "object" && e ? "fake" in e ? `fake:${e.fake}` : "custom" : String(e), zn = (e) => n.gen(function* () {
|
|
1621
|
+
let t = yield* V(e.scope ?? { kind: "all" }, e.snapshot, {
|
|
1533
1622
|
store: e.store,
|
|
1534
1623
|
...e.tenantTables ? { tenantTables: e.tenantTables } : {}
|
|
1535
|
-
}), r =
|
|
1624
|
+
}), r = At(t.tables, e.snapshot), i = e.classification ?? k(e.snapshot), a = e.sampleSize ?? 20, o = Ae(r.map((t) => ({
|
|
1536
1625
|
name: t,
|
|
1537
1626
|
columns: (e.snapshot.tables.find((e) => e.name === t)?.columns ?? []).map((e) => ({
|
|
1538
1627
|
name: e.name,
|
|
@@ -1540,26 +1629,26 @@ var vn = (e, t) => ({
|
|
|
1540
1629
|
}))
|
|
1541
1630
|
})), i, e.masking), s = [], l = [];
|
|
1542
1631
|
for (let i of r) {
|
|
1543
|
-
let r = t.predicateFor(i), u = yield*
|
|
1632
|
+
let r = t.predicateFor(i), u = yield* h(e.store, {
|
|
1544
1633
|
table: i,
|
|
1545
1634
|
chunkSize: a,
|
|
1546
1635
|
...r === void 0 ? {} : { where: r }
|
|
1547
|
-
}).pipe(c.take(a), c.runCollect, n.map((e) => [...e])), d = u.map((t) =>
|
|
1548
|
-
l.push(...
|
|
1549
|
-
let f = u[0],
|
|
1550
|
-
if (f &&
|
|
1636
|
+
}).pipe(c.take(a), c.runCollect, n.map((e) => [...e])), d = u.map((t) => Ne(t, i, o.actions, e.masking.seed));
|
|
1637
|
+
l.push(...Me(d, i, o.actions));
|
|
1638
|
+
let f = u[0], p = d[0], m = [];
|
|
1639
|
+
if (f && p) for (let [e, t] of Object.entries(o.actions)) {
|
|
1551
1640
|
if (!e.startsWith(`${i}.`) || t === "keep") continue;
|
|
1552
1641
|
let n = e.slice(i.length + 1);
|
|
1553
|
-
|
|
1642
|
+
m.push({
|
|
1554
1643
|
column: n,
|
|
1555
|
-
action:
|
|
1644
|
+
action: Rn(t),
|
|
1556
1645
|
before: f[n],
|
|
1557
|
-
after:
|
|
1646
|
+
after: p[n]
|
|
1558
1647
|
});
|
|
1559
1648
|
}
|
|
1560
1649
|
s.push({
|
|
1561
1650
|
table: i,
|
|
1562
|
-
samples:
|
|
1651
|
+
samples: m
|
|
1563
1652
|
});
|
|
1564
1653
|
}
|
|
1565
1654
|
return {
|
|
@@ -1567,7 +1656,7 @@ var vn = (e, t) => ({
|
|
|
1567
1656
|
leaks: l,
|
|
1568
1657
|
unclassified: o.unclassified
|
|
1569
1658
|
};
|
|
1570
|
-
}),
|
|
1659
|
+
}), Bn = (e, t) => {
|
|
1571
1660
|
let n = new Map(t.map((e) => [e.name, e])), r = [], i = {};
|
|
1572
1661
|
for (let t of e) {
|
|
1573
1662
|
let e = n.get(t.name);
|
|
@@ -1622,7 +1711,7 @@ var vn = (e, t) => ({
|
|
|
1622
1711
|
skipColumns: i,
|
|
1623
1712
|
refuse: r.some((e) => e.verdict === "refuse")
|
|
1624
1713
|
};
|
|
1625
|
-
},
|
|
1714
|
+
}, Vn = (e) => {
|
|
1626
1715
|
if (e.findings.length === 0) return [" schema matches the bundle"];
|
|
1627
1716
|
let t = {
|
|
1628
1717
|
safe: "✓",
|
|
@@ -1633,4 +1722,4 @@ var vn = (e, t) => ({
|
|
|
1633
1722
|
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;
|
|
1634
1723
|
};
|
|
1635
1724
|
//#endregion
|
|
1636
|
-
export {
|
|
1725
|
+
export { W as ASSETS_DIR, G as ASSET_INDEX_FILE, P as BundleError, Pe as CodecError, Fe as CompressionError, Be as CrossDialectError, U as DATA_DIR, ye as DEFAULT_CLASS_ACTIONS, Ie as DanglingReferenceError, Qe as FORMAT_VERSION, ze as ImportModeError, F as IntegrityError, ot as LEDGER_FILE, H as MANIFEST_FILE, Re as MaskingError, L as NativeToolError, Le as SchemaDriftError, I as ScopeError, Oe as applyAction, tn as assessLiveImport, gt as assetDone, Mn as backupArtifactName, Fn as backupCommand, Bt as canonicalType, Ut as checkPortability, Zt as checkSchemaDrift, k as classificationFromSnapshot, Bn as classifyImportDrift, Ye as compressionExt, z as computeSubsetIds, it as decodeManifest, Et as decodeRow, wt as decodeValue, En as decryptStream, en as defineDataProfile, Xt as diffSnapshots, gn as dirBlobSink, pt as emptyLedger, rt as encodeManifest, Tt as encodeRow, St as encodeValue, Tn as encryptStream, Vn as formatImportDrift, Wt as formatIssue, qe as hasZstd, Xe as makeCompressor, Ze as makeDecompressor, Ne as maskRow, kt as ndjsonToRows, un as packBundle, mn as peekBundle, Je as pickCompression, Ae as planMasking, zn as previewMasking, mt as readLedger, be as resolveAction, V as resolveScope, Ke as restoreAssetFromCas, In as restoreCommand, Ot as rowsToNdjson, zt as runExport, $t as runImport, Ln as runNativeStep, Me as scanForLeaks, We as storageAssetSink, Ue as storageAssetSource, kn as storageBlobSink, Dn as storageBlobSource, On as storageRefsBlobSource, He as subsetToScope, ht as tableDone, At as topoSortTables, pn as unpackBundle, hn as unpackBundleToDir, Ge as writeAssetToCas, q as writeLedger };
|