@lark-apaas/db-schema-sync 0.1.23 → 0.1.24-alpha.20261028024931
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/bin.js +204 -42
- package/dist/{chunk-PXGXJ64L.js → chunk-KHVF5NNE.js} +942 -89
- package/dist/index.d.ts +113 -3
- package/dist/index.js +7 -1
- package/package.json +2 -2
package/dist/bin.js
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from "node:module"; const require = createRequire(import.meta.url);
|
|
3
3
|
import {
|
|
4
|
+
DATASOURCE_CONF_ENV,
|
|
5
|
+
DATASOURCE_TYPES,
|
|
4
6
|
__commonJS,
|
|
5
7
|
__require,
|
|
6
8
|
__toESM,
|
|
7
|
-
|
|
9
|
+
detectEngine,
|
|
10
|
+
fetchListTableView,
|
|
11
|
+
generateSchemaFromData,
|
|
12
|
+
normalizeApiResponse,
|
|
13
|
+
parseDatasourceConf,
|
|
8
14
|
resolveEnvOptions,
|
|
9
15
|
validateGeneratedSchema
|
|
10
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-KHVF5NNE.js";
|
|
11
17
|
|
|
12
18
|
// node_modules/dotenv/package.json
|
|
13
19
|
var require_package = __commonJS({
|
|
@@ -300,8 +306,8 @@ var require_main = __commonJS({
|
|
|
300
306
|
const shortPaths = [];
|
|
301
307
|
for (const filePath of optionPaths) {
|
|
302
308
|
try {
|
|
303
|
-
const
|
|
304
|
-
shortPaths.push(
|
|
309
|
+
const relative2 = path.relative(process.cwd(), filePath);
|
|
310
|
+
shortPaths.push(relative2);
|
|
305
311
|
} catch (e) {
|
|
306
312
|
if (debug) {
|
|
307
313
|
_debug(`Failed to load ${filePath} ${e.message}`);
|
|
@@ -406,10 +412,11 @@ var require_main = __commonJS({
|
|
|
406
412
|
|
|
407
413
|
// src/bin.ts
|
|
408
414
|
var import_dotenv = __toESM(require_main(), 1);
|
|
409
|
-
import { resolve
|
|
410
|
-
import {
|
|
415
|
+
import { resolve as resolve2 } from "path";
|
|
416
|
+
import { existsSync as existsSync2 } from "fs";
|
|
411
417
|
|
|
412
418
|
// src/cli-args.ts
|
|
419
|
+
var ENGINES = ["auto", "dataloom", "base"];
|
|
413
420
|
function parseArgs(argv) {
|
|
414
421
|
const args = argv.slice(2);
|
|
415
422
|
const command = args[0] === "validate" ? "validate" : "generate";
|
|
@@ -424,9 +431,179 @@ function parseArgs(argv) {
|
|
|
424
431
|
output: getArg("--output"),
|
|
425
432
|
input: getArg("--input"),
|
|
426
433
|
project: getArg("--project"),
|
|
427
|
-
exportCustomTypes: hasFlag("--export-custom-types")
|
|
434
|
+
exportCustomTypes: hasFlag("--export-custom-types"),
|
|
435
|
+
engine: parseEngine(getArg("--engine")),
|
|
436
|
+
type: parseType(getArg("--type"))
|
|
428
437
|
};
|
|
429
438
|
}
|
|
439
|
+
function parseType(value) {
|
|
440
|
+
if (value === void 0) return void 0;
|
|
441
|
+
if (DATASOURCE_TYPES.includes(value)) return value;
|
|
442
|
+
throw new Error(
|
|
443
|
+
`[db-schema-sync] Invalid --type "${value}", expected one of: ${DATASOURCE_TYPES.join(", ")}`
|
|
444
|
+
);
|
|
445
|
+
}
|
|
446
|
+
function parseEngine(value) {
|
|
447
|
+
if (value === void 0) return "auto";
|
|
448
|
+
if (ENGINES.includes(value)) return value;
|
|
449
|
+
throw new Error(
|
|
450
|
+
`[db-schema-sync] Invalid --engine "${value}", expected one of: ${ENGINES.join(", ")}`
|
|
451
|
+
);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// src/sync.ts
|
|
455
|
+
import { dirname, join, relative, resolve } from "path";
|
|
456
|
+
import {
|
|
457
|
+
existsSync,
|
|
458
|
+
mkdirSync,
|
|
459
|
+
renameSync,
|
|
460
|
+
rmSync,
|
|
461
|
+
writeFileSync
|
|
462
|
+
} from "fs";
|
|
463
|
+
var DEFAULT_OUTPUT = "server/database/schema.ts";
|
|
464
|
+
var BASE_FILE_NAME = "base.ts";
|
|
465
|
+
var errorMessage = (err) => err instanceof Error ? err.message : String(err);
|
|
466
|
+
function writeFilesAtomic(files) {
|
|
467
|
+
const suffix = `.tmp-${process.pid}-${Date.now()}`;
|
|
468
|
+
const temps = [];
|
|
469
|
+
try {
|
|
470
|
+
for (const f of files) {
|
|
471
|
+
mkdirSync(dirname(f.path), { recursive: true });
|
|
472
|
+
temps.push(f.path + suffix);
|
|
473
|
+
writeFileSync(f.path + suffix, f.content, "utf8");
|
|
474
|
+
}
|
|
475
|
+
} catch (err) {
|
|
476
|
+
for (const t of temps) rmSync(t, { force: true });
|
|
477
|
+
throw err;
|
|
478
|
+
}
|
|
479
|
+
for (const f of files) renameSync(f.path + suffix, f.path);
|
|
480
|
+
}
|
|
481
|
+
function generatePgSchema(raw, engine, exportCustomTypes) {
|
|
482
|
+
const source = generateSchemaFromData(
|
|
483
|
+
// Base apps are treated as one default schema for now: pgTable, no pgSchema prefix
|
|
484
|
+
normalizeApiResponse(raw, engine, { baseSchemas: false }),
|
|
485
|
+
{ exportCustomTypes }
|
|
486
|
+
);
|
|
487
|
+
if (!source.includes("pgTable") && !source.includes("pgView")) {
|
|
488
|
+
console.warn(
|
|
489
|
+
"[db-schema-sync] \u26A0 Warning: No tables or views found in database. The generated schema will be empty."
|
|
490
|
+
);
|
|
491
|
+
}
|
|
492
|
+
return source;
|
|
493
|
+
}
|
|
494
|
+
function resolveOutput(o) {
|
|
495
|
+
return o.args.output || o.env.DB_SCHEMA_OUTPUT || DEFAULT_OUTPUT;
|
|
496
|
+
}
|
|
497
|
+
async function runSync(o) {
|
|
498
|
+
let conf;
|
|
499
|
+
try {
|
|
500
|
+
conf = parseDatasourceConf(o.env[DATASOURCE_CONF_ENV]);
|
|
501
|
+
} catch (err) {
|
|
502
|
+
console.error(`[db-schema-sync] \u2717 ${errorMessage(err)}`);
|
|
503
|
+
return {
|
|
504
|
+
mode: "datasources",
|
|
505
|
+
failures: [{ name: DATASOURCE_CONF_ENV, message: errorMessage(err) }]
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
return conf ? runDatasources(conf, o) : runLegacy(o);
|
|
509
|
+
}
|
|
510
|
+
async function runLegacy(o) {
|
|
511
|
+
const { args } = o;
|
|
512
|
+
if (args.type === "base") {
|
|
513
|
+
console.log(
|
|
514
|
+
`[db-schema-sync] \u672A\u914D\u7F6E ${DATASOURCE_CONF_ENV}\uFF0C\u6CA1\u6709 base \u6570\u636E\u6E90\u53EF\u540C\u6B65`
|
|
515
|
+
);
|
|
516
|
+
return { mode: "legacy", failures: [] };
|
|
517
|
+
}
|
|
518
|
+
const output = resolveOutput(o);
|
|
519
|
+
const fetchSchema = o.fetchSchema ?? fetchListTableView;
|
|
520
|
+
try {
|
|
521
|
+
const options = resolveEnvOptions(o.env);
|
|
522
|
+
console.log("[db-schema-sync] Starting...");
|
|
523
|
+
console.log(
|
|
524
|
+
`[db-schema-sync] app_id=${options.appId}, dbBranch=${options.dbBranch}, exportCustomTypes=${args.exportCustomTypes}, engine=${args.engine}`
|
|
525
|
+
);
|
|
526
|
+
const raw = await fetchSchema(options);
|
|
527
|
+
const schema = generatePgSchema(raw, args.engine, args.exportCustomTypes);
|
|
528
|
+
writeFilesAtomic([{ path: resolve(o.cwd, output), content: schema }]);
|
|
529
|
+
console.log(`[db-schema-sync] \u2713 Schema written to ${output}`);
|
|
530
|
+
return { mode: "legacy", failures: [] };
|
|
531
|
+
} catch (err) {
|
|
532
|
+
console.error(`[db-schema-sync] \u2717 Failed:`, errorMessage(err));
|
|
533
|
+
return {
|
|
534
|
+
mode: "legacy",
|
|
535
|
+
failures: [{ name: "schema", message: errorMessage(err) }]
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
function assertEngineMatches(ds, raw) {
|
|
540
|
+
const engine = detectEngine(raw);
|
|
541
|
+
if (ds.type === "base" && engine !== "base") {
|
|
542
|
+
throw new Error(
|
|
543
|
+
`\u914D\u7F6E\u4E3A base\uFF0C\u4F46\u63A5\u53E3\u8FD4\u56DE\u7684\u4E0D\u662F Base \u5F15\u64CE\uFF08engine=${String(raw?.engine ?? "\u672A\u6807\u8BB0")}\uFF0C\u5B57\u6BB5 extraInfo \u4E5F\u4E0D\u5E26 Base \u5C5E\u6027\uFF09`
|
|
544
|
+
);
|
|
545
|
+
}
|
|
546
|
+
if (ds.type === "pg" && engine === "base") {
|
|
547
|
+
throw new Error("\u914D\u7F6E\u4E3A pg\uFF0C\u4F46\u63A5\u53E3\u8FD4\u56DE\u7684\u662F Base \u5F15\u64CE");
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
async function runDatasources(conf, o) {
|
|
551
|
+
const { args, cwd } = o;
|
|
552
|
+
const fetchSchema = o.fetchSchema ?? fetchListTableView;
|
|
553
|
+
const pgPath = resolve(cwd, resolveOutput(o));
|
|
554
|
+
const basePath = join(dirname(pgPath), BASE_FILE_NAME);
|
|
555
|
+
const rel = (p) => relative(cwd, p) || ".";
|
|
556
|
+
const types = args.type ? [args.type] : DATASOURCE_TYPES;
|
|
557
|
+
const failures = [];
|
|
558
|
+
let appOptions;
|
|
559
|
+
try {
|
|
560
|
+
appOptions = resolveEnvOptions(o.env);
|
|
561
|
+
} catch (err) {
|
|
562
|
+
console.error(`[db-schema-sync] \u2717 Failed:`, errorMessage(err));
|
|
563
|
+
return { mode: "datasources", failures: [{ name: "app_id", message: errorMessage(err) }] };
|
|
564
|
+
}
|
|
565
|
+
console.log("[db-schema-sync] Starting...");
|
|
566
|
+
const listed = conf.datasources.map((d) => `${d.type}:${d.id}`).join(", ") || "(none)";
|
|
567
|
+
console.log(
|
|
568
|
+
`[db-schema-sync] app_id=${appOptions.appId}, dbBranch=${appOptions.dbBranch}, datasources=${listed}, type=${args.type ?? "all"}`
|
|
569
|
+
);
|
|
570
|
+
if (args.engine !== "auto") {
|
|
571
|
+
console.warn(
|
|
572
|
+
`[db-schema-sync] \u26A0 \u5DF2\u914D\u7F6E ${DATASOURCE_CONF_ENV}\uFF0C\u5FFD\u7565 --engine\uFF08\u5F15\u64CE\u7531\u6570\u636E\u6E90 type \u51B3\u5B9A\uFF09`
|
|
573
|
+
);
|
|
574
|
+
}
|
|
575
|
+
for (const type of types) {
|
|
576
|
+
if (conf.datasources.some((d) => d.type === type)) continue;
|
|
577
|
+
const existing = type === "pg" ? pgPath : basePath;
|
|
578
|
+
console.log(
|
|
579
|
+
existsSync(existing) ? `[db-schema-sync] \u914D\u7F6E\u4E2D\u6CA1\u6709 ${type} \u6570\u636E\u6E90\uFF0C\u4FDD\u7559\u5DF2\u6709\u7684 ${rel(existing)}` : `[db-schema-sync] \u914D\u7F6E\u4E2D\u6CA1\u6709 ${type} \u6570\u636E\u6E90\uFF0C\u8DF3\u8FC7`
|
|
580
|
+
);
|
|
581
|
+
}
|
|
582
|
+
for (const ds of conf.datasources.filter((d) => types.includes(d.type))) {
|
|
583
|
+
const label = `${ds.type} \u6570\u636E\u6E90 ${ds.id}`;
|
|
584
|
+
try {
|
|
585
|
+
const raw = await fetchSchema({
|
|
586
|
+
appId: appOptions.appId,
|
|
587
|
+
timeoutMs: appOptions.timeoutMs,
|
|
588
|
+
datasourceId: ds.id,
|
|
589
|
+
...ds.type === "pg" ? { dbBranch: appOptions.dbBranch } : {}
|
|
590
|
+
});
|
|
591
|
+
assertEngineMatches(ds, raw);
|
|
592
|
+
const [path, schema] = ds.type === "pg" ? [pgPath, generatePgSchema(raw, "dataloom", args.exportCustomTypes)] : [basePath, generatePgSchema(raw, "base", true)];
|
|
593
|
+
writeFilesAtomic([{ path, content: schema }]);
|
|
594
|
+
console.log(`[db-schema-sync] \u2713 ${label} \u2192 ${rel(path)}`);
|
|
595
|
+
} catch (err) {
|
|
596
|
+
failures.push({ name: label, message: errorMessage(err) });
|
|
597
|
+
console.error(`[db-schema-sync] \u2717 ${label} \u540C\u6B65\u5931\u8D25\uFF1A${errorMessage(err)}`);
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
if (failures.length > 0) {
|
|
601
|
+
console.error(
|
|
602
|
+
`[db-schema-sync] \u2717 ${failures.length} \u4E2A\u6570\u636E\u6E90\u540C\u6B65\u5931\u8D25\uFF0C\u5176\u6587\u4EF6\u4FDD\u6301\u539F\u6837\uFF1A${failures.map((f) => f.name).join("\u3001")}`
|
|
603
|
+
);
|
|
604
|
+
}
|
|
605
|
+
return { mode: "datasources", failures };
|
|
606
|
+
}
|
|
430
607
|
|
|
431
608
|
// src/bin.ts
|
|
432
609
|
function getValidateExitCode(result) {
|
|
@@ -436,59 +613,44 @@ function getValidateExitCode(result) {
|
|
|
436
613
|
return 1;
|
|
437
614
|
}
|
|
438
615
|
async function main() {
|
|
439
|
-
|
|
616
|
+
let cliArgs;
|
|
617
|
+
try {
|
|
618
|
+
cliArgs = parseArgs(process.argv);
|
|
619
|
+
} catch (err) {
|
|
620
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
621
|
+
process.exit(1);
|
|
622
|
+
}
|
|
440
623
|
if (cliArgs.command === "validate") {
|
|
441
624
|
if (!cliArgs.input) {
|
|
442
|
-
const
|
|
625
|
+
const output2 = {
|
|
443
626
|
ok: false,
|
|
444
627
|
status: "inconclusive",
|
|
445
628
|
errorType: "validation_runner_failed",
|
|
446
629
|
durationMs: 0,
|
|
447
630
|
message: "--input is required for validate command"
|
|
448
631
|
};
|
|
449
|
-
console.log(JSON.stringify(
|
|
632
|
+
console.log(JSON.stringify(output2));
|
|
450
633
|
process.exit(1);
|
|
451
634
|
}
|
|
452
|
-
const
|
|
635
|
+
const output = validateGeneratedSchema({
|
|
453
636
|
input: cliArgs.input,
|
|
454
637
|
project: cliArgs.project
|
|
455
638
|
});
|
|
456
|
-
console.log(JSON.stringify(
|
|
457
|
-
process.exit(getValidateExitCode(
|
|
639
|
+
console.log(JSON.stringify(output));
|
|
640
|
+
process.exit(getValidateExitCode(output));
|
|
458
641
|
}
|
|
459
642
|
for (const name of [".env.local", ".env"]) {
|
|
460
|
-
const envPath =
|
|
461
|
-
if (
|
|
643
|
+
const envPath = resolve2(process.cwd(), name);
|
|
644
|
+
if (existsSync2(envPath)) {
|
|
462
645
|
(0, import_dotenv.config)({ path: envPath });
|
|
463
646
|
console.log(`[db-schema-sync] \u2713 Loaded ${name}`);
|
|
464
647
|
}
|
|
465
648
|
}
|
|
466
|
-
const
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
);
|
|
473
|
-
const schema = await generateSchema({
|
|
474
|
-
...options,
|
|
475
|
-
exportCustomTypes: cliArgs.exportCustomTypes
|
|
476
|
-
});
|
|
477
|
-
if (!schema.includes("pgTable") && !schema.includes("pgView")) {
|
|
478
|
-
console.warn(
|
|
479
|
-
"[db-schema-sync] \u26A0 Warning: No tables or views found in database. The generated schema will be empty."
|
|
480
|
-
);
|
|
481
|
-
}
|
|
482
|
-
const outputPath = resolve(process.cwd(), output);
|
|
483
|
-
mkdirSync(dirname(outputPath), { recursive: true });
|
|
484
|
-
writeFileSync(outputPath, schema, "utf8");
|
|
485
|
-
console.log(`[db-schema-sync] \u2713 Schema written to ${output}`);
|
|
486
|
-
} catch (err) {
|
|
487
|
-
console.error(
|
|
488
|
-
`[db-schema-sync] \u2717 Failed:`,
|
|
489
|
-
err instanceof Error ? err.message : String(err)
|
|
490
|
-
);
|
|
491
|
-
process.exit(1);
|
|
492
|
-
}
|
|
649
|
+
const { failures } = await runSync({
|
|
650
|
+
cwd: process.cwd(),
|
|
651
|
+
env: process.env,
|
|
652
|
+
args: cliArgs
|
|
653
|
+
});
|
|
654
|
+
if (failures.length > 0) process.exit(1);
|
|
493
655
|
}
|
|
494
656
|
main();
|