busa-cli 0.9.8 → 0.9.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-37OV7S7G.js → chunk-ZC4HCQAB.js} +1139 -513
- package/dist/cli.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +3 -2
|
@@ -120,7 +120,7 @@ function nodeIcon(type) {
|
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
// src/run.ts
|
|
123
|
-
import { createHash as
|
|
123
|
+
import { createHash as createHash6 } from "crypto";
|
|
124
124
|
import { readFileSync as readFileSync2 } from "fs";
|
|
125
125
|
import { basename, extname as extname2 } from "path";
|
|
126
126
|
import {
|
|
@@ -137,6 +137,566 @@ import {
|
|
|
137
137
|
Option
|
|
138
138
|
} from "commander";
|
|
139
139
|
|
|
140
|
+
// src/backup/export/full-exporter.ts
|
|
141
|
+
import { createHash as createHash2 } from "crypto";
|
|
142
|
+
|
|
143
|
+
// ../../packages/busabase-contract/src/domains/dump/types.ts
|
|
144
|
+
import { z } from "zod";
|
|
145
|
+
var DumpTableSchema = z.enum([
|
|
146
|
+
"nodes",
|
|
147
|
+
/** Node-level access grants (permissions). Not secret — restore must keep them
|
|
148
|
+
* so a backed-up space's sharing/permission config survives a full restore. */
|
|
149
|
+
"nodePrincipals",
|
|
150
|
+
"bases",
|
|
151
|
+
"baseFields",
|
|
152
|
+
"views",
|
|
153
|
+
"records",
|
|
154
|
+
"fieldValues",
|
|
155
|
+
"recordLinks",
|
|
156
|
+
/** The physical bytes registry (open-domains/attachments) an Asset's `attachmentId` FKs into. */
|
|
157
|
+
"attachments",
|
|
158
|
+
"assets",
|
|
159
|
+
"assetUsages",
|
|
160
|
+
"assetTexts",
|
|
161
|
+
"commits",
|
|
162
|
+
"changeRequests",
|
|
163
|
+
"operations",
|
|
164
|
+
"comments",
|
|
165
|
+
"reviews",
|
|
166
|
+
"auditEvents"
|
|
167
|
+
]);
|
|
168
|
+
var ExportTablesInputSchema = z.object({
|
|
169
|
+
table: DumpTableSchema,
|
|
170
|
+
/** Opaque pagination cursor from a previous page's `nextCursor`; omit for the first page. */
|
|
171
|
+
cursor: z.string().optional(),
|
|
172
|
+
/** Page size, default 500, capped at 2000. */
|
|
173
|
+
limit: z.number().int().positive().max(2e3).optional().default(500)
|
|
174
|
+
});
|
|
175
|
+
var ExportTablesVOSchema = z.object({
|
|
176
|
+
rows: z.array(z.record(z.string(), z.unknown())),
|
|
177
|
+
nextCursor: z.string().nullable()
|
|
178
|
+
});
|
|
179
|
+
var ExportAssetTextInputSchema = z.object({ assetId: z.string() });
|
|
180
|
+
var ExportAssetTextVOSchema = z.object({
|
|
181
|
+
assetId: z.string(),
|
|
182
|
+
/** The row's `text_storage_key` — the exact key a restore must write back to. */
|
|
183
|
+
textStorageKey: z.string(),
|
|
184
|
+
/**
|
|
185
|
+
* `null` when this row owns no separate text object and therefore needs no
|
|
186
|
+
* blob in the archive:
|
|
187
|
+
* - auto-registered text-kind rows (`writtenBy: "auto"`), whose
|
|
188
|
+
* `textStorageKey` IS the owning attachment's own key — those bytes are
|
|
189
|
+
* already archived by the attachment-blob pass, and re-archiving them
|
|
190
|
+
* would duplicate bytes and collide on restore;
|
|
191
|
+
* - `status: "none"` rows (no extractable text), whose key is `""`.
|
|
192
|
+
*/
|
|
193
|
+
downloadUrl: z.string().nullable(),
|
|
194
|
+
/** `sha256:<hex>` of the text bytes, so a backup can verify what it downloaded. */
|
|
195
|
+
textContentHash: z.string().nullable(),
|
|
196
|
+
byteCount: z.number().int().nonnegative()
|
|
197
|
+
});
|
|
198
|
+
var ImportBeginVOSchema = z.object({
|
|
199
|
+
sessionId: z.string()
|
|
200
|
+
});
|
|
201
|
+
var ImportTablesInputSchema = z.object({
|
|
202
|
+
sessionId: z.string(),
|
|
203
|
+
table: z.union([
|
|
204
|
+
DumpTableSchema,
|
|
205
|
+
z.literal("docBodies"),
|
|
206
|
+
z.literal("attachmentBlobs"),
|
|
207
|
+
z.literal("assetTextBlobs")
|
|
208
|
+
]),
|
|
209
|
+
rows: z.array(z.record(z.string(), z.unknown()))
|
|
210
|
+
});
|
|
211
|
+
var ImportTablesVOSchema = z.object({
|
|
212
|
+
inserted: z.number().int().nonnegative()
|
|
213
|
+
});
|
|
214
|
+
var ImportSessionInputSchema = z.object({
|
|
215
|
+
sessionId: z.string()
|
|
216
|
+
});
|
|
217
|
+
var ImportCommitVOSchema = z.object({
|
|
218
|
+
ok: z.boolean(),
|
|
219
|
+
warnings: z.array(z.string())
|
|
220
|
+
});
|
|
221
|
+
var ImportAbortVOSchema = z.object({
|
|
222
|
+
ok: z.boolean()
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
// src/backup/format/archive-writer.ts
|
|
226
|
+
import { createHash } from "crypto";
|
|
227
|
+
import { createWriteStream } from "fs";
|
|
228
|
+
import { PassThrough } from "stream";
|
|
229
|
+
import { pipeline } from "stream/promises";
|
|
230
|
+
import { ZstdCompress } from "zlib";
|
|
231
|
+
import * as tar from "tar-stream";
|
|
232
|
+
|
|
233
|
+
// src/backup/format/manifest.ts
|
|
234
|
+
import { z as z2 } from "zod";
|
|
235
|
+
var FORMAT_VERSION = 1;
|
|
236
|
+
var ManifestSchema = z2.object({
|
|
237
|
+
formatVersion: z2.literal(FORMAT_VERSION),
|
|
238
|
+
toolVersion: z2.string(),
|
|
239
|
+
exportedAt: z2.string(),
|
|
240
|
+
spaceId: z2.string(),
|
|
241
|
+
sourceHost: z2.string(),
|
|
242
|
+
fidelity: z2.enum(["full", "state"]),
|
|
243
|
+
/** Vault items and webhook signing secrets are never written to the archive. */
|
|
244
|
+
excludesSecrets: z2.literal(true),
|
|
245
|
+
tables: z2.record(z2.string(), z2.number().int().nonnegative()),
|
|
246
|
+
blobCount: z2.number().int().nonnegative(),
|
|
247
|
+
blobBytes: z2.number().int().nonnegative(),
|
|
248
|
+
/**
|
|
249
|
+
* Extracted-text ("Drive Grep Retrieval") blobs, counted separately from the
|
|
250
|
+
* attachment blobs above because they are a different object class — derived
|
|
251
|
+
* text, content-addressed under `asset-texts/blobs/sha256/…`, one per
|
|
252
|
+
* `busabase_asset_texts` row that owns its own object.
|
|
253
|
+
*
|
|
254
|
+
* `.default(0)` rather than required: archives written before asset-text
|
|
255
|
+
* blobs were captured at all carry no such field, and those are exactly the
|
|
256
|
+
* archives an operator most needs to be able to still open — the import's
|
|
257
|
+
* integrity pass is what tells them the text is missing, which it can only
|
|
258
|
+
* do if the manifest parses in the first place.
|
|
259
|
+
*/
|
|
260
|
+
textBlobCount: z2.number().int().nonnegative().optional().default(0),
|
|
261
|
+
textBlobBytes: z2.number().int().nonnegative().optional().default(0),
|
|
262
|
+
/** sha256 hex digest — see the doc comment above for exactly what it covers. */
|
|
263
|
+
checksum: z2.string()
|
|
264
|
+
});
|
|
265
|
+
function canonicalizeEntryChecksums(entries) {
|
|
266
|
+
return entries.slice().sort((a, b) => a.path.localeCompare(b.path)).map((e) => `${e.path}:${e.sha256}`).join("\n");
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// src/backup/format/archive-writer.ts
|
|
270
|
+
var ArchiveWriter = class _ArchiveWriter {
|
|
271
|
+
pack = tar.pack();
|
|
272
|
+
entryChecksums = [];
|
|
273
|
+
donePromise;
|
|
274
|
+
constructor(outPath) {
|
|
275
|
+
const zstd = new ZstdCompress();
|
|
276
|
+
this.donePromise = pipeline(this.pack, zstd, createWriteStream(outPath));
|
|
277
|
+
}
|
|
278
|
+
static create(outPath) {
|
|
279
|
+
return new _ArchiveWriter(outPath);
|
|
280
|
+
}
|
|
281
|
+
/** Add one archive entry from an in-memory buffer/string (small entries: manifest, base.json, etc). */
|
|
282
|
+
async addBuffer(path, data) {
|
|
283
|
+
const buf = typeof data === "string" ? Buffer.from(data, "utf8") : data;
|
|
284
|
+
const sha256 = createHash("sha256").update(buf).digest("hex");
|
|
285
|
+
this.entryChecksums.push({ path, sha256 });
|
|
286
|
+
await new Promise((resolvePromise, reject) => {
|
|
287
|
+
this.pack.entry(
|
|
288
|
+
{ name: path, size: buf.length },
|
|
289
|
+
buf,
|
|
290
|
+
(err) => err ? reject(err) : resolvePromise()
|
|
291
|
+
);
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
/** Add one archive entry streamed from a Readable (large entries: records.ndjson, blobs). */
|
|
295
|
+
async addStream(path, size, source) {
|
|
296
|
+
const hash = createHash("sha256");
|
|
297
|
+
const entry = this.pack.entry({ name: path, size });
|
|
298
|
+
const tee = new PassThrough();
|
|
299
|
+
tee.on("data", (chunk) => hash.update(chunk));
|
|
300
|
+
await pipeline(source, tee, entry);
|
|
301
|
+
this.entryChecksums.push({ path, sha256: hash.digest("hex") });
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Write `manifest.json` (with the top-level checksum over every other
|
|
305
|
+
* entry) and close the archive. Must be called exactly once, after every
|
|
306
|
+
* other entry has been added.
|
|
307
|
+
*/
|
|
308
|
+
async finalize(manifestWithoutChecksum) {
|
|
309
|
+
const checksum = createHash("sha256").update(canonicalizeEntryChecksums(this.entryChecksums)).digest("hex");
|
|
310
|
+
const manifest = { ...manifestWithoutChecksum, checksum };
|
|
311
|
+
const buf = Buffer.from(JSON.stringify(manifest, null, 2), "utf8");
|
|
312
|
+
await new Promise((resolvePromise, reject) => {
|
|
313
|
+
this.pack.entry(
|
|
314
|
+
{ name: "manifest.json", size: buf.length },
|
|
315
|
+
buf,
|
|
316
|
+
(err) => err ? reject(err) : resolvePromise()
|
|
317
|
+
);
|
|
318
|
+
});
|
|
319
|
+
this.pack.finalize();
|
|
320
|
+
await this.donePromise;
|
|
321
|
+
return manifest;
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
// src/backup/export/full-exporter.ts
|
|
326
|
+
var EXPORT_TABLES = [...DumpTableSchema.options];
|
|
327
|
+
var HISTORY_TABLES = [
|
|
328
|
+
"commits",
|
|
329
|
+
"changeRequests",
|
|
330
|
+
"operations",
|
|
331
|
+
"comments",
|
|
332
|
+
"reviews",
|
|
333
|
+
"auditEvents"
|
|
334
|
+
];
|
|
335
|
+
async function exportFull(options) {
|
|
336
|
+
const { client, outPath, spaceId, sourceHost, includeHistory, toolVersion, onProgress } = options;
|
|
337
|
+
const pageLimit = options.pageLimit ?? 500;
|
|
338
|
+
const log = onProgress ?? (() => {
|
|
339
|
+
});
|
|
340
|
+
const writer = ArchiveWriter.create(outPath);
|
|
341
|
+
const tableCounts = {};
|
|
342
|
+
const tableRows = {};
|
|
343
|
+
const tables = includeHistory ? EXPORT_TABLES : EXPORT_TABLES.filter((t) => !HISTORY_TABLES.includes(t));
|
|
344
|
+
for (const table of tables) {
|
|
345
|
+
const rows = [];
|
|
346
|
+
let cursor;
|
|
347
|
+
do {
|
|
348
|
+
const page = await client.dump.exportTables({ table, cursor, limit: pageLimit });
|
|
349
|
+
rows.push(...page.rows);
|
|
350
|
+
cursor = page.nextCursor ?? void 0;
|
|
351
|
+
} while (cursor);
|
|
352
|
+
tableRows[table] = rows;
|
|
353
|
+
tableCounts[table] = rows.length;
|
|
354
|
+
const ndjson = `${rows.map((row) => JSON.stringify(row)).join("\n")}${rows.length ? "\n" : ""}`;
|
|
355
|
+
await writer.addBuffer(`tree/${table}.ndjson`, ndjson);
|
|
356
|
+
log(`exported ${table}: ${rows.length} rows`);
|
|
357
|
+
}
|
|
358
|
+
const docNodes = (tableRows.nodes ?? []).filter((n) => n.type === "doc");
|
|
359
|
+
let docBodyCount = 0;
|
|
360
|
+
for (const node of docNodes) {
|
|
361
|
+
const nodeId = node.id;
|
|
362
|
+
const doc = await client.docs.get({ nodeId });
|
|
363
|
+
await writer.addBuffer(`docs/${nodeId}.md`, doc.body ?? "");
|
|
364
|
+
docBodyCount += 1;
|
|
365
|
+
}
|
|
366
|
+
log(`exported ${docBodyCount} doc bodies`);
|
|
367
|
+
const attachmentsById = new Map((tableRows.attachments ?? []).map((a) => [a.id, a]));
|
|
368
|
+
const attachmentBlobRows = [];
|
|
369
|
+
let blobCount = 0;
|
|
370
|
+
let blobBytes = 0;
|
|
371
|
+
const skippedAssets = [];
|
|
372
|
+
for (const asset of tableRows.assets ?? []) {
|
|
373
|
+
const assetId = asset.id;
|
|
374
|
+
const attachment = attachmentsById.get(asset.attachmentId);
|
|
375
|
+
try {
|
|
376
|
+
if (!attachment) throw new Error("no matching attachments row (orphaned asset)");
|
|
377
|
+
const download = await client.assets.download({ assetId });
|
|
378
|
+
const resolvedUrl = new URL(download.downloadUrl, sourceHost).toString();
|
|
379
|
+
const res = await fetch(resolvedUrl);
|
|
380
|
+
if (!res.ok || !res.body) {
|
|
381
|
+
throw new Error(`HTTP ${res.status}`);
|
|
382
|
+
}
|
|
383
|
+
const buf = Buffer.from(await res.arrayBuffer());
|
|
384
|
+
attachmentBlobRows.push({
|
|
385
|
+
storageKey: attachment.storageKey,
|
|
386
|
+
mimeType: download.mimeType,
|
|
387
|
+
base64: buf.toString("base64")
|
|
388
|
+
});
|
|
389
|
+
blobCount += 1;
|
|
390
|
+
blobBytes += buf.length;
|
|
391
|
+
} catch (error) {
|
|
392
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
393
|
+
skippedAssets.push(assetId);
|
|
394
|
+
log(` WARNING: could not download asset ${assetId}, skipping its blob (${message})`);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
const blobNdjson = `${attachmentBlobRows.map((row) => JSON.stringify(row)).join("\n")}${attachmentBlobRows.length ? "\n" : ""}`;
|
|
398
|
+
await writer.addBuffer("tree/attachmentBlobs.ndjson", blobNdjson);
|
|
399
|
+
log(
|
|
400
|
+
`exported ${blobCount} attachment blobs (${blobBytes} bytes), skipped ${skippedAssets.length}`
|
|
401
|
+
);
|
|
402
|
+
const textBlobRows = [];
|
|
403
|
+
const seenTextKeys = /* @__PURE__ */ new Set();
|
|
404
|
+
let textBlobCount = 0;
|
|
405
|
+
let textBlobBytes = 0;
|
|
406
|
+
const skippedAssetTexts = [];
|
|
407
|
+
for (const assetText of tableRows.assetTexts ?? []) {
|
|
408
|
+
const assetId = assetText.assetId;
|
|
409
|
+
try {
|
|
410
|
+
const info = await client.dump.exportAssetText({ assetId });
|
|
411
|
+
if (!info.downloadUrl) continue;
|
|
412
|
+
if (seenTextKeys.has(info.textStorageKey)) continue;
|
|
413
|
+
const resolvedUrl = new URL(info.downloadUrl, sourceHost).toString();
|
|
414
|
+
const res = await fetch(resolvedUrl);
|
|
415
|
+
if (!res.ok || !res.body) {
|
|
416
|
+
throw new Error(`HTTP ${res.status}`);
|
|
417
|
+
}
|
|
418
|
+
const buf = Buffer.from(await res.arrayBuffer());
|
|
419
|
+
if (info.textContentHash) {
|
|
420
|
+
const actual = `sha256:${createHash2("sha256").update(buf).digest("hex")}`;
|
|
421
|
+
if (actual !== info.textContentHash) {
|
|
422
|
+
throw new Error(
|
|
423
|
+
`content hash mismatch (expected ${info.textContentHash}, downloaded ${actual})`
|
|
424
|
+
);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
seenTextKeys.add(info.textStorageKey);
|
|
428
|
+
textBlobRows.push({ textStorageKey: info.textStorageKey, base64: buf.toString("base64") });
|
|
429
|
+
textBlobCount += 1;
|
|
430
|
+
textBlobBytes += buf.length;
|
|
431
|
+
} catch (error) {
|
|
432
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
433
|
+
skippedAssetTexts.push(assetId);
|
|
434
|
+
log(
|
|
435
|
+
` WARNING: could not download text for asset ${assetId}, skipping its blob (${message})`
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
const textBlobNdjson = `${textBlobRows.map((row) => JSON.stringify(row)).join("\n")}${textBlobRows.length ? "\n" : ""}`;
|
|
440
|
+
await writer.addBuffer("tree/assetTextBlobs.ndjson", textBlobNdjson);
|
|
441
|
+
log(
|
|
442
|
+
`exported ${textBlobCount} asset-text blobs (${textBlobBytes} bytes), skipped ${skippedAssetTexts.length}`
|
|
443
|
+
);
|
|
444
|
+
const manifestWithoutChecksum = {
|
|
445
|
+
formatVersion: FORMAT_VERSION,
|
|
446
|
+
toolVersion,
|
|
447
|
+
exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
448
|
+
spaceId,
|
|
449
|
+
sourceHost,
|
|
450
|
+
fidelity: "full",
|
|
451
|
+
excludesSecrets: true,
|
|
452
|
+
tables: tableCounts,
|
|
453
|
+
blobCount,
|
|
454
|
+
blobBytes,
|
|
455
|
+
textBlobCount,
|
|
456
|
+
textBlobBytes
|
|
457
|
+
};
|
|
458
|
+
return writer.finalize(manifestWithoutChecksum);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// src/backup/format/archive-reader.ts
|
|
462
|
+
import { createHash as createHash3 } from "crypto";
|
|
463
|
+
import { createReadStream } from "fs";
|
|
464
|
+
import { ZstdDecompress } from "zlib";
|
|
465
|
+
import * as tar2 from "tar-stream";
|
|
466
|
+
var ZSTD_MAGIC = Buffer.from([40, 181, 47, 253]);
|
|
467
|
+
var GZIP_MAGIC = Buffer.from([31, 139]);
|
|
468
|
+
async function detectCompression(path) {
|
|
469
|
+
return new Promise((resolvePromise, reject) => {
|
|
470
|
+
const stream = createReadStream(path, { start: 0, end: 3 });
|
|
471
|
+
const chunks = [];
|
|
472
|
+
stream.on("data", (c2) => chunks.push(c2));
|
|
473
|
+
stream.on("error", reject);
|
|
474
|
+
stream.on("end", () => {
|
|
475
|
+
const head = Buffer.concat(chunks);
|
|
476
|
+
if (head.subarray(0, 4).equals(ZSTD_MAGIC)) resolvePromise("zstd");
|
|
477
|
+
else if (head.subarray(0, 2).equals(GZIP_MAGIC)) resolvePromise("gzip");
|
|
478
|
+
else reject(new Error("Unrecognized .bbdump compression (not zstd or gzip magic bytes)."));
|
|
479
|
+
});
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
async function readArchive(path) {
|
|
483
|
+
const compression = await detectCompression(path);
|
|
484
|
+
if (compression === "gzip") {
|
|
485
|
+
throw new Error(
|
|
486
|
+
"This .bbdump archive is gzip-compressed; `busabase-cli restore` only reads zstd archives in v1."
|
|
487
|
+
);
|
|
488
|
+
}
|
|
489
|
+
const extract2 = tar2.extract();
|
|
490
|
+
const entries = /* @__PURE__ */ new Map();
|
|
491
|
+
const checksums = [];
|
|
492
|
+
let manifestBuf;
|
|
493
|
+
await new Promise((resolvePromise, reject) => {
|
|
494
|
+
extract2.on("entry", (header, stream, next) => {
|
|
495
|
+
const chunks = [];
|
|
496
|
+
stream.on("data", (c2) => chunks.push(c2));
|
|
497
|
+
stream.on("end", () => {
|
|
498
|
+
const buf = Buffer.concat(chunks);
|
|
499
|
+
if (header.name === "manifest.json") {
|
|
500
|
+
manifestBuf = buf;
|
|
501
|
+
} else {
|
|
502
|
+
const sha256 = createHash3("sha256").update(buf).digest("hex");
|
|
503
|
+
checksums.push({ path: header.name, sha256 });
|
|
504
|
+
entries.set(header.name, buf);
|
|
505
|
+
}
|
|
506
|
+
next();
|
|
507
|
+
});
|
|
508
|
+
stream.on("error", reject);
|
|
509
|
+
stream.resume();
|
|
510
|
+
});
|
|
511
|
+
extract2.on("finish", resolvePromise);
|
|
512
|
+
extract2.on("error", reject);
|
|
513
|
+
createReadStream(path).pipe(new ZstdDecompress()).on("error", reject).pipe(extract2);
|
|
514
|
+
});
|
|
515
|
+
if (!manifestBuf) {
|
|
516
|
+
throw new Error("Archive is missing manifest.json.");
|
|
517
|
+
}
|
|
518
|
+
const manifest = ManifestSchema.parse(JSON.parse(manifestBuf.toString("utf8")));
|
|
519
|
+
const recomputed = createHash3("sha256").update(canonicalizeEntryChecksums(checksums)).digest("hex");
|
|
520
|
+
if (recomputed !== manifest.checksum) {
|
|
521
|
+
throw new Error(
|
|
522
|
+
`Archive checksum mismatch \u2014 file may be truncated or corrupted (expected ${manifest.checksum}, got ${recomputed}).`
|
|
523
|
+
);
|
|
524
|
+
}
|
|
525
|
+
return { manifest, entries };
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// src/backup/import/full-importer.ts
|
|
529
|
+
var IMPORT_ORDER = [
|
|
530
|
+
"nodes",
|
|
531
|
+
// `nodeId` / `sourceNodeId` FK into busabase_nodes — must follow "nodes".
|
|
532
|
+
"nodePrincipals",
|
|
533
|
+
"bases",
|
|
534
|
+
"baseFields",
|
|
535
|
+
"views",
|
|
536
|
+
"commits",
|
|
537
|
+
"changeRequests",
|
|
538
|
+
"operations",
|
|
539
|
+
"records",
|
|
540
|
+
"fieldValues",
|
|
541
|
+
"recordLinks",
|
|
542
|
+
"attachments",
|
|
543
|
+
"assets",
|
|
544
|
+
"assetUsages",
|
|
545
|
+
"assetTexts",
|
|
546
|
+
"comments",
|
|
547
|
+
"reviews",
|
|
548
|
+
"auditEvents"
|
|
549
|
+
];
|
|
550
|
+
var BATCH_SIZE = 200;
|
|
551
|
+
async function importFull(options) {
|
|
552
|
+
const { client, archive, onProgress } = options;
|
|
553
|
+
const log = onProgress ?? (() => {
|
|
554
|
+
});
|
|
555
|
+
const { sessionId } = await client.dump.importBegin();
|
|
556
|
+
try {
|
|
557
|
+
const blobBuf = archive.entries.get("tree/attachmentBlobs.ndjson");
|
|
558
|
+
if (blobBuf) {
|
|
559
|
+
const blobRows = blobBuf.toString("utf8").split("\n").filter(Boolean).map((line) => JSON.parse(line));
|
|
560
|
+
for (let i = 0; i < blobRows.length; i += BATCH_SIZE) {
|
|
561
|
+
const batch = blobRows.slice(i, i + BATCH_SIZE);
|
|
562
|
+
await client.dump.importTables({ sessionId, table: "attachmentBlobs", rows: batch });
|
|
563
|
+
}
|
|
564
|
+
log(`imported ${blobRows.length} attachment blobs`);
|
|
565
|
+
}
|
|
566
|
+
const textBlobBuf = archive.entries.get("tree/assetTextBlobs.ndjson");
|
|
567
|
+
if (textBlobBuf) {
|
|
568
|
+
const textBlobRows = textBlobBuf.toString("utf8").split("\n").filter(Boolean).map((line) => JSON.parse(line));
|
|
569
|
+
for (let i = 0; i < textBlobRows.length; i += BATCH_SIZE) {
|
|
570
|
+
const batch = textBlobRows.slice(i, i + BATCH_SIZE);
|
|
571
|
+
await client.dump.importTables({ sessionId, table: "assetTextBlobs", rows: batch });
|
|
572
|
+
}
|
|
573
|
+
log(`imported ${textBlobRows.length} asset-text blobs`);
|
|
574
|
+
}
|
|
575
|
+
for (const table of IMPORT_ORDER) {
|
|
576
|
+
const buf = archive.entries.get(`tree/${table}.ndjson`);
|
|
577
|
+
if (!buf) continue;
|
|
578
|
+
const rows = buf.toString("utf8").split("\n").filter(Boolean).map((line) => JSON.parse(line));
|
|
579
|
+
for (let i = 0; i < rows.length; i += BATCH_SIZE) {
|
|
580
|
+
const batch = rows.slice(i, i + BATCH_SIZE);
|
|
581
|
+
await client.dump.importTables({ sessionId, table, rows: batch });
|
|
582
|
+
}
|
|
583
|
+
log(`imported ${table}: ${rows.length} rows`);
|
|
584
|
+
}
|
|
585
|
+
const docBodyRows = [...archive.entries.entries()].filter(([path]) => path.startsWith("docs/") && path.endsWith(".md")).map(([path, buf]) => ({
|
|
586
|
+
nodeId: path.slice("docs/".length, -".md".length),
|
|
587
|
+
markdown: buf.toString("utf8")
|
|
588
|
+
}));
|
|
589
|
+
if (docBodyRows.length > 0) {
|
|
590
|
+
await client.dump.importTables({ sessionId, table: "docBodies", rows: docBodyRows });
|
|
591
|
+
log(`imported ${docBodyRows.length} doc bodies`);
|
|
592
|
+
}
|
|
593
|
+
const result = await client.dump.importCommit({ sessionId });
|
|
594
|
+
return result;
|
|
595
|
+
} catch (error) {
|
|
596
|
+
await client.dump.importAbort({ sessionId }).catch(() => void 0);
|
|
597
|
+
throw error;
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// src/backup/commands.ts
|
|
602
|
+
var reportProgress = (message) => {
|
|
603
|
+
console.error(message);
|
|
604
|
+
};
|
|
605
|
+
var STATE_ONLY_BACKUP_MESSAGE = [
|
|
606
|
+
"--state-only backup is not implemented, and will not be \u2014 it is superseded by",
|
|
607
|
+
"`busabase-cli publish`, which writes the same state-level content as a readable,",
|
|
608
|
+
"diffable directory you can push to GitHub:",
|
|
609
|
+
"",
|
|
610
|
+
" busabase-cli publish <node-slug> -o ./my-package",
|
|
611
|
+
" busabase-cli install https://github.com/<you>/<repo>",
|
|
612
|
+
"",
|
|
613
|
+
"`busabase-cli backup` stays the full-fidelity backup path (raw rows, original ids,",
|
|
614
|
+
"history) \u2014 run it without --state-only. The two formats are deliberately separate:",
|
|
615
|
+
"a backup carries the audit events, comments, reviews and ACLs that a published",
|
|
616
|
+
"package must never leak."
|
|
617
|
+
].join("\n");
|
|
618
|
+
var STATE_ONLY_RESTORE_MESSAGE = [
|
|
619
|
+
"state-only archive restore is not implemented, and will not be \u2014 it is superseded",
|
|
620
|
+
"by `busabase-cli install`, which installs state-level content into an EXISTING",
|
|
621
|
+
"space as change requests you review before they go live:",
|
|
622
|
+
"",
|
|
623
|
+
" busabase-cli install https://github.com/<you>/<repo> --into-folder <name>",
|
|
624
|
+
"",
|
|
625
|
+
"`busabase-cli restore` stays the full-fidelity restore path (original ids and",
|
|
626
|
+
"history, into an empty space)."
|
|
627
|
+
].join("\n");
|
|
628
|
+
var runBackup = async (client, options) => {
|
|
629
|
+
if (options.stateOnly) throw new Error(STATE_ONLY_BACKUP_MESSAGE);
|
|
630
|
+
const { spaceId } = options;
|
|
631
|
+
if (!spaceId) {
|
|
632
|
+
throw new Error(
|
|
633
|
+
"No space configured. Pass --space-id <id>, export BUSABASE_SPACE_ID, or run `busabase-cli login`."
|
|
634
|
+
);
|
|
635
|
+
}
|
|
636
|
+
reportProgress(
|
|
637
|
+
`Backing up space ${spaceId} \u2192 ${options.outFile} (full fidelity, history=${options.includeHistory})`
|
|
638
|
+
);
|
|
639
|
+
const manifest = await exportFull({
|
|
640
|
+
client,
|
|
641
|
+
outPath: options.outFile,
|
|
642
|
+
spaceId,
|
|
643
|
+
sourceHost: options.baseUrl,
|
|
644
|
+
includeHistory: options.includeHistory,
|
|
645
|
+
toolVersion: options.toolVersion,
|
|
646
|
+
onProgress: (message) => reportProgress(` ${message}`)
|
|
647
|
+
});
|
|
648
|
+
const rows = Object.values(manifest.tables).reduce((total, count) => total + count, 0);
|
|
649
|
+
if (options.json) {
|
|
650
|
+
return {
|
|
651
|
+
file: options.outFile,
|
|
652
|
+
rows,
|
|
653
|
+
blobCount: manifest.blobCount,
|
|
654
|
+
blobBytes: manifest.blobBytes,
|
|
655
|
+
manifest
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
return [
|
|
659
|
+
`Done. ${rows} rows, ${manifest.blobCount} blobs (${manifest.blobBytes} bytes) \u2192 ${options.outFile}`,
|
|
660
|
+
"",
|
|
661
|
+
"Restore it into an empty space with:",
|
|
662
|
+
` busabase-cli restore ${options.outFile}`
|
|
663
|
+
].join("\n");
|
|
664
|
+
};
|
|
665
|
+
var runRestore = async (client, options) => {
|
|
666
|
+
if (options.resume) {
|
|
667
|
+
throw new Error("--resume is not implemented yet in this version.");
|
|
668
|
+
}
|
|
669
|
+
reportProgress(`Reading ${options.file} \u2026`);
|
|
670
|
+
const archive = await readArchive(options.file);
|
|
671
|
+
reportProgress(
|
|
672
|
+
`Archive: fidelity=${archive.manifest.fidelity} space=${archive.manifest.spaceId} exportedAt=${archive.manifest.exportedAt}`
|
|
673
|
+
);
|
|
674
|
+
if (archive.manifest.fidelity !== "full") throw new Error(STATE_ONLY_RESTORE_MESSAGE);
|
|
675
|
+
if (options.intoFolder) {
|
|
676
|
+
throw new Error("--into-folder only applies to state-only archives.");
|
|
677
|
+
}
|
|
678
|
+
const result = await importFull({
|
|
679
|
+
client,
|
|
680
|
+
archive,
|
|
681
|
+
onProgress: (message) => reportProgress(` ${message}`)
|
|
682
|
+
});
|
|
683
|
+
if (options.json) {
|
|
684
|
+
return {
|
|
685
|
+
restored: result.ok,
|
|
686
|
+
file: options.file,
|
|
687
|
+
spaceId: archive.manifest.spaceId,
|
|
688
|
+
warnings: result.warnings
|
|
689
|
+
};
|
|
690
|
+
}
|
|
691
|
+
if (!result.ok || result.warnings.length > 0) {
|
|
692
|
+
return [
|
|
693
|
+
"Restore completed with warnings:",
|
|
694
|
+
...result.warnings.map((warning) => ` \u2022 ${warning}`)
|
|
695
|
+
].join("\n");
|
|
696
|
+
}
|
|
697
|
+
return "Restore completed successfully.";
|
|
698
|
+
};
|
|
699
|
+
|
|
140
700
|
// src/banner.ts
|
|
141
701
|
var useColor = Boolean(process.stdout.isTTY) && !process.env.NO_COLOR;
|
|
142
702
|
var paint = (code, s) => useColor ? `\x1B[${code}m${s}\x1B[0m` : s;
|
|
@@ -206,7 +766,7 @@ function writeDotEnvFile(updates) {
|
|
|
206
766
|
|
|
207
767
|
// src/login.ts
|
|
208
768
|
import { spawn } from "child_process";
|
|
209
|
-
import { createHash, randomBytes } from "crypto";
|
|
769
|
+
import { createHash as createHash4, randomBytes } from "crypto";
|
|
210
770
|
import { createServer } from "http";
|
|
211
771
|
import { createInterface } from "readline/promises";
|
|
212
772
|
import { DEFAULT_BASE_URL, normalizeBaseUrl } from "busabase-sdk";
|
|
@@ -274,7 +834,7 @@ async function verifyAuth(baseUrl, token) {
|
|
|
274
834
|
}
|
|
275
835
|
async function oauthLogin(baseUrl, useBrowser) {
|
|
276
836
|
const codeVerifier = base64url(randomBytes(32));
|
|
277
|
-
const codeChallenge = base64url(
|
|
837
|
+
const codeChallenge = base64url(createHash4("sha256").update(codeVerifier).digest());
|
|
278
838
|
const state = base64url(randomBytes(16));
|
|
279
839
|
const { code, redirectUri } = await new Promise(
|
|
280
840
|
(resolve, reject) => {
|
|
@@ -585,34 +1145,34 @@ import { readFile } from "fs/promises";
|
|
|
585
1145
|
import { join as join3 } from "path";
|
|
586
1146
|
|
|
587
1147
|
// ../../packages/busabase-contract/src/domains/package/types.ts
|
|
588
|
-
import { z as
|
|
1148
|
+
import { z as z8 } from "zod";
|
|
589
1149
|
|
|
590
1150
|
// ../../packages/openlib/i18n/i-string.ts
|
|
591
|
-
import { z as
|
|
1151
|
+
import { z as z4 } from "zod";
|
|
592
1152
|
|
|
593
1153
|
// ../../packages/openlib/i18n/i18n.ts
|
|
594
|
-
import { z } from "zod";
|
|
1154
|
+
import { z as z3 } from "zod";
|
|
595
1155
|
var i18n = {
|
|
596
1156
|
defaultLocale: "en",
|
|
597
1157
|
locales: ["en", "zh-CN", "zh-TW", "ja", "ko", "de", "fr", "es", "pt"],
|
|
598
1158
|
extendLocales: ["en", "zh-CN", "zh-TW", "ja", "ko", "fr", "de", "es", "ru", "it", "vi", "pt"]
|
|
599
1159
|
};
|
|
600
|
-
var LocaleSchema =
|
|
1160
|
+
var LocaleSchema = z3.enum(i18n.locales);
|
|
601
1161
|
|
|
602
1162
|
// ../../packages/openlib/i18n/i-string.ts
|
|
603
|
-
var iStringRecordSchema =
|
|
604
|
-
var iStringSchema =
|
|
1163
|
+
var iStringRecordSchema = z4.partialRecord(LocaleSchema, z4.string());
|
|
1164
|
+
var iStringSchema = z4.union([z4.string(), iStringRecordSchema]).describe("i18n string");
|
|
605
1165
|
|
|
606
1166
|
// ../../packages/busabase-contract/src/domains/base/contract/base-schemas.ts
|
|
607
|
-
import { z as
|
|
608
|
-
var fieldNameSchema =
|
|
609
|
-
|
|
1167
|
+
import { z as z5 } from "zod";
|
|
1168
|
+
var fieldNameSchema = z5.union([
|
|
1169
|
+
z5.string().min(1),
|
|
610
1170
|
iStringRecordSchema.refine(
|
|
611
1171
|
(record) => Object.values(record).some((value) => value && value.trim().length > 0),
|
|
612
1172
|
"field name must have at least one non-empty locale value"
|
|
613
1173
|
)
|
|
614
1174
|
]);
|
|
615
|
-
var fieldTypeSchema =
|
|
1175
|
+
var fieldTypeSchema = z5.enum([
|
|
616
1176
|
"text",
|
|
617
1177
|
"longtext",
|
|
618
1178
|
"markdown",
|
|
@@ -639,85 +1199,85 @@ var fieldTypeSchema = z3.enum([
|
|
|
639
1199
|
"json",
|
|
640
1200
|
"yaml"
|
|
641
1201
|
]);
|
|
642
|
-
var fieldOptionsSchema =
|
|
643
|
-
ai:
|
|
644
|
-
model:
|
|
645
|
-
prompt:
|
|
646
|
-
reviewRequired:
|
|
647
|
-
sourceFieldIds:
|
|
1202
|
+
var fieldOptionsSchema = z5.object({
|
|
1203
|
+
ai: z5.object({
|
|
1204
|
+
model: z5.string().optional(),
|
|
1205
|
+
prompt: z5.string().optional(),
|
|
1206
|
+
reviewRequired: z5.boolean().optional(),
|
|
1207
|
+
sourceFieldIds: z5.array(z5.string()).optional()
|
|
648
1208
|
}).optional(),
|
|
649
1209
|
// Per-field config for `attachment` columns (all optional; logic enforces a
|
|
650
1210
|
// 25MB ceiling regardless).
|
|
651
|
-
attachment:
|
|
652
|
-
maxFiles:
|
|
653
|
-
allowedMimeTypes:
|
|
654
|
-
maxFileSize:
|
|
1211
|
+
attachment: z5.object({
|
|
1212
|
+
maxFiles: z5.number().int().positive().optional(),
|
|
1213
|
+
allowedMimeTypes: z5.array(z5.string()).optional(),
|
|
1214
|
+
maxFileSize: z5.number().int().positive().optional()
|
|
655
1215
|
}).optional(),
|
|
656
|
-
choices:
|
|
657
|
-
|
|
658
|
-
color:
|
|
659
|
-
id:
|
|
660
|
-
name:
|
|
1216
|
+
choices: z5.array(
|
|
1217
|
+
z5.object({
|
|
1218
|
+
color: z5.string().optional(),
|
|
1219
|
+
id: z5.string(),
|
|
1220
|
+
name: z5.string()
|
|
661
1221
|
})
|
|
662
1222
|
).optional(),
|
|
663
|
-
code:
|
|
664
|
-
language:
|
|
1223
|
+
code: z5.object({
|
|
1224
|
+
language: z5.string().optional()
|
|
665
1225
|
}).optional(),
|
|
666
|
-
embed:
|
|
667
|
-
aspectRatio:
|
|
668
|
-
height:
|
|
669
|
-
providers:
|
|
1226
|
+
embed: z5.object({
|
|
1227
|
+
aspectRatio: z5.enum(["16:9", "4:3", "1:1"]).optional(),
|
|
1228
|
+
height: z5.number().int().positive().max(1200).optional(),
|
|
1229
|
+
providers: z5.array(z5.string()).optional()
|
|
670
1230
|
}).optional(),
|
|
671
|
-
inverseFieldId:
|
|
672
|
-
multiple:
|
|
1231
|
+
inverseFieldId: z5.string().optional(),
|
|
1232
|
+
multiple: z5.boolean().optional(),
|
|
673
1233
|
// Display formatting for `number` columns (Notion-style: one number type,
|
|
674
1234
|
// a format option). `currency` renders via Intl.NumberFormat.
|
|
675
|
-
number:
|
|
676
|
-
format:
|
|
677
|
-
currency:
|
|
678
|
-
locale:
|
|
1235
|
+
number: z5.object({
|
|
1236
|
+
format: z5.enum(["plain", "currency"]).optional(),
|
|
1237
|
+
currency: z5.string().optional(),
|
|
1238
|
+
locale: z5.string().optional()
|
|
679
1239
|
}).optional(),
|
|
680
|
-
targetBaseId:
|
|
681
|
-
targetBaseSlug:
|
|
1240
|
+
targetBaseId: z5.string().optional().describe("Relation target Base id (bse_\u2026). Or pass targetBaseSlug to name it by slug."),
|
|
1241
|
+
targetBaseSlug: z5.string().optional().describe(
|
|
682
1242
|
"Relation target Base by slug \u2014 a convenience alias for targetBaseId, resolved server-side (active bases in the current space). If both are given, targetBaseId wins."
|
|
683
1243
|
)
|
|
684
1244
|
}).default({});
|
|
685
|
-
var baseFieldSchema =
|
|
686
|
-
id:
|
|
687
|
-
baseId:
|
|
688
|
-
slug:
|
|
1245
|
+
var baseFieldSchema = z5.object({
|
|
1246
|
+
id: z5.string(),
|
|
1247
|
+
baseId: z5.string(),
|
|
1248
|
+
slug: z5.string(),
|
|
689
1249
|
name: fieldNameSchema,
|
|
690
1250
|
type: fieldTypeSchema,
|
|
691
|
-
required:
|
|
692
|
-
position:
|
|
1251
|
+
required: z5.boolean(),
|
|
1252
|
+
position: z5.number(),
|
|
693
1253
|
options: fieldOptionsSchema
|
|
694
1254
|
});
|
|
695
|
-
var baseSchema =
|
|
696
|
-
id:
|
|
697
|
-
nodeId:
|
|
698
|
-
slug:
|
|
699
|
-
name:
|
|
700
|
-
description:
|
|
701
|
-
reviewPolicy:
|
|
702
|
-
kind:
|
|
703
|
-
requiredApprovals:
|
|
1255
|
+
var baseSchema = z5.object({
|
|
1256
|
+
id: z5.string(),
|
|
1257
|
+
nodeId: z5.string(),
|
|
1258
|
+
slug: z5.string(),
|
|
1259
|
+
name: z5.string(),
|
|
1260
|
+
description: z5.string(),
|
|
1261
|
+
reviewPolicy: z5.object({
|
|
1262
|
+
kind: z5.literal("single"),
|
|
1263
|
+
requiredApprovals: z5.number()
|
|
704
1264
|
}),
|
|
705
|
-
createdAt:
|
|
706
|
-
fields:
|
|
1265
|
+
createdAt: z5.string(),
|
|
1266
|
+
fields: z5.array(baseFieldSchema)
|
|
707
1267
|
});
|
|
708
|
-
var createBaseInputSchema =
|
|
709
|
-
parentNodeId:
|
|
1268
|
+
var createBaseInputSchema = z5.object({
|
|
1269
|
+
parentNodeId: z5.string().optional().describe(
|
|
710
1270
|
"Parent node id. Must be a folder or the space root; container-incapable node types (Base, Doc, AirApp, etc.) cannot hold children."
|
|
711
1271
|
),
|
|
712
|
-
slug:
|
|
713
|
-
name:
|
|
714
|
-
description:
|
|
715
|
-
fields:
|
|
716
|
-
|
|
717
|
-
slug:
|
|
1272
|
+
slug: z5.string().min(1).regex(/^[a-z0-9-]+$/),
|
|
1273
|
+
name: z5.string().min(1),
|
|
1274
|
+
description: z5.string().optional().default(""),
|
|
1275
|
+
fields: z5.array(
|
|
1276
|
+
z5.object({
|
|
1277
|
+
slug: z5.string().min(1),
|
|
718
1278
|
name: fieldNameSchema,
|
|
719
1279
|
type: fieldTypeSchema.default("text"),
|
|
720
|
-
required:
|
|
1280
|
+
required: z5.boolean().default(false),
|
|
721
1281
|
options: fieldOptionsSchema.optional().default({})
|
|
722
1282
|
})
|
|
723
1283
|
).default([]),
|
|
@@ -725,78 +1285,78 @@ var createBaseInputSchema = z3.object({
|
|
|
725
1285
|
// as a pending ChangeRequest (status "in_review") instead of creating it
|
|
726
1286
|
// immediately. Pass `autoMerge: true` only for callers that don't need human
|
|
727
1287
|
// review (seed/migration scripts, an explicit no-review agent task).
|
|
728
|
-
autoMerge:
|
|
1288
|
+
autoMerge: z5.boolean().optional().default(false)
|
|
729
1289
|
});
|
|
730
|
-
var createBaseFieldInputSchema =
|
|
1290
|
+
var createBaseFieldInputSchema = z5.object({
|
|
731
1291
|
name: fieldNameSchema,
|
|
732
1292
|
// Field slugs are snake_case identifiers (e.g. `cover_image`, `publish_date`) — the
|
|
733
1293
|
// seed and the inline-fields path on `POST /bases` already allow underscores, so the
|
|
734
1294
|
// add-field endpoint must too. (Base/folder/view slugs stay kebab-case: they go in URLs.)
|
|
735
|
-
slug:
|
|
1295
|
+
slug: z5.string().min(1).regex(/^[a-z0-9_-]+$/),
|
|
736
1296
|
type: fieldTypeSchema.default("text"),
|
|
737
|
-
required:
|
|
1297
|
+
required: z5.boolean().optional().default(false),
|
|
738
1298
|
options: fieldOptionsSchema.optional().default({})
|
|
739
1299
|
});
|
|
740
1300
|
var createFieldChangeRequestInputSchema = createBaseFieldInputSchema.extend({
|
|
741
|
-
message:
|
|
742
|
-
submittedBy:
|
|
1301
|
+
message: z5.string().optional().default("Add field"),
|
|
1302
|
+
submittedBy: z5.string().optional().default("local-editor")
|
|
743
1303
|
});
|
|
744
|
-
var deleteFieldChangeRequestInputSchema =
|
|
745
|
-
fieldId:
|
|
746
|
-
message:
|
|
747
|
-
submittedBy:
|
|
1304
|
+
var deleteFieldChangeRequestInputSchema = z5.object({
|
|
1305
|
+
fieldId: z5.string().min(1),
|
|
1306
|
+
message: z5.string().optional(),
|
|
1307
|
+
submittedBy: z5.string().optional().default("local-editor")
|
|
748
1308
|
});
|
|
749
|
-
var updateFieldChangeRequestInputSchema =
|
|
750
|
-
fieldId:
|
|
751
|
-
patch:
|
|
1309
|
+
var updateFieldChangeRequestInputSchema = z5.object({
|
|
1310
|
+
fieldId: z5.string().min(1),
|
|
1311
|
+
patch: z5.object({
|
|
752
1312
|
name: fieldNameSchema.optional(),
|
|
753
|
-
required:
|
|
1313
|
+
required: z5.boolean().optional(),
|
|
754
1314
|
options: fieldOptionsSchema.optional()
|
|
755
1315
|
}),
|
|
756
|
-
message:
|
|
757
|
-
submittedBy:
|
|
1316
|
+
message: z5.string().optional(),
|
|
1317
|
+
submittedBy: z5.string().optional().default("local-editor")
|
|
758
1318
|
});
|
|
759
|
-
var previewFieldConversionInputSchema =
|
|
760
|
-
fieldId:
|
|
1319
|
+
var previewFieldConversionInputSchema = z5.object({
|
|
1320
|
+
fieldId: z5.string().min(1),
|
|
761
1321
|
newType: fieldTypeSchema
|
|
762
1322
|
});
|
|
763
|
-
var previewFieldConversionOutputSchema =
|
|
764
|
-
totalCount:
|
|
765
|
-
convertibleCount:
|
|
766
|
-
nullCount:
|
|
767
|
-
conflicts:
|
|
1323
|
+
var previewFieldConversionOutputSchema = z5.object({
|
|
1324
|
+
totalCount: z5.number(),
|
|
1325
|
+
convertibleCount: z5.number(),
|
|
1326
|
+
nullCount: z5.number(),
|
|
1327
|
+
conflicts: z5.array(z5.object({ recordId: z5.string(), currentValue: z5.unknown() }))
|
|
768
1328
|
});
|
|
769
|
-
var convertFieldChangeRequestInputSchema =
|
|
770
|
-
fieldId:
|
|
1329
|
+
var convertFieldChangeRequestInputSchema = z5.object({
|
|
1330
|
+
fieldId: z5.string().min(1),
|
|
771
1331
|
newType: fieldTypeSchema,
|
|
772
|
-
selectChoiceMode:
|
|
773
|
-
message:
|
|
774
|
-
submittedBy:
|
|
1332
|
+
selectChoiceMode: z5.enum(["auto_create", "null_on_missing"]).default("null_on_missing"),
|
|
1333
|
+
message: z5.string().optional(),
|
|
1334
|
+
submittedBy: z5.string().optional().default("local-editor")
|
|
775
1335
|
});
|
|
776
|
-
var reorderFieldsChangeRequestInputSchema =
|
|
777
|
-
fieldIds:
|
|
778
|
-
message:
|
|
779
|
-
submittedBy:
|
|
1336
|
+
var reorderFieldsChangeRequestInputSchema = z5.object({
|
|
1337
|
+
fieldIds: z5.array(z5.string()).min(1),
|
|
1338
|
+
message: z5.string().optional(),
|
|
1339
|
+
submittedBy: z5.string().optional().default("local-editor")
|
|
780
1340
|
});
|
|
781
|
-
var archiveBaseInputSchema =
|
|
782
|
-
message:
|
|
783
|
-
submittedBy:
|
|
1341
|
+
var archiveBaseInputSchema = z5.object({
|
|
1342
|
+
message: z5.string().optional(),
|
|
1343
|
+
submittedBy: z5.string().optional().default("local-editor")
|
|
784
1344
|
});
|
|
785
|
-
var restoreBaseInputSchema =
|
|
786
|
-
message:
|
|
787
|
-
submittedBy:
|
|
1345
|
+
var restoreBaseInputSchema = z5.object({
|
|
1346
|
+
message: z5.string().optional(),
|
|
1347
|
+
submittedBy: z5.string().optional().default("local-editor")
|
|
788
1348
|
});
|
|
789
|
-
var restoreFieldChangeRequestInputSchema =
|
|
790
|
-
fieldId:
|
|
791
|
-
message:
|
|
792
|
-
submittedBy:
|
|
1349
|
+
var restoreFieldChangeRequestInputSchema = z5.object({
|
|
1350
|
+
fieldId: z5.string().min(1),
|
|
1351
|
+
message: z5.string().optional(),
|
|
1352
|
+
submittedBy: z5.string().optional().default("local-editor")
|
|
793
1353
|
});
|
|
794
1354
|
|
|
795
1355
|
// ../../packages/busabase-contract/src/domains/base/contract/view-schemas.ts
|
|
796
|
-
import { z as
|
|
1356
|
+
import { z as z7 } from "zod";
|
|
797
1357
|
|
|
798
1358
|
// ../../packages/busabase-contract/src/contract/schemas.ts
|
|
799
|
-
import { z as
|
|
1359
|
+
import { z as z6 } from "zod";
|
|
800
1360
|
|
|
801
1361
|
// ../../packages/busabase-contract/src/domains/filetree/definition.ts
|
|
802
1362
|
var fileTreeOperations = (type) => [
|
|
@@ -1025,128 +1585,140 @@ var OPERATION_META = Object.fromEntries(
|
|
|
1025
1585
|
);
|
|
1026
1586
|
|
|
1027
1587
|
// ../../packages/busabase-contract/src/contract/schemas.ts
|
|
1028
|
-
var nodeSchema =
|
|
1029
|
-
() =>
|
|
1030
|
-
id:
|
|
1031
|
-
parentId:
|
|
1032
|
-
type:
|
|
1033
|
-
slug:
|
|
1034
|
-
name:
|
|
1035
|
-
description:
|
|
1036
|
-
metadata:
|
|
1037
|
-
entryFile:
|
|
1038
|
-
visibility:
|
|
1039
|
-
version:
|
|
1040
|
-
assetId:
|
|
1588
|
+
var nodeSchema = z6.lazy(
|
|
1589
|
+
() => z6.object({
|
|
1590
|
+
id: z6.string(),
|
|
1591
|
+
parentId: z6.string().nullable(),
|
|
1592
|
+
type: z6.enum(NODE_TYPES),
|
|
1593
|
+
slug: z6.string(),
|
|
1594
|
+
name: z6.string(),
|
|
1595
|
+
description: z6.string(),
|
|
1596
|
+
metadata: z6.object({
|
|
1597
|
+
entryFile: z6.string().optional(),
|
|
1598
|
+
visibility: z6.enum(["private", "workspace", "public"]).optional(),
|
|
1599
|
+
version: z6.string().optional(),
|
|
1600
|
+
assetId: z6.string().optional()
|
|
1041
1601
|
}).default({}),
|
|
1042
|
-
position:
|
|
1043
|
-
createdAt:
|
|
1044
|
-
updatedAt:
|
|
1045
|
-
baseId:
|
|
1046
|
-
children:
|
|
1047
|
-
hasChildren:
|
|
1602
|
+
position: z6.number(),
|
|
1603
|
+
createdAt: z6.string(),
|
|
1604
|
+
updatedAt: z6.string(),
|
|
1605
|
+
baseId: z6.string().nullable(),
|
|
1606
|
+
children: z6.array(nodeSchema),
|
|
1607
|
+
hasChildren: z6.boolean().optional()
|
|
1048
1608
|
})
|
|
1049
1609
|
);
|
|
1050
|
-
var nodePrincipalSchema =
|
|
1051
|
-
id:
|
|
1052
|
-
nodeId:
|
|
1053
|
-
principalType:
|
|
1054
|
-
principalId:
|
|
1055
|
-
role:
|
|
1056
|
-
grantedBy:
|
|
1057
|
-
createdAt:
|
|
1058
|
-
updatedAt:
|
|
1610
|
+
var nodePrincipalSchema = z6.object({
|
|
1611
|
+
id: z6.string(),
|
|
1612
|
+
nodeId: z6.string(),
|
|
1613
|
+
principalType: z6.enum(["user", "team", "space"]),
|
|
1614
|
+
principalId: z6.string(),
|
|
1615
|
+
role: z6.enum(["read", "changeRequest", "write", "manage"]),
|
|
1616
|
+
grantedBy: z6.string(),
|
|
1617
|
+
createdAt: z6.string(),
|
|
1618
|
+
updatedAt: z6.string()
|
|
1059
1619
|
});
|
|
1060
|
-
var listNodesInputSchema =
|
|
1061
|
-
parentId:
|
|
1062
|
-
depth:
|
|
1620
|
+
var listNodesInputSchema = z6.object({
|
|
1621
|
+
parentId: z6.string().nullable().optional().describe("Node to start from. Omit or null to start from the space root."),
|
|
1622
|
+
depth: z6.coerce.number().int().min(1).max(5).optional().describe(
|
|
1063
1623
|
"How many levels beneath the start point to eagerly include (default 2 once either field is set). Capped at 5."
|
|
1064
1624
|
)
|
|
1065
1625
|
}).optional();
|
|
1066
|
-
var isDescendantInputSchema =
|
|
1067
|
-
nodeId:
|
|
1068
|
-
potentialAncestorId:
|
|
1626
|
+
var isDescendantInputSchema = z6.object({
|
|
1627
|
+
nodeId: z6.string(),
|
|
1628
|
+
potentialAncestorId: z6.string()
|
|
1629
|
+
});
|
|
1630
|
+
var isDescendantOutputSchema = z6.object({
|
|
1631
|
+
isDescendant: z6.boolean()
|
|
1632
|
+
});
|
|
1633
|
+
var searchNodesByNameInputSchema = z6.object({
|
|
1634
|
+
query: z6.string().min(1),
|
|
1635
|
+
limit: z6.number().int().min(1).max(50).optional().default(20)
|
|
1069
1636
|
});
|
|
1070
|
-
var
|
|
1071
|
-
|
|
1637
|
+
var nodeSearchResultSchema = z6.object({
|
|
1638
|
+
id: z6.string(),
|
|
1639
|
+
type: z6.enum(NODE_TYPES),
|
|
1640
|
+
name: z6.string(),
|
|
1641
|
+
slug: z6.string(),
|
|
1642
|
+
path: z6.string(),
|
|
1643
|
+
updatedAt: z6.string()
|
|
1072
1644
|
});
|
|
1073
|
-
var userRefSchema =
|
|
1074
|
-
id:
|
|
1075
|
-
name:
|
|
1076
|
-
email:
|
|
1077
|
-
image:
|
|
1078
|
-
role:
|
|
1645
|
+
var userRefSchema = z6.object({
|
|
1646
|
+
id: z6.string(),
|
|
1647
|
+
name: z6.string().nullable(),
|
|
1648
|
+
email: z6.string().nullable(),
|
|
1649
|
+
image: z6.string().nullable(),
|
|
1650
|
+
role: z6.string().nullable().optional()
|
|
1079
1651
|
});
|
|
1080
|
-
var commitSchema =
|
|
1081
|
-
id:
|
|
1082
|
-
baseId:
|
|
1083
|
-
targetType:
|
|
1084
|
-
nodeId:
|
|
1085
|
-
operationId:
|
|
1086
|
-
parentCommitId:
|
|
1087
|
-
fields:
|
|
1088
|
-
operation:
|
|
1089
|
-
message:
|
|
1090
|
-
author:
|
|
1652
|
+
var commitSchema = z6.object({
|
|
1653
|
+
id: z6.string(),
|
|
1654
|
+
baseId: z6.string().nullable(),
|
|
1655
|
+
targetType: z6.enum(["base", "node"]),
|
|
1656
|
+
nodeId: z6.string().nullable(),
|
|
1657
|
+
operationId: z6.string().nullable(),
|
|
1658
|
+
parentCommitId: z6.string().nullable(),
|
|
1659
|
+
fields: z6.record(z6.string(), z6.unknown()),
|
|
1660
|
+
operation: z6.enum(OPERATION_KINDS),
|
|
1661
|
+
message: z6.string(),
|
|
1662
|
+
author: z6.string(),
|
|
1091
1663
|
authorUser: userRefSchema.nullable().optional().default(null),
|
|
1092
|
-
createdAt:
|
|
1664
|
+
createdAt: z6.string()
|
|
1093
1665
|
});
|
|
1094
|
-
var operationSchema =
|
|
1095
|
-
id:
|
|
1096
|
-
changeRequestId:
|
|
1097
|
-
baseId:
|
|
1098
|
-
targetType:
|
|
1099
|
-
nodeId:
|
|
1100
|
-
operation:
|
|
1101
|
-
status:
|
|
1102
|
-
targetRecordId:
|
|
1103
|
-
targetViewId:
|
|
1104
|
-
filePath:
|
|
1105
|
-
sourceRecordId:
|
|
1106
|
-
sourceCommitId:
|
|
1107
|
-
baseCommitId:
|
|
1108
|
-
headCommitId:
|
|
1109
|
-
deleteMode:
|
|
1110
|
-
mergedRecordId:
|
|
1111
|
-
mergedViewId:
|
|
1112
|
-
position:
|
|
1113
|
-
createdAt:
|
|
1114
|
-
updatedAt:
|
|
1666
|
+
var operationSchema = z6.object({
|
|
1667
|
+
id: z6.string(),
|
|
1668
|
+
changeRequestId: z6.string(),
|
|
1669
|
+
baseId: z6.string().nullable(),
|
|
1670
|
+
targetType: z6.enum(["base", "node"]),
|
|
1671
|
+
nodeId: z6.string().nullable(),
|
|
1672
|
+
operation: z6.enum(OPERATION_KINDS),
|
|
1673
|
+
status: z6.enum(["pending", "merged", "archived", "failed"]),
|
|
1674
|
+
targetRecordId: z6.string().nullable(),
|
|
1675
|
+
targetViewId: z6.string().nullable(),
|
|
1676
|
+
filePath: z6.string().nullable(),
|
|
1677
|
+
sourceRecordId: z6.string().nullable(),
|
|
1678
|
+
sourceCommitId: z6.string().nullable(),
|
|
1679
|
+
baseCommitId: z6.string().nullable(),
|
|
1680
|
+
headCommitId: z6.string(),
|
|
1681
|
+
deleteMode: z6.enum(["archive"]),
|
|
1682
|
+
mergedRecordId: z6.string().nullable(),
|
|
1683
|
+
mergedViewId: z6.string().nullable(),
|
|
1684
|
+
position: z6.number(),
|
|
1685
|
+
createdAt: z6.string(),
|
|
1686
|
+
updatedAt: z6.string(),
|
|
1115
1687
|
headCommit: commitSchema,
|
|
1116
1688
|
// Resolved canonical "before" values for the operation's target, so the UI can
|
|
1117
1689
|
// render a true before → after field diff. Null for creations (no prior state)
|
|
1118
1690
|
// and for kinds whose prior state isn't a field map (e.g. skill files, whose
|
|
1119
1691
|
// previous content lives in storage). Records resolve from the base commit;
|
|
1120
1692
|
// views resolve from the current view row ({ name, description, config }).
|
|
1121
|
-
baseFields:
|
|
1693
|
+
baseFields: z6.record(z6.string(), z6.unknown()).nullable()
|
|
1122
1694
|
});
|
|
1123
|
-
var reviewSchema =
|
|
1124
|
-
id:
|
|
1125
|
-
changeRequestId:
|
|
1126
|
-
reviewerId:
|
|
1695
|
+
var reviewSchema = z6.object({
|
|
1696
|
+
id: z6.string(),
|
|
1697
|
+
changeRequestId: z6.string(),
|
|
1698
|
+
reviewerId: z6.string(),
|
|
1127
1699
|
reviewer: userRefSchema.nullable().optional().default(null),
|
|
1128
|
-
verdict:
|
|
1129
|
-
reason:
|
|
1130
|
-
visibleOperationHeads:
|
|
1131
|
-
createdAt:
|
|
1700
|
+
verdict: z6.enum(["approved", "rejected"]),
|
|
1701
|
+
reason: z6.string().nullable(),
|
|
1702
|
+
visibleOperationHeads: z6.record(z6.string(), z6.string()),
|
|
1703
|
+
createdAt: z6.string()
|
|
1132
1704
|
});
|
|
1133
|
-
var commentSubjectTypeSchema =
|
|
1134
|
-
var commentSchema =
|
|
1135
|
-
id:
|
|
1705
|
+
var commentSubjectTypeSchema = z6.enum(["record", "change_request", "operation", "commit"]);
|
|
1706
|
+
var commentSchema = z6.object({
|
|
1707
|
+
id: z6.string(),
|
|
1136
1708
|
subjectType: commentSubjectTypeSchema,
|
|
1137
|
-
subjectId:
|
|
1138
|
-
recordId:
|
|
1139
|
-
changeRequestId:
|
|
1140
|
-
operationId:
|
|
1141
|
-
commitId:
|
|
1142
|
-
authorId:
|
|
1709
|
+
subjectId: z6.string(),
|
|
1710
|
+
recordId: z6.string().nullable(),
|
|
1711
|
+
changeRequestId: z6.string().nullable(),
|
|
1712
|
+
operationId: z6.string().nullable(),
|
|
1713
|
+
commitId: z6.string().nullable(),
|
|
1714
|
+
authorId: z6.string(),
|
|
1143
1715
|
author: userRefSchema.nullable().optional().default(null),
|
|
1144
|
-
body:
|
|
1145
|
-
mentionsAi:
|
|
1146
|
-
createdAt:
|
|
1147
|
-
updatedAt:
|
|
1716
|
+
body: z6.string(),
|
|
1717
|
+
mentionsAi: z6.boolean(),
|
|
1718
|
+
createdAt: z6.string(),
|
|
1719
|
+
updatedAt: z6.string()
|
|
1148
1720
|
});
|
|
1149
|
-
var changeRequestStatusSchema =
|
|
1721
|
+
var changeRequestStatusSchema = z6.enum([
|
|
1150
1722
|
"in_review",
|
|
1151
1723
|
"changes_requested",
|
|
1152
1724
|
"approved",
|
|
@@ -1155,53 +1727,53 @@ var changeRequestStatusSchema = z4.enum([
|
|
|
1155
1727
|
"abandoned",
|
|
1156
1728
|
"conflict"
|
|
1157
1729
|
]);
|
|
1158
|
-
var changeRequestSchema =
|
|
1159
|
-
id:
|
|
1160
|
-
baseId:
|
|
1161
|
-
targetType:
|
|
1162
|
-
nodeId:
|
|
1730
|
+
var changeRequestSchema = z6.object({
|
|
1731
|
+
id: z6.string(),
|
|
1732
|
+
baseId: z6.string().nullable(),
|
|
1733
|
+
targetType: z6.enum(["base", "node"]),
|
|
1734
|
+
nodeId: z6.string().nullable(),
|
|
1163
1735
|
status: changeRequestStatusSchema,
|
|
1164
|
-
submittedBy:
|
|
1736
|
+
submittedBy: z6.string(),
|
|
1165
1737
|
submittedByUser: userRefSchema.nullable().optional().default(null),
|
|
1166
|
-
sourceMeta:
|
|
1167
|
-
reviewPolicySnapshot:
|
|
1168
|
-
mergeSummary:
|
|
1169
|
-
rejectedReason:
|
|
1170
|
-
reviewedAt:
|
|
1171
|
-
mergedAt:
|
|
1172
|
-
createdAt:
|
|
1173
|
-
updatedAt:
|
|
1738
|
+
sourceMeta: z6.record(z6.string(), z6.unknown()),
|
|
1739
|
+
reviewPolicySnapshot: z6.record(z6.string(), z6.unknown()),
|
|
1740
|
+
mergeSummary: z6.record(z6.string(), z6.unknown()),
|
|
1741
|
+
rejectedReason: z6.string().nullable(),
|
|
1742
|
+
reviewedAt: z6.string().nullable(),
|
|
1743
|
+
mergedAt: z6.string().nullable(),
|
|
1744
|
+
createdAt: z6.string(),
|
|
1745
|
+
updatedAt: z6.string(),
|
|
1174
1746
|
base: baseSchema.nullable(),
|
|
1175
1747
|
node: nodeSchema.nullable(),
|
|
1176
|
-
operations:
|
|
1748
|
+
operations: z6.array(operationSchema),
|
|
1177
1749
|
primaryOperation: operationSchema.nullable(),
|
|
1178
|
-
operationCount:
|
|
1179
|
-
reviews:
|
|
1750
|
+
operationCount: z6.number(),
|
|
1751
|
+
reviews: z6.array(reviewSchema)
|
|
1180
1752
|
});
|
|
1181
|
-
var agentTaskSchema =
|
|
1753
|
+
var agentTaskSchema = z6.object({
|
|
1182
1754
|
changeRequest: changeRequestSchema,
|
|
1183
|
-
trigger:
|
|
1184
|
-
reviewReason:
|
|
1185
|
-
aiComments:
|
|
1755
|
+
trigger: z6.enum(["changes_requested", "ai_mention"]),
|
|
1756
|
+
reviewReason: z6.string().nullable(),
|
|
1757
|
+
aiComments: z6.array(commentSchema)
|
|
1186
1758
|
});
|
|
1187
|
-
var searchResultSchema =
|
|
1188
|
-
id:
|
|
1189
|
-
kind:
|
|
1190
|
-
title:
|
|
1191
|
-
body:
|
|
1192
|
-
eyebrow:
|
|
1193
|
-
href:
|
|
1194
|
-
updatedAt:
|
|
1759
|
+
var searchResultSchema = z6.object({
|
|
1760
|
+
id: z6.string(),
|
|
1761
|
+
kind: z6.enum(["record", "change_request", "base", "file"]),
|
|
1762
|
+
title: z6.string(),
|
|
1763
|
+
body: z6.string(),
|
|
1764
|
+
eyebrow: z6.string(),
|
|
1765
|
+
href: z6.string(),
|
|
1766
|
+
updatedAt: z6.string().nullable()
|
|
1195
1767
|
});
|
|
1196
|
-
var searchResponseSchema =
|
|
1197
|
-
query:
|
|
1198
|
-
limit:
|
|
1199
|
-
offset:
|
|
1200
|
-
hasMore:
|
|
1201
|
-
results:
|
|
1768
|
+
var searchResponseSchema = z6.object({
|
|
1769
|
+
query: z6.string(),
|
|
1770
|
+
limit: z6.number(),
|
|
1771
|
+
offset: z6.number(),
|
|
1772
|
+
hasMore: z6.boolean(),
|
|
1773
|
+
results: z6.array(searchResultSchema)
|
|
1202
1774
|
});
|
|
1203
|
-
var liveEventSchema =
|
|
1204
|
-
kind:
|
|
1775
|
+
var liveEventSchema = z6.object({
|
|
1776
|
+
kind: z6.enum([
|
|
1205
1777
|
"change_request.created",
|
|
1206
1778
|
"change_request.updated",
|
|
1207
1779
|
"change_request.deleted",
|
|
@@ -1215,16 +1787,16 @@ var liveEventSchema = z4.object({
|
|
|
1215
1787
|
// busabase-cloud's host hook to persist an inbox notification row.
|
|
1216
1788
|
"change_request.pending_review"
|
|
1217
1789
|
]),
|
|
1218
|
-
spaceId:
|
|
1219
|
-
actorId:
|
|
1220
|
-
changeRequestId:
|
|
1221
|
-
baseId:
|
|
1222
|
-
nodeIds:
|
|
1223
|
-
recordIds:
|
|
1224
|
-
viewIds:
|
|
1225
|
-
operationCount:
|
|
1790
|
+
spaceId: z6.string(),
|
|
1791
|
+
actorId: z6.string(),
|
|
1792
|
+
changeRequestId: z6.string(),
|
|
1793
|
+
baseId: z6.string().nullable(),
|
|
1794
|
+
nodeIds: z6.array(z6.string()),
|
|
1795
|
+
recordIds: z6.array(z6.string()),
|
|
1796
|
+
viewIds: z6.array(z6.string()),
|
|
1797
|
+
operationCount: z6.number()
|
|
1226
1798
|
});
|
|
1227
|
-
var auditActionSchema =
|
|
1799
|
+
var auditActionSchema = z6.enum([
|
|
1228
1800
|
"record.viewed",
|
|
1229
1801
|
"change_request.created",
|
|
1230
1802
|
"change_request.updated",
|
|
@@ -1248,155 +1820,155 @@ var auditActionSchema = z4.enum([
|
|
|
1248
1820
|
"asset.text_marked_none",
|
|
1249
1821
|
"node.purged"
|
|
1250
1822
|
]);
|
|
1251
|
-
var auditEventSchema =
|
|
1252
|
-
id:
|
|
1823
|
+
var auditEventSchema = z6.object({
|
|
1824
|
+
id: z6.string(),
|
|
1253
1825
|
action: auditActionSchema,
|
|
1254
|
-
actorId:
|
|
1826
|
+
actorId: z6.string(),
|
|
1255
1827
|
actor: userRefSchema.nullable().optional().default(null),
|
|
1256
|
-
baseId:
|
|
1257
|
-
recordId:
|
|
1258
|
-
changeRequestId:
|
|
1259
|
-
operationId:
|
|
1260
|
-
commitId:
|
|
1261
|
-
metadata:
|
|
1262
|
-
createdAt:
|
|
1828
|
+
baseId: z6.string().nullable(),
|
|
1829
|
+
recordId: z6.string().nullable(),
|
|
1830
|
+
changeRequestId: z6.string().nullable(),
|
|
1831
|
+
operationId: z6.string().nullable(),
|
|
1832
|
+
commitId: z6.string().nullable(),
|
|
1833
|
+
metadata: z6.record(z6.string(), z6.unknown()),
|
|
1834
|
+
createdAt: z6.string()
|
|
1263
1835
|
});
|
|
1264
|
-
var createAuditEventInputSchema =
|
|
1836
|
+
var createAuditEventInputSchema = z6.object({
|
|
1265
1837
|
action: auditActionSchema,
|
|
1266
|
-
actorId:
|
|
1267
|
-
baseId:
|
|
1268
|
-
recordId:
|
|
1269
|
-
changeRequestId:
|
|
1270
|
-
operationId:
|
|
1271
|
-
commitId:
|
|
1272
|
-
metadata:
|
|
1838
|
+
actorId: z6.string().optional().default("local-viewer"),
|
|
1839
|
+
baseId: z6.string().optional().nullable(),
|
|
1840
|
+
recordId: z6.string().optional().nullable(),
|
|
1841
|
+
changeRequestId: z6.string().optional().nullable(),
|
|
1842
|
+
operationId: z6.string().optional().nullable(),
|
|
1843
|
+
commitId: z6.string().optional().nullable(),
|
|
1844
|
+
metadata: z6.record(z6.string(), z6.unknown()).optional().default({})
|
|
1273
1845
|
});
|
|
1274
|
-
var nodeOperationInputSchema =
|
|
1275
|
-
|
|
1276
|
-
kind:
|
|
1277
|
-
ref:
|
|
1846
|
+
var nodeOperationInputSchema = z6.discriminatedUnion("kind", [
|
|
1847
|
+
z6.object({
|
|
1848
|
+
kind: z6.literal("create"),
|
|
1849
|
+
ref: z6.string().min(1).optional().describe(
|
|
1278
1850
|
'Optional in-change-request temp id for this node. A later operation can set parentNodeRef to this value to nest under it \u2014 e.g. create a folder with ref "growth", then create Bases with parentNodeRef "growth", all in one change request.'
|
|
1279
1851
|
),
|
|
1280
|
-
parentNodeId:
|
|
1281
|
-
parentNodeRef:
|
|
1852
|
+
parentNodeId: z6.string().optional(),
|
|
1853
|
+
parentNodeRef: z6.string().min(1).optional().describe(
|
|
1282
1854
|
"Parent this node under a node an EARLIER operation in the same change request created (matched by its ref). Mutually exclusive with parentNodeId."
|
|
1283
1855
|
),
|
|
1284
|
-
nodeType:
|
|
1285
|
-
slug:
|
|
1286
|
-
name:
|
|
1287
|
-
description:
|
|
1288
|
-
metadata:
|
|
1856
|
+
nodeType: z6.enum(CREATABLE_NODE_TYPES),
|
|
1857
|
+
slug: z6.string().min(1).regex(/^[a-z0-9-]+$/),
|
|
1858
|
+
name: z6.string().min(1),
|
|
1859
|
+
description: z6.string().optional().default(""),
|
|
1860
|
+
metadata: z6.record(z6.string(), z6.unknown()).optional().default({}),
|
|
1289
1861
|
// Base fields (nodeType "base" only); a base needs at least one field.
|
|
1290
|
-
fields:
|
|
1291
|
-
|
|
1292
|
-
slug:
|
|
1862
|
+
fields: z6.array(
|
|
1863
|
+
z6.object({
|
|
1864
|
+
slug: z6.string().min(1).regex(/^[a-z0-9-]+$/),
|
|
1293
1865
|
name: fieldNameSchema,
|
|
1294
1866
|
type: fieldTypeSchema.default("text"),
|
|
1295
|
-
required:
|
|
1867
|
+
required: z6.boolean().optional().default(false),
|
|
1296
1868
|
options: fieldOptionsSchema.optional().default({})
|
|
1297
1869
|
})
|
|
1298
1870
|
).optional()
|
|
1299
1871
|
}),
|
|
1300
|
-
|
|
1301
|
-
kind:
|
|
1302
|
-
nodeId:
|
|
1303
|
-
slug:
|
|
1304
|
-
name:
|
|
1305
|
-
description:
|
|
1872
|
+
z6.object({
|
|
1873
|
+
kind: z6.literal("rename"),
|
|
1874
|
+
nodeId: z6.string(),
|
|
1875
|
+
slug: z6.string().min(1).regex(/^[a-z0-9-]+$/).optional(),
|
|
1876
|
+
name: z6.string().min(1).optional(),
|
|
1877
|
+
description: z6.string().optional()
|
|
1306
1878
|
}),
|
|
1307
|
-
|
|
1308
|
-
kind:
|
|
1309
|
-
nodeId:
|
|
1879
|
+
z6.object({
|
|
1880
|
+
kind: z6.literal("delete"),
|
|
1881
|
+
nodeId: z6.string()
|
|
1310
1882
|
}),
|
|
1311
|
-
|
|
1312
|
-
kind:
|
|
1313
|
-
nodeId:
|
|
1883
|
+
z6.object({
|
|
1884
|
+
kind: z6.literal("restore"),
|
|
1885
|
+
nodeId: z6.string()
|
|
1314
1886
|
}),
|
|
1315
|
-
|
|
1316
|
-
kind:
|
|
1317
|
-
nodeId:
|
|
1887
|
+
z6.object({
|
|
1888
|
+
kind: z6.literal("move"),
|
|
1889
|
+
nodeId: z6.string(),
|
|
1318
1890
|
// Exactly one of parentNodeId / parentNodeRef (a ref created earlier in this CR).
|
|
1319
|
-
parentNodeId:
|
|
1320
|
-
parentNodeRef:
|
|
1321
|
-
position:
|
|
1891
|
+
parentNodeId: z6.string().optional(),
|
|
1892
|
+
parentNodeRef: z6.string().min(1).optional(),
|
|
1893
|
+
position: z6.number().int().optional()
|
|
1322
1894
|
})
|
|
1323
1895
|
]);
|
|
1324
|
-
var createNodeChangeRequestInputSchema =
|
|
1325
|
-
message:
|
|
1896
|
+
var createNodeChangeRequestInputSchema = z6.object({
|
|
1897
|
+
message: z6.string().optional().default("Update node tree").describe(
|
|
1326
1898
|
'Explanation shown to the human reviewer. Write a conventional-commit style subject \u2014 imperative verb + what + why, e.g. "Reorganize marketing docs under a Campaigns folder".'
|
|
1327
1899
|
),
|
|
1328
|
-
submittedBy:
|
|
1329
|
-
autoMerge:
|
|
1900
|
+
submittedBy: z6.string().optional().default("local-producer"),
|
|
1901
|
+
autoMerge: z6.boolean().optional().default(false).describe(
|
|
1330
1902
|
"Whether to approve and merge this structural node change immediately. Defaults to false so node Change Request creation is review-first unless explicitly requested."
|
|
1331
1903
|
),
|
|
1332
|
-
operations:
|
|
1904
|
+
operations: z6.array(nodeOperationInputSchema).min(1)
|
|
1333
1905
|
});
|
|
1334
|
-
var moveNodeInputSchema =
|
|
1335
|
-
nodeId:
|
|
1336
|
-
parentNodeId:
|
|
1337
|
-
position:
|
|
1338
|
-
message:
|
|
1339
|
-
submittedBy:
|
|
1906
|
+
var moveNodeInputSchema = z6.object({
|
|
1907
|
+
nodeId: z6.string(),
|
|
1908
|
+
parentNodeId: z6.string().optional().describe("New parent folder node id. Omit to keep the current parent and only reorder."),
|
|
1909
|
+
position: z6.number().int().optional().describe("New position among the target parent's children."),
|
|
1910
|
+
message: z6.string().optional().describe("Reviewer-facing Change Request message."),
|
|
1911
|
+
submittedBy: z6.string().optional()
|
|
1340
1912
|
});
|
|
1341
|
-
var createDeleteChangeRequestInputSchema =
|
|
1342
|
-
message:
|
|
1913
|
+
var createDeleteChangeRequestInputSchema = z6.object({
|
|
1914
|
+
message: z6.string().optional().default("Delete record").describe(
|
|
1343
1915
|
'Explanation shown to the human reviewer. Say what is being removed and why, e.g. "Archive duplicate contact \u2014 merged into Acme Corp".'
|
|
1344
1916
|
),
|
|
1345
|
-
submittedBy:
|
|
1917
|
+
submittedBy: z6.string().optional().default("local-producer"),
|
|
1346
1918
|
// Only "archive" is supported — hard delete after retention was never
|
|
1347
1919
|
// implemented, so the API no longer accepts it (breaking change).
|
|
1348
|
-
deleteMode:
|
|
1920
|
+
deleteMode: z6.enum(["archive"]).optional().default("archive")
|
|
1349
1921
|
});
|
|
1350
|
-
var reviseOperationInputSchema =
|
|
1351
|
-
fields:
|
|
1922
|
+
var reviseOperationInputSchema = z6.object({
|
|
1923
|
+
fields: z6.record(z6.string(), z6.unknown()).describe(
|
|
1352
1924
|
"Updated field values keyed by field slug. If you set the base's PRIMARY field (its first field), keep it a short human-readable name \u2014 it is the record's display title everywhere."
|
|
1353
1925
|
),
|
|
1354
|
-
message:
|
|
1926
|
+
message: z6.string().optional().default("Revise operation").describe(
|
|
1355
1927
|
'Explanation shown to the human reviewer. Write a conventional-commit style subject \u2014 imperative verb + what + why, e.g. "Update deal stage to qualified \u2014 demo booked for July 8".'
|
|
1356
1928
|
),
|
|
1357
|
-
author:
|
|
1358
|
-
baseCommitId:
|
|
1929
|
+
author: z6.string().optional().default("local-producer"),
|
|
1930
|
+
baseCommitId: z6.string().optional()
|
|
1359
1931
|
});
|
|
1360
|
-
var reviewChangeRequestInputSchema =
|
|
1361
|
-
verdict:
|
|
1362
|
-
reason:
|
|
1932
|
+
var reviewChangeRequestInputSchema = z6.object({
|
|
1933
|
+
verdict: z6.enum(["approved", "rejected"]),
|
|
1934
|
+
reason: z6.string().optional()
|
|
1363
1935
|
});
|
|
1364
|
-
var commentSubjectInputSchema =
|
|
1936
|
+
var commentSubjectInputSchema = z6.object({
|
|
1365
1937
|
subjectType: commentSubjectTypeSchema,
|
|
1366
|
-
subjectId:
|
|
1938
|
+
subjectId: z6.string().min(1)
|
|
1367
1939
|
});
|
|
1368
1940
|
var createCommentInputSchema = commentSubjectInputSchema.extend({
|
|
1369
|
-
authorId:
|
|
1370
|
-
body:
|
|
1371
|
-
mentionsAi:
|
|
1941
|
+
authorId: z6.string().optional().default("local-admin"),
|
|
1942
|
+
body: z6.string().trim().min(1),
|
|
1943
|
+
mentionsAi: z6.boolean().optional().default(false)
|
|
1372
1944
|
});
|
|
1373
|
-
var listInputSchema =
|
|
1374
|
-
limit:
|
|
1945
|
+
var listInputSchema = z6.object({
|
|
1946
|
+
limit: z6.coerce.number().int().min(1).max(100).optional().default(50)
|
|
1375
1947
|
}).optional().default({ limit: 50 });
|
|
1376
|
-
var listChangeRequestsPagedInputSchema =
|
|
1377
|
-
limit:
|
|
1948
|
+
var listChangeRequestsPagedInputSchema = z6.object({
|
|
1949
|
+
limit: z6.coerce.number().int().min(1).max(100).optional().default(50),
|
|
1378
1950
|
/** Opaque base64 cursor (`createdAt|id`) for keyset pagination. */
|
|
1379
|
-
cursor:
|
|
1380
|
-
status:
|
|
1381
|
-
mine:
|
|
1951
|
+
cursor: z6.string().optional(),
|
|
1952
|
+
status: z6.array(changeRequestStatusSchema).optional(),
|
|
1953
|
+
mine: z6.boolean().optional()
|
|
1382
1954
|
}).optional().default({ limit: 50 });
|
|
1383
|
-
var listChangeRequestsResponseSchema =
|
|
1384
|
-
changeRequests:
|
|
1385
|
-
nextCursor:
|
|
1955
|
+
var listChangeRequestsResponseSchema = z6.object({
|
|
1956
|
+
changeRequests: z6.array(changeRequestSchema),
|
|
1957
|
+
nextCursor: z6.string().nullable()
|
|
1386
1958
|
});
|
|
1387
|
-
var changeRequestCountsSchema =
|
|
1388
|
-
review:
|
|
1389
|
-
changes:
|
|
1390
|
-
created:
|
|
1391
|
-
approved:
|
|
1392
|
-
merged:
|
|
1393
|
-
rejected:
|
|
1959
|
+
var changeRequestCountsSchema = z6.object({
|
|
1960
|
+
review: z6.number().int().nonnegative(),
|
|
1961
|
+
changes: z6.number().int().nonnegative(),
|
|
1962
|
+
created: z6.number().int().nonnegative(),
|
|
1963
|
+
approved: z6.number().int().nonnegative(),
|
|
1964
|
+
merged: z6.number().int().nonnegative(),
|
|
1965
|
+
rejected: z6.number().int().nonnegative()
|
|
1394
1966
|
});
|
|
1395
1967
|
var SEARCH_SOURCES = ["records", "files", "names"];
|
|
1396
|
-
var searchInputSchema =
|
|
1397
|
-
query:
|
|
1398
|
-
limit:
|
|
1399
|
-
offset:
|
|
1968
|
+
var searchInputSchema = z6.object({
|
|
1969
|
+
query: z6.string().default(""),
|
|
1970
|
+
limit: z6.coerce.number().int().min(1).max(100).optional().default(20),
|
|
1971
|
+
offset: z6.coerce.number().int().min(0).optional().default(0),
|
|
1400
1972
|
/**
|
|
1401
1973
|
* Restrict which content this call searches. Omitted means all three
|
|
1402
1974
|
* (unchanged behavior for every caller before this parameter existed).
|
|
@@ -1406,26 +1978,26 @@ var searchInputSchema = z4.object({
|
|
|
1406
1978
|
* (`?sources=records&sources=files`) becomes an array. Accept both shapes
|
|
1407
1979
|
* and normalize to an array.
|
|
1408
1980
|
*/
|
|
1409
|
-
sources:
|
|
1981
|
+
sources: z6.union([z6.array(z6.enum(SEARCH_SOURCES)), z6.enum(SEARCH_SOURCES)]).transform((value) => Array.isArray(value) ? value : [value]).optional()
|
|
1410
1982
|
});
|
|
1411
|
-
var authSpaceSchema =
|
|
1412
|
-
id:
|
|
1413
|
-
name:
|
|
1414
|
-
slug:
|
|
1415
|
-
plan:
|
|
1983
|
+
var authSpaceSchema = z6.object({
|
|
1984
|
+
id: z6.string(),
|
|
1985
|
+
name: z6.string(),
|
|
1986
|
+
slug: z6.string().nullable(),
|
|
1987
|
+
plan: z6.string().nullable()
|
|
1416
1988
|
});
|
|
1417
|
-
var authUserSchema =
|
|
1418
|
-
id:
|
|
1419
|
-
name:
|
|
1420
|
-
email:
|
|
1421
|
-
image:
|
|
1989
|
+
var authUserSchema = z6.object({
|
|
1990
|
+
id: z6.string(),
|
|
1991
|
+
name: z6.string(),
|
|
1992
|
+
email: z6.string().nullable(),
|
|
1993
|
+
image: z6.string().nullable()
|
|
1422
1994
|
});
|
|
1423
|
-
var authMemberSchema =
|
|
1424
|
-
userId:
|
|
1425
|
-
spaceId:
|
|
1426
|
-
role:
|
|
1995
|
+
var authMemberSchema = z6.object({
|
|
1996
|
+
userId: z6.string(),
|
|
1997
|
+
spaceId: z6.string(),
|
|
1998
|
+
role: z6.string()
|
|
1427
1999
|
});
|
|
1428
|
-
var authInfoSchema =
|
|
2000
|
+
var authInfoSchema = z6.object({
|
|
1429
2001
|
/** The space this request is scoped to (explicit `x-busabase-space` header, else the default). */
|
|
1430
2002
|
space: authSpaceSchema,
|
|
1431
2003
|
user: authUserSchema,
|
|
@@ -1435,11 +2007,11 @@ var authInfoSchema = z4.object({
|
|
|
1435
2007
|
* space-scoped — when this has more than one entry, callers should confirm the
|
|
1436
2008
|
* intended space and target it explicitly via the `x-busabase-space` header.
|
|
1437
2009
|
*/
|
|
1438
|
-
spaces:
|
|
2010
|
+
spaces: z6.array(authSpaceSchema)
|
|
1439
2011
|
});
|
|
1440
2012
|
|
|
1441
2013
|
// ../../packages/busabase-contract/src/domains/base/contract/view-schemas.ts
|
|
1442
|
-
var viewFilterOperatorSchema =
|
|
2014
|
+
var viewFilterOperatorSchema = z7.enum([
|
|
1443
2015
|
"contains",
|
|
1444
2016
|
"equals",
|
|
1445
2017
|
"not_empty",
|
|
@@ -1447,82 +2019,82 @@ var viewFilterOperatorSchema = z5.enum([
|
|
|
1447
2019
|
"is_true",
|
|
1448
2020
|
"is_false"
|
|
1449
2021
|
]);
|
|
1450
|
-
var viewFilterSchema =
|
|
1451
|
-
fieldSlug:
|
|
2022
|
+
var viewFilterSchema = z7.object({
|
|
2023
|
+
fieldSlug: z7.string(),
|
|
1452
2024
|
// Stable field identity — survives slug reuse; populated on merge.
|
|
1453
|
-
fieldId:
|
|
2025
|
+
fieldId: z7.string().optional(),
|
|
1454
2026
|
operator: viewFilterOperatorSchema,
|
|
1455
|
-
value:
|
|
2027
|
+
value: z7.unknown().optional()
|
|
1456
2028
|
});
|
|
1457
|
-
var viewSortSchema =
|
|
1458
|
-
direction:
|
|
1459
|
-
fieldSlug:
|
|
1460
|
-
fieldId:
|
|
2029
|
+
var viewSortSchema = z7.object({
|
|
2030
|
+
direction: z7.enum(["asc", "desc"]),
|
|
2031
|
+
fieldSlug: z7.string(),
|
|
2032
|
+
fieldId: z7.string().optional()
|
|
1461
2033
|
});
|
|
1462
|
-
var viewTypeSchema =
|
|
1463
|
-
var ganttScaleSchema =
|
|
1464
|
-
var galleryCoverFitSchema =
|
|
1465
|
-
var galleryCardSizeSchema =
|
|
1466
|
-
var viewConfigSchema =
|
|
1467
|
-
filters:
|
|
1468
|
-
sorts:
|
|
1469
|
-
visibleFieldSlugs:
|
|
2034
|
+
var viewTypeSchema = z7.enum(["table", "gallery", "kanban", "calendar", "gantt"]);
|
|
2035
|
+
var ganttScaleSchema = z7.enum(["week", "month"]);
|
|
2036
|
+
var galleryCoverFitSchema = z7.enum(["cover", "fit"]);
|
|
2037
|
+
var galleryCardSizeSchema = z7.enum(["small", "medium", "large"]);
|
|
2038
|
+
var viewConfigSchema = z7.object({
|
|
2039
|
+
filters: z7.array(viewFilterSchema).default([]),
|
|
2040
|
+
sorts: z7.array(viewSortSchema).default([]),
|
|
2041
|
+
visibleFieldSlugs: z7.array(z7.string()).nullable().optional(),
|
|
1470
2042
|
// ── Gallery-only presentation config (ignored by table views) ──
|
|
1471
2043
|
// Which attachment field supplies the cover image (null = no cover).
|
|
1472
|
-
coverFieldSlug:
|
|
2044
|
+
coverFieldSlug: z7.string().nullable().optional(),
|
|
1473
2045
|
coverFit: galleryCoverFitSchema.optional(),
|
|
1474
2046
|
cardSize: galleryCardSizeSchema.optional(),
|
|
1475
2047
|
// Whether to render the field label above each value on a card.
|
|
1476
|
-
showFieldLabels:
|
|
2048
|
+
showFieldLabels: z7.boolean().optional(),
|
|
1477
2049
|
// ── Kanban-only: which single-select field stacks records into columns.
|
|
1478
|
-
stackByFieldSlug:
|
|
2050
|
+
stackByFieldSlug: z7.string().nullable().optional(),
|
|
1479
2051
|
// ── Calendar-only: which date field positions records on the month grid.
|
|
1480
|
-
dateFieldSlug:
|
|
2052
|
+
dateFieldSlug: z7.string().nullable().optional(),
|
|
1481
2053
|
// ── Gantt-only: the start/end date fields bounding each record's bar, plus
|
|
1482
2054
|
// the time-axis granularity.
|
|
1483
|
-
startFieldSlug:
|
|
1484
|
-
endFieldSlug:
|
|
2055
|
+
startFieldSlug: z7.string().nullable().optional(),
|
|
2056
|
+
endFieldSlug: z7.string().nullable().optional(),
|
|
1485
2057
|
ganttScale: ganttScaleSchema.optional()
|
|
1486
2058
|
});
|
|
1487
|
-
var viewSchema =
|
|
1488
|
-
id:
|
|
1489
|
-
baseId:
|
|
1490
|
-
slug:
|
|
1491
|
-
name:
|
|
1492
|
-
description:
|
|
2059
|
+
var viewSchema = z7.object({
|
|
2060
|
+
id: z7.string(),
|
|
2061
|
+
baseId: z7.string(),
|
|
2062
|
+
slug: z7.string(),
|
|
2063
|
+
name: z7.string(),
|
|
2064
|
+
description: z7.string(),
|
|
1493
2065
|
type: viewTypeSchema,
|
|
1494
2066
|
config: viewConfigSchema,
|
|
1495
|
-
status:
|
|
1496
|
-
createdBy:
|
|
2067
|
+
status: z7.enum(["active", "archived"]),
|
|
2068
|
+
createdBy: z7.string(),
|
|
1497
2069
|
createdByUser: userRefSchema.nullable().optional().default(null),
|
|
1498
|
-
archivedAt:
|
|
1499
|
-
createdAt:
|
|
1500
|
-
updatedAt:
|
|
2070
|
+
archivedAt: z7.string().nullable(),
|
|
2071
|
+
createdAt: z7.string(),
|
|
2072
|
+
updatedAt: z7.string()
|
|
1501
2073
|
});
|
|
1502
|
-
var createViewInputSchema =
|
|
2074
|
+
var createViewInputSchema = z7.object({
|
|
1503
2075
|
config: viewConfigSchema.optional().default({ filters: [], sorts: [] }),
|
|
1504
|
-
description:
|
|
1505
|
-
message:
|
|
1506
|
-
name:
|
|
2076
|
+
description: z7.string().optional().default(""),
|
|
2077
|
+
message: z7.string().optional().default("Create view"),
|
|
2078
|
+
name: z7.string().min(1),
|
|
1507
2079
|
type: viewTypeSchema.optional().default("table"),
|
|
1508
|
-
slug:
|
|
1509
|
-
submittedBy:
|
|
2080
|
+
slug: z7.string().min(1).regex(/^[a-z0-9-]+$/).optional(),
|
|
2081
|
+
submittedBy: z7.string().optional().default("local-producer")
|
|
1510
2082
|
});
|
|
1511
|
-
var updateViewInputSchema =
|
|
2083
|
+
var updateViewInputSchema = z7.object({
|
|
1512
2084
|
config: viewConfigSchema.optional(),
|
|
1513
|
-
description:
|
|
1514
|
-
message:
|
|
1515
|
-
name:
|
|
2085
|
+
description: z7.string().optional(),
|
|
2086
|
+
message: z7.string().optional().default("Update view"),
|
|
2087
|
+
name: z7.string().min(1).optional(),
|
|
1516
2088
|
type: viewTypeSchema.optional(),
|
|
1517
|
-
submittedBy:
|
|
2089
|
+
submittedBy: z7.string().optional().default("local-producer")
|
|
1518
2090
|
});
|
|
1519
|
-
var deleteViewInputSchema =
|
|
1520
|
-
message:
|
|
1521
|
-
submittedBy:
|
|
2091
|
+
var deleteViewInputSchema = z7.object({
|
|
2092
|
+
message: z7.string().optional().default("Delete view"),
|
|
2093
|
+
submittedBy: z7.string().optional().default("local-producer")
|
|
1522
2094
|
});
|
|
1523
|
-
var restoreViewInputSchema =
|
|
1524
|
-
message:
|
|
1525
|
-
submittedBy:
|
|
2095
|
+
var restoreViewInputSchema = z7.object({
|
|
2096
|
+
message: z7.string().optional().default("Restore view"),
|
|
2097
|
+
submittedBy: z7.string().optional().default("local-producer")
|
|
1526
2098
|
});
|
|
1527
2099
|
|
|
1528
2100
|
// ../../packages/busabase-contract/src/domains/package/types.ts
|
|
@@ -1552,129 +2124,129 @@ var PACKAGE_MAX_FILE_BYTES = 25 * 1024 * 1024;
|
|
|
1552
2124
|
var PACKAGE_MAX_TOTAL_BYTES = 250 * 1024 * 1024;
|
|
1553
2125
|
var PACKAGE_MAX_FILE_COUNT = 5e3;
|
|
1554
2126
|
var PACKAGE_MAX_RECORDS_PER_BASE = 5e4;
|
|
1555
|
-
var PackageManifestSchema =
|
|
1556
|
-
format:
|
|
2127
|
+
var PackageManifestSchema = z8.object({
|
|
2128
|
+
format: z8.literal(PACKAGE_FORMAT),
|
|
1557
2129
|
/** Default `--into-folder` name. Not the repo name — a repo can host many packages. */
|
|
1558
|
-
name:
|
|
1559
|
-
description:
|
|
2130
|
+
name: z8.string().min(1),
|
|
2131
|
+
description: z8.string().default(""),
|
|
1560
2132
|
/**
|
|
1561
2133
|
* Informational / reserved for a future marketplace index. The real version pin
|
|
1562
2134
|
* is always the git ref in the install URL.
|
|
1563
2135
|
*/
|
|
1564
|
-
version:
|
|
1565
|
-
author:
|
|
1566
|
-
license:
|
|
1567
|
-
homepage:
|
|
1568
|
-
tags:
|
|
2136
|
+
version: z8.string().optional(),
|
|
2137
|
+
author: z8.string().optional(),
|
|
2138
|
+
license: z8.string().optional(),
|
|
2139
|
+
homepage: z8.string().url().optional(),
|
|
2140
|
+
tags: z8.array(z8.string()).default([])
|
|
1569
2141
|
});
|
|
1570
|
-
var PackageFileTreeNodeMetaSchema =
|
|
1571
|
-
type:
|
|
1572
|
-
name:
|
|
1573
|
-
description:
|
|
1574
|
-
position:
|
|
2142
|
+
var PackageFileTreeNodeMetaSchema = z8.object({
|
|
2143
|
+
type: z8.enum(PACKAGE_FILE_TREE_NODE_TYPES),
|
|
2144
|
+
name: z8.string().min(1),
|
|
2145
|
+
description: z8.string().default(""),
|
|
2146
|
+
position: z8.number().int().optional()
|
|
1575
2147
|
});
|
|
1576
|
-
var PackageFolderMetaSchema =
|
|
2148
|
+
var PackageFolderMetaSchema = z8.object({
|
|
1577
2149
|
/** Defaults to the humanized slug when absent. */
|
|
1578
|
-
name:
|
|
1579
|
-
description:
|
|
1580
|
-
position:
|
|
2150
|
+
name: z8.string().optional(),
|
|
2151
|
+
description: z8.string().default(""),
|
|
2152
|
+
position: z8.number().int().optional()
|
|
1581
2153
|
});
|
|
1582
2154
|
var PackageFileNodeMetaSchema = PackageFolderMetaSchema;
|
|
1583
|
-
var PackageDocFrontmatterSchema =
|
|
1584
|
-
name:
|
|
1585
|
-
description:
|
|
1586
|
-
position:
|
|
2155
|
+
var PackageDocFrontmatterSchema = z8.object({
|
|
2156
|
+
name: z8.string().min(1),
|
|
2157
|
+
description: z8.string().default(""),
|
|
2158
|
+
position: z8.number().int().optional()
|
|
1587
2159
|
});
|
|
1588
|
-
var PackageFieldOptionsSchema =
|
|
1589
|
-
ai:
|
|
1590
|
-
model:
|
|
1591
|
-
prompt:
|
|
1592
|
-
reviewRequired:
|
|
2160
|
+
var PackageFieldOptionsSchema = z8.object({
|
|
2161
|
+
ai: z8.object({
|
|
2162
|
+
model: z8.string().optional(),
|
|
2163
|
+
prompt: z8.string().optional(),
|
|
2164
|
+
reviewRequired: z8.boolean().optional(),
|
|
1593
2165
|
/** Package-only — replaces `ai.sourceFieldIds`. */
|
|
1594
|
-
sourceFieldSlugs:
|
|
2166
|
+
sourceFieldSlugs: z8.array(z8.string()).optional()
|
|
1595
2167
|
}).optional(),
|
|
1596
|
-
attachment:
|
|
1597
|
-
maxFiles:
|
|
1598
|
-
allowedMimeTypes:
|
|
1599
|
-
maxFileSize:
|
|
2168
|
+
attachment: z8.object({
|
|
2169
|
+
maxFiles: z8.number().int().positive().optional(),
|
|
2170
|
+
allowedMimeTypes: z8.array(z8.string()).optional(),
|
|
2171
|
+
maxFileSize: z8.number().int().positive().optional()
|
|
1600
2172
|
}).optional(),
|
|
1601
|
-
choices:
|
|
1602
|
-
|
|
1603
|
-
color:
|
|
1604
|
-
id:
|
|
1605
|
-
name:
|
|
2173
|
+
choices: z8.array(
|
|
2174
|
+
z8.object({
|
|
2175
|
+
color: z8.string().optional(),
|
|
2176
|
+
id: z8.string(),
|
|
2177
|
+
name: z8.string()
|
|
1606
2178
|
})
|
|
1607
2179
|
).optional(),
|
|
1608
|
-
code:
|
|
1609
|
-
language:
|
|
2180
|
+
code: z8.object({
|
|
2181
|
+
language: z8.string().optional()
|
|
1610
2182
|
}).optional(),
|
|
1611
|
-
embed:
|
|
1612
|
-
aspectRatio:
|
|
1613
|
-
height:
|
|
1614
|
-
providers:
|
|
2183
|
+
embed: z8.object({
|
|
2184
|
+
aspectRatio: z8.enum(["16:9", "4:3", "1:1"]).optional(),
|
|
2185
|
+
height: z8.number().int().positive().max(1200).optional(),
|
|
2186
|
+
providers: z8.array(z8.string()).optional()
|
|
1615
2187
|
}).optional(),
|
|
1616
2188
|
/** Package-only — replaces `inverseFieldId`. */
|
|
1617
|
-
inverseFieldSlug:
|
|
1618
|
-
multiple:
|
|
1619
|
-
number:
|
|
1620
|
-
format:
|
|
1621
|
-
currency:
|
|
1622
|
-
locale:
|
|
2189
|
+
inverseFieldSlug: z8.string().optional(),
|
|
2190
|
+
multiple: z8.boolean().optional(),
|
|
2191
|
+
number: z8.object({
|
|
2192
|
+
format: z8.enum(["plain", "currency"]).optional(),
|
|
2193
|
+
currency: z8.string().optional(),
|
|
2194
|
+
locale: z8.string().optional()
|
|
1623
2195
|
}).optional(),
|
|
1624
2196
|
/** Native API alias — the server resolves it to `targetBaseId`. */
|
|
1625
|
-
targetBaseSlug:
|
|
2197
|
+
targetBaseSlug: z8.string().optional()
|
|
1626
2198
|
}).default({});
|
|
1627
|
-
var PackageBaseFieldSchema =
|
|
1628
|
-
slug:
|
|
2199
|
+
var PackageBaseFieldSchema = z8.object({
|
|
2200
|
+
slug: z8.string().min(1),
|
|
1629
2201
|
name: fieldNameSchema,
|
|
1630
2202
|
type: fieldTypeSchema,
|
|
1631
|
-
required:
|
|
1632
|
-
position:
|
|
2203
|
+
required: z8.boolean().default(false),
|
|
2204
|
+
position: z8.number().int(),
|
|
1633
2205
|
options: PackageFieldOptionsSchema
|
|
1634
2206
|
});
|
|
1635
|
-
var PackageViewFilterSchema =
|
|
1636
|
-
fieldSlug:
|
|
2207
|
+
var PackageViewFilterSchema = z8.object({
|
|
2208
|
+
fieldSlug: z8.string().min(1),
|
|
1637
2209
|
operator: viewFilterOperatorSchema,
|
|
1638
|
-
value:
|
|
2210
|
+
value: z8.unknown().optional()
|
|
1639
2211
|
});
|
|
1640
|
-
var PackageViewSortSchema =
|
|
1641
|
-
direction:
|
|
1642
|
-
fieldSlug:
|
|
2212
|
+
var PackageViewSortSchema = z8.object({
|
|
2213
|
+
direction: z8.enum(["asc", "desc"]),
|
|
2214
|
+
fieldSlug: z8.string().min(1)
|
|
1643
2215
|
});
|
|
1644
|
-
var PackageViewConfigSchema =
|
|
1645
|
-
filters:
|
|
1646
|
-
sorts:
|
|
1647
|
-
visibleFieldSlugs:
|
|
2216
|
+
var PackageViewConfigSchema = z8.object({
|
|
2217
|
+
filters: z8.array(PackageViewFilterSchema).default([]),
|
|
2218
|
+
sorts: z8.array(PackageViewSortSchema).default([]),
|
|
2219
|
+
visibleFieldSlugs: z8.array(z8.string()).nullable().optional()
|
|
1648
2220
|
});
|
|
1649
|
-
var PackageViewSchema =
|
|
1650
|
-
slug:
|
|
1651
|
-
name:
|
|
1652
|
-
description:
|
|
1653
|
-
type:
|
|
2221
|
+
var PackageViewSchema = z8.object({
|
|
2222
|
+
slug: z8.string().min(1),
|
|
2223
|
+
name: z8.string().min(1),
|
|
2224
|
+
description: z8.string().default(""),
|
|
2225
|
+
type: z8.literal("table").default("table"),
|
|
1654
2226
|
config: PackageViewConfigSchema.default({ filters: [], sorts: [] })
|
|
1655
2227
|
});
|
|
1656
|
-
var PackageBaseSchema =
|
|
1657
|
-
name:
|
|
1658
|
-
description:
|
|
1659
|
-
position:
|
|
2228
|
+
var PackageBaseSchema = z8.object({
|
|
2229
|
+
name: z8.string().min(1),
|
|
2230
|
+
description: z8.string().default(""),
|
|
2231
|
+
position: z8.number().int().optional(),
|
|
1660
2232
|
/**
|
|
1661
2233
|
* Informational only — `reviewPolicy` is NOT settable via `bases.create`, so
|
|
1662
2234
|
* install cannot apply it and warns when it differs from the target's default.
|
|
1663
2235
|
*/
|
|
1664
|
-
reviewPolicy:
|
|
1665
|
-
kind:
|
|
1666
|
-
requiredApprovals:
|
|
2236
|
+
reviewPolicy: z8.object({
|
|
2237
|
+
kind: z8.literal("single"),
|
|
2238
|
+
requiredApprovals: z8.number()
|
|
1667
2239
|
}).optional(),
|
|
1668
|
-
fields:
|
|
1669
|
-
views:
|
|
2240
|
+
fields: z8.array(PackageBaseFieldSchema).default([]),
|
|
2241
|
+
views: z8.array(PackageViewSchema).default([])
|
|
1670
2242
|
});
|
|
1671
|
-
var PackageRecordLineSchema =
|
|
1672
|
-
key:
|
|
1673
|
-
fields:
|
|
2243
|
+
var PackageRecordLineSchema = z8.object({
|
|
2244
|
+
key: z8.string().min(1),
|
|
2245
|
+
fields: z8.record(z8.string(), z8.unknown()).default({})
|
|
1674
2246
|
});
|
|
1675
2247
|
|
|
1676
2248
|
// src/package/apply.ts
|
|
1677
|
-
import { createHash as
|
|
2249
|
+
import { createHash as createHash5 } from "crypto";
|
|
1678
2250
|
|
|
1679
2251
|
// src/package/tree.ts
|
|
1680
2252
|
import { extname } from "path";
|
|
@@ -2047,7 +2619,7 @@ var createFileTreeNode = async (client, node, parentNodeId, options, state) => {
|
|
|
2047
2619
|
var UploadTargetUnsupportedError = class extends Error {
|
|
2048
2620
|
};
|
|
2049
2621
|
var uploadAsset = async (client, fileName, mimeType, bytes) => {
|
|
2050
|
-
const contentHash = `sha256:${
|
|
2622
|
+
const contentHash = `sha256:${createHash5("sha256").update(bytes).digest("hex")}`;
|
|
2051
2623
|
const requested = await client.assets.createUploadUrl({
|
|
2052
2624
|
fileName,
|
|
2053
2625
|
mimeType,
|
|
@@ -3338,15 +3910,15 @@ Nothing was created. Re-run with --rename to install the colliding items under s
|
|
|
3338
3910
|
};
|
|
3339
3911
|
|
|
3340
3912
|
// src/package/commands.ts
|
|
3341
|
-
var
|
|
3913
|
+
var reportProgress2 = (message) => {
|
|
3342
3914
|
console.error(message);
|
|
3343
3915
|
};
|
|
3344
3916
|
var runInstall = async (client, repoUrl, options) => {
|
|
3345
|
-
|
|
3917
|
+
reportProgress2(`Fetching ${repoUrl} \u2026`);
|
|
3346
3918
|
const { source, files } = await fetchGithubPackageFiles(repoUrl, {
|
|
3347
3919
|
githubToken: options.githubToken
|
|
3348
3920
|
});
|
|
3349
|
-
|
|
3921
|
+
reportProgress2(`Downloaded ${files.size} file(s) from ${source.owner}/${source.repo}.`);
|
|
3350
3922
|
const tree = readPackageTree(files);
|
|
3351
3923
|
const [nodes, bases] = await Promise.all([client.nodes.list(), client.bases.list()]);
|
|
3352
3924
|
const targetFolderSlug = options.intoFolder ?? tree.manifest.name;
|
|
@@ -3368,7 +3940,7 @@ Dry run \u2014 nothing was created.`;
|
|
|
3368
3940
|
const result = await applyInstall(client, plan, {
|
|
3369
3941
|
autoMerge: Boolean(options.autoMerge),
|
|
3370
3942
|
submittedBy: `busabase-cli install (${tree.manifest.name})`,
|
|
3371
|
-
onProgress:
|
|
3943
|
+
onProgress: reportProgress2
|
|
3372
3944
|
});
|
|
3373
3945
|
if (options.json) return { installed: true, ...result };
|
|
3374
3946
|
return renderInstallReport(plan.targetFolderSlug, result);
|
|
@@ -3412,7 +3984,7 @@ var runPublish = async (client, nodeSlugOrId, options) => {
|
|
|
3412
3984
|
const source = findSourceNode(nodes, nodeSlugOrId);
|
|
3413
3985
|
const warnings = [];
|
|
3414
3986
|
const manifest = await resolveManifest(options.outDir, options.name, source.name, source.slug);
|
|
3415
|
-
|
|
3987
|
+
reportProgress2(`Reading "${source.slug}" \u2026`);
|
|
3416
3988
|
const tree = await collectPackageTree(client, source, {
|
|
3417
3989
|
manifest,
|
|
3418
3990
|
warn: (message) => warnings.push(message),
|
|
@@ -3666,7 +4238,7 @@ async function uploadAsset2(config, opts) {
|
|
|
3666
4238
|
const fileName = opts.fileName ?? basename(filePath);
|
|
3667
4239
|
const mimeType = opts.mimeType ?? guessMimeType2(filePath);
|
|
3668
4240
|
const context = opts.context ?? "record-field";
|
|
3669
|
-
const contentHash = `sha256:${
|
|
4241
|
+
const contentHash = `sha256:${createHash6("sha256").update(file).digest("hex")}`;
|
|
3670
4242
|
const requested = await rawRequest(config, "POST", "/api/v1/assets/upload-urls", {
|
|
3671
4243
|
fileName,
|
|
3672
4244
|
mimeType,
|
|
@@ -4496,6 +5068,60 @@ Examples:
|
|
|
4496
5068
|
})
|
|
4497
5069
|
)
|
|
4498
5070
|
);
|
|
5071
|
+
addGlobalFlags(program.command("backup")).description("Back up the active space to a full-fidelity .bbdump archive").option("-o, --out-file <file>", "output archive path", "space.bbdump").option(
|
|
5072
|
+
"--no-history",
|
|
5073
|
+
"full-fidelity rows, but omit the history tables (commits, change requests, operations, comments, reviews, audit events)"
|
|
5074
|
+
).option("--state-only", "superseded by `busabase-cli publish` \u2014 see the error text").addHelpText(
|
|
5075
|
+
"after",
|
|
5076
|
+
`
|
|
5077
|
+
A backup is full fidelity: raw rows with their original ids, every doc body, and
|
|
5078
|
+
every attachment's bytes, in one integrity-checked archive. Vault items and
|
|
5079
|
+
webhook signing secrets are never written to it.
|
|
5080
|
+
|
|
5081
|
+
Examples:
|
|
5082
|
+
busabase-cli backup # \u2192 ./space.bbdump
|
|
5083
|
+
busabase-cli backup -o ./backups/acme-2026-07-19.bbdump
|
|
5084
|
+
busabase-cli backup --no-history # smaller: current state only
|
|
5085
|
+
|
|
5086
|
+
To hand content to someone else rather than back it up, use \`busabase-cli publish\`,
|
|
5087
|
+
which writes a readable, diffable package directory you can push to GitHub.`
|
|
5088
|
+
).action(
|
|
5089
|
+
runAction(
|
|
5090
|
+
state,
|
|
5091
|
+
(client, opts, config) => runBackup(client, {
|
|
5092
|
+
outFile: opts.outFile,
|
|
5093
|
+
includeHistory: opts.history !== false,
|
|
5094
|
+
stateOnly: Boolean(opts.stateOnly),
|
|
5095
|
+
json: config.output === "json",
|
|
5096
|
+
baseUrl: config.baseUrl,
|
|
5097
|
+
spaceId: config.spaceId,
|
|
5098
|
+
toolVersion: pkgVersion()
|
|
5099
|
+
})
|
|
5100
|
+
)
|
|
5101
|
+
);
|
|
5102
|
+
addGlobalFlags(program.command("restore")).description("Restore a .bbdump archive into an EMPTY space, preserving original ids").argument("<file>", "path to the .bbdump archive").option("--resume", "resume a previously interrupted restore (not yet implemented)").option("--into-folder <name>", "state-only archives only \u2014 see the error text").addHelpText(
|
|
5103
|
+
"after",
|
|
5104
|
+
`
|
|
5105
|
+
The archive's checksum is verified before a single row is written, and the target
|
|
5106
|
+
space must be EMPTY \u2014 a restore replays original ids, so it cannot merge into a
|
|
5107
|
+
space that already holds content.
|
|
5108
|
+
|
|
5109
|
+
Examples:
|
|
5110
|
+
busabase-cli restore ./space.bbdump
|
|
5111
|
+
busabase-cli restore ./backups/acme-2026-07-19.bbdump --space-id spc_123
|
|
5112
|
+
|
|
5113
|
+
To add content to a space that is already in use, use \`busabase-cli install\` instead.`
|
|
5114
|
+
).action(
|
|
5115
|
+
runArgAction(
|
|
5116
|
+
state,
|
|
5117
|
+
(file, client, opts, config) => runRestore(client, {
|
|
5118
|
+
file,
|
|
5119
|
+
resume: Boolean(opts.resume),
|
|
5120
|
+
intoFolder: opts.intoFolder,
|
|
5121
|
+
json: config.output === "json"
|
|
5122
|
+
})
|
|
5123
|
+
)
|
|
5124
|
+
);
|
|
4499
5125
|
addGlobalFlags(program.command("api")).description("Raw request to any /api/v1 endpoint").addOption(
|
|
4500
5126
|
new Option("--method <get|post|put|delete>", "HTTP method").choices(["get", "post", "put", "delete"]).makeOptionMandatory()
|
|
4501
5127
|
).requiredOption("--path <p>", "path under /api/v1, e.g. /bases").option("--query <k=v...>", "query-string param, repeatable").option("--body-json <json>", "JSON request body").action(
|