@objectstack/cli 10.0.0 → 10.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/compile.d.ts.map +1 -1
- package/dist/commands/compile.js +34 -2
- package/dist/commands/compile.js.map +1 -1
- package/dist/commands/doctor.js +1 -1
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/lint.js +1 -1
- package/dist/commands/lint.js.map +1 -1
- package/dist/commands/migrate/apply.d.ts +19 -0
- package/dist/commands/migrate/apply.d.ts.map +1 -0
- package/dist/commands/migrate/apply.js +155 -0
- package/dist/commands/migrate/apply.js.map +1 -0
- package/dist/commands/migrate/index.d.ts +9 -0
- package/dist/commands/migrate/index.d.ts.map +1 -0
- package/dist/commands/migrate/index.js +10 -0
- package/dist/commands/migrate/index.js.map +1 -0
- package/dist/commands/migrate/plan.d.ts +16 -0
- package/dist/commands/migrate/plan.d.ts.map +1 -0
- package/dist/commands/migrate/plan.js +93 -0
- package/dist/commands/migrate/plan.js.map +1 -0
- package/dist/commands/package/publish.js +4 -4
- package/dist/commands/serve.d.ts.map +1 -1
- package/dist/commands/serve.js +74 -98
- package/dist/commands/serve.js.map +1 -1
- package/dist/commands/start.d.ts.map +1 -1
- package/dist/commands/start.js +10 -0
- package/dist/commands/start.js.map +1 -1
- package/dist/commands/validate.d.ts.map +1 -1
- package/dist/commands/validate.js +70 -3
- package/dist/commands/validate.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/utils/schema-migrate.d.ts +26 -0
- package/dist/utils/schema-migrate.d.ts.map +1 -0
- package/dist/utils/schema-migrate.integration.test.d.ts +2 -0
- package/dist/utils/schema-migrate.integration.test.d.ts.map +1 -0
- package/dist/utils/schema-migrate.integration.test.js +83 -0
- package/dist/utils/schema-migrate.integration.test.js.map +1 -0
- package/dist/utils/schema-migrate.js +123 -0
- package/dist/utils/schema-migrate.js.map +1 -0
- package/package.json +48 -47
- package/dist/commands/publish.d.ts +0 -17
- package/dist/commands/publish.d.ts.map +0 -1
- package/dist/commands/publish.js +0 -135
- package/dist/commands/publish.js.map +0 -1
- package/dist/commands/rollback.d.ts +0 -13
- package/dist/commands/rollback.d.ts.map +0 -1
- package/dist/commands/rollback.js +0 -77
- package/dist/commands/rollback.js.map +0 -1
- package/dist/utils/validate-expressions.d.ts +0 -19
- package/dist/utils/validate-expressions.d.ts.map +0 -1
- package/dist/utils/validate-expressions.js +0 -201
- package/dist/utils/validate-expressions.js.map +0 -1
- package/dist/utils/validate-expressions.test.d.ts +0 -2
- package/dist/utils/validate-expressions.test.d.ts.map +0 -1
- package/dist/utils/validate-expressions.test.js +0 -320
- package/dist/utils/validate-expressions.test.js.map +0 -1
- package/dist/utils/validate-widget-bindings.d.ts +0 -74
- package/dist/utils/validate-widget-bindings.d.ts.map +0 -1
- package/dist/utils/validate-widget-bindings.js +0 -304
- package/dist/utils/validate-widget-bindings.js.map +0 -1
- package/dist/utils/validate-widget-bindings.test.d.ts +0 -2
- package/dist/utils/validate-widget-bindings.test.d.ts.map +0 -1
- package/dist/utils/validate-widget-bindings.test.js +0 -285
- package/dist/utils/validate-widget-bindings.test.js.map +0 -1
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
|
+
import { Command, Flags } from '@oclif/core';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { printHeader, printSuccess, printWarning, printError, printInfo, printStep, createTimer, } from '../../utils/format.js';
|
|
5
|
+
import { bootSchemaStack, renderPlan, summarize } from '../../utils/schema-migrate.js';
|
|
6
|
+
/**
|
|
7
|
+
* `os migrate plan` — dry-run diff of metadata vs the physical database,
|
|
8
|
+
* categorised safe / needs-confirm / destructive (issue #2186). Never mutates
|
|
9
|
+
* the schema.
|
|
10
|
+
*/
|
|
11
|
+
export default class MigratePlan extends Command {
|
|
12
|
+
static description = 'Show how the physical database has drifted from metadata (dry run; no changes applied)';
|
|
13
|
+
static examples = [
|
|
14
|
+
'$ os migrate plan',
|
|
15
|
+
'$ os migrate plan --json',
|
|
16
|
+
'$ os migrate plan --database-url postgres://localhost/app',
|
|
17
|
+
];
|
|
18
|
+
static flags = {
|
|
19
|
+
'database-url': Flags.string({
|
|
20
|
+
description: 'Database URL to inspect (defaults to $OS_DATABASE_URL / the project DB)',
|
|
21
|
+
env: 'OS_DATABASE_URL',
|
|
22
|
+
}),
|
|
23
|
+
json: Flags.boolean({ description: 'Output as JSON' }),
|
|
24
|
+
};
|
|
25
|
+
async run() {
|
|
26
|
+
const { flags } = await this.parse(MigratePlan);
|
|
27
|
+
const timer = createTimer();
|
|
28
|
+
if (!flags.json) {
|
|
29
|
+
printHeader('Migrate · plan');
|
|
30
|
+
printStep('Booting schema stack…');
|
|
31
|
+
}
|
|
32
|
+
let stack;
|
|
33
|
+
try {
|
|
34
|
+
stack = await bootSchemaStack({ databaseUrl: flags['database-url'] });
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
if (flags.json) {
|
|
38
|
+
console.log(JSON.stringify({ error: error.message }));
|
|
39
|
+
this.exit(1);
|
|
40
|
+
}
|
|
41
|
+
printError(error.message || String(error));
|
|
42
|
+
this.exit(1);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
if (!stack.driver) {
|
|
47
|
+
if (flags.json) {
|
|
48
|
+
console.log(JSON.stringify({ error: 'no_sql_driver', changes: [] }));
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
printWarning('Schema migration is only supported on SQL drivers (SQLite / Postgres). No SQL driver is active.');
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const drift = await stack.driver.detectManagedDrift();
|
|
55
|
+
if (flags.json) {
|
|
56
|
+
console.log(JSON.stringify({
|
|
57
|
+
database: stack.dbLabel,
|
|
58
|
+
managedTables: stack.managedTableCount,
|
|
59
|
+
total: drift.length,
|
|
60
|
+
changes: drift,
|
|
61
|
+
duration: timer.elapsed(),
|
|
62
|
+
}, null, 2));
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
printInfo(`Database: ${chalk.white(stack.dbLabel)}`);
|
|
66
|
+
printInfo(`Examined ${chalk.white(String(stack.managedTableCount))} managed table(s).`);
|
|
67
|
+
console.log('');
|
|
68
|
+
if (drift.length === 0) {
|
|
69
|
+
printSuccess('Physical schema is in sync with metadata — nothing to migrate.');
|
|
70
|
+
console.log('');
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
renderPlan(drift);
|
|
74
|
+
printInfo(summarize(drift));
|
|
75
|
+
console.log(chalk.dim(' Apply with: ') + chalk.white('os migrate apply') +
|
|
76
|
+
chalk.dim(' (add --allow-destructive for drops / tightenings)'));
|
|
77
|
+
console.log(chalk.dim(` ${timer.display()}`));
|
|
78
|
+
console.log('');
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
if (flags.json) {
|
|
82
|
+
console.log(JSON.stringify({ error: error.message }));
|
|
83
|
+
this.exit(1);
|
|
84
|
+
}
|
|
85
|
+
printError(error.message || String(error));
|
|
86
|
+
this.exit(1);
|
|
87
|
+
}
|
|
88
|
+
finally {
|
|
89
|
+
await stack.shutdown();
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=plan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan.js","sourceRoot":"","sources":["../../../src/commands/migrate/plan.ts"],"names":[],"mappings":"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"}
|
|
@@ -11,10 +11,10 @@
|
|
|
11
11
|
* into sys_package_version.manifest_json (status=published).
|
|
12
12
|
* 3. (optional) auto-install into a target environment via --env.
|
|
13
13
|
*
|
|
14
|
-
* This is the "upload my local code to my org" path
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
14
|
+
* This is the "upload my local code to my org" path — the single supported
|
|
15
|
+
* way to publish. (The legacy direct-to-environment `os publish` / `os
|
|
16
|
+
* rollback` commands, which wrote sys_environment_revision, have been
|
|
17
|
+
* removed.)
|
|
18
18
|
*/
|
|
19
19
|
import { readFile } from 'node:fs/promises';
|
|
20
20
|
import { resolve as resolvePath, basename, dirname, isAbsolute } from 'node:path';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/commands/serve.ts"],"names":[],"mappings":"AAEA,OAAO,EAAQ,OAAO,EAAS,MAAM,aAAa,CAAC;AAyInD,MAAM,CAAC,OAAO,OAAO,KAAM,SAAQ,OAAO;IACxC,OAAgB,WAAW,SAAkM;IAE7N,OAAgB,IAAI;;MAElB;IAEF,OAAgB,KAAK;;;;;;;;;;MAoBnB;IAEF;;;;;;;;;;;;;;;;;OAiBG;IACH,MAAM,CAAC,QAAQ,CAAC,sBAAsB,EAAE,SAAS,MAAM,EAAE,CAQtD;IAEH;;;;OAIG;IACH,MAAM,CAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAIpD;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/commands/serve.ts"],"names":[],"mappings":"AAEA,OAAO,EAAQ,OAAO,EAAS,MAAM,aAAa,CAAC;AAyInD,MAAM,CAAC,OAAO,OAAO,KAAM,SAAQ,OAAO;IACxC,OAAgB,WAAW,SAAkM;IAE7N,OAAgB,IAAI;;MAElB;IAEF,OAAgB,KAAK;;;;;;;;;;MAoBnB;IAEF;;;;;;;;;;;;;;;;;OAiBG;IACH,MAAM,CAAC,QAAQ,CAAC,sBAAsB,EAAE,SAAS,MAAM,EAAE,CAQtD;IAEH;;;;OAIG;IACH,MAAM,CAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAIpD;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CA80D3B"}
|
package/dist/commands/serve.js
CHANGED
|
@@ -412,7 +412,7 @@ export default class Serve extends Command {
|
|
|
412
412
|
// "missing artifact" error and assemble a bare kernel that
|
|
413
413
|
// can later install marketplace apps at runtime.
|
|
414
414
|
const { createDefaultHostConfig } = await import('@objectstack/runtime');
|
|
415
|
-
const bootResult = await createDefaultHostConfig({ requireArtifact: !useEmptyBoot });
|
|
415
|
+
const bootResult = await createDefaultHostConfig({ requireArtifact: !useEmptyBoot, dev: isDev });
|
|
416
416
|
config = { ...originalConfig, ...bootResult };
|
|
417
417
|
}
|
|
418
418
|
else if (resolvedMode === 'standalone') {
|
|
@@ -423,6 +423,9 @@ export default class Serve extends Command {
|
|
|
423
423
|
const standaloneInput = {
|
|
424
424
|
...(config.standalone ?? {}),
|
|
425
425
|
projectRoot: (config.standalone?.projectRoot ?? path.dirname(absolutePath)),
|
|
426
|
+
// #2229: dev enables the native-better-sqlite3 → wasm → in-memory
|
|
427
|
+
// step-down in the shared datasource factory; prod fails loudly.
|
|
428
|
+
dev: isDev,
|
|
426
429
|
};
|
|
427
430
|
const bootResult = await createStandaloneStack(standaloneInput);
|
|
428
431
|
config = { ...originalConfig, ...bootResult };
|
|
@@ -516,10 +519,39 @@ export default class Serve extends Command {
|
|
|
516
519
|
envLevel: readLogLevelEnv(),
|
|
517
520
|
}),
|
|
518
521
|
};
|
|
522
|
+
// Cluster wiring: env-driven driver selection (mirrors OS_DATABASE_URL).
|
|
523
|
+
// The remote driver self-registers on import; import it dynamically so it
|
|
524
|
+
// works in BOTH config-boot and compiled-artifact mode. Open-core ships
|
|
525
|
+
// only the in-memory driver — remote drivers (e.g. redis) come from the EE
|
|
526
|
+
// distribution; if absent we fall back to the in-memory cluster.
|
|
527
|
+
let clusterConfig;
|
|
528
|
+
const __clusterDriver = process.env.OS_CLUSTER_DRIVER?.trim();
|
|
529
|
+
if (__clusterDriver && __clusterDriver !== 'memory') {
|
|
530
|
+
// Multi-node authorization gate (open mechanism): a distribution (e.g.
|
|
531
|
+
// an EE license) may deny multi-node. On denial, downgrade to
|
|
532
|
+
// single-node rather than fail — multi-node is an add-on, never brick.
|
|
533
|
+
// Dynamic, non-literal specifier so the CLI does not statically depend
|
|
534
|
+
// on the cluster package (mirrors the remote-driver import below).
|
|
535
|
+
const __clusterPkg = '@objectstack/service-cluster';
|
|
536
|
+
const { checkMultiNodeAllowed } = (await import(__clusterPkg));
|
|
537
|
+
const __gate = checkMultiNodeAllowed();
|
|
538
|
+
if (!__gate.allowed) {
|
|
539
|
+
console.warn(`[cluster] multi-node not authorized (${__gate.reason ?? 'denied'}) — ` +
|
|
540
|
+
`downgrading to single-node (in-memory cluster). Remove OS_CLUSTER_DRIVER to silence.`);
|
|
541
|
+
}
|
|
542
|
+
else {
|
|
543
|
+
try {
|
|
544
|
+
await import(`@objectstack/service-cluster-${__clusterDriver}`);
|
|
545
|
+
}
|
|
546
|
+
catch { /* may already be registered by the loaded config */ }
|
|
547
|
+
clusterConfig = { driver: __clusterDriver, url: process.env.OS_REDIS_URL };
|
|
548
|
+
}
|
|
549
|
+
}
|
|
519
550
|
const runtime = new Runtime({
|
|
520
551
|
kernel: {
|
|
521
552
|
logger: loggerConfig
|
|
522
|
-
}
|
|
553
|
+
},
|
|
554
|
+
cluster: clusterConfig,
|
|
523
555
|
});
|
|
524
556
|
const kernel = runtime.getKernel();
|
|
525
557
|
// Load plugins from configuration
|
|
@@ -587,16 +619,25 @@ export default class Serve extends Command {
|
|
|
587
619
|
resolvedDatabaseUrl = databaseUrl ?? 'mongodb://localhost:27017/objectstack';
|
|
588
620
|
}
|
|
589
621
|
else if (driverType === 'sqlite' || driverType === 'sql') {
|
|
590
|
-
const { SqlDriver } = await import('@objectstack/driver-sql');
|
|
591
622
|
const filePath = (databaseUrl ?? ':memory:').replace(/^file:/, '').replace(/^sqlite:/, '').replace(/^sql:\/\//, '');
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
623
|
+
// Probe-by-connect with a dev-only native → wasm → in-memory
|
|
624
|
+
// step-down (#2229). better-sqlite3 loads its native addon lazily
|
|
625
|
+
// (first query), so an ABI mismatch is invisible here and would
|
|
626
|
+
// otherwise surface much later as a runtime crash. resolveSqliteDriver
|
|
627
|
+
// forces the load and degrades gracefully in dev / fails loudly in prod.
|
|
628
|
+
const { resolveSqliteDriver } = await import('@objectstack/service-datasource');
|
|
629
|
+
const resolved = await resolveSqliteDriver({
|
|
630
|
+
filename: filePath,
|
|
631
|
+
dev: isDev,
|
|
632
|
+
// #2186: in dev, self-heal a persisted DB when a metadata change
|
|
633
|
+
// relaxes a constraint (loosen-only; never destructive / never in prod).
|
|
634
|
+
autoMigrate: isDev ? 'safe' : undefined,
|
|
635
|
+
warn: (m) => console.warn(chalk.yellow(m)),
|
|
636
|
+
});
|
|
637
|
+
await kernel.use(new DriverPlugin(resolved.driver));
|
|
638
|
+
trackPlugin(resolved.engine === 'memory' ? 'MemoryDriver' : resolved.engine === 'sqlite-wasm' ? 'SqliteWasmDriver' : 'SqlDriver');
|
|
639
|
+
resolvedDriverLabel = resolved.label;
|
|
640
|
+
resolvedDatabaseUrl = resolved.engine === 'memory' ? '(in-memory)' : (databaseUrl ?? ':memory:');
|
|
600
641
|
}
|
|
601
642
|
else if (driverType === 'sqlite-wasm' || driverType === 'wasm-sqlite' || driverType === 'wasm') {
|
|
602
643
|
const { SqliteWasmDriver } = await import('@objectstack/driver-sqlite-wasm');
|
|
@@ -615,6 +656,7 @@ export default class Serve extends Command {
|
|
|
615
656
|
client: 'pg',
|
|
616
657
|
connection: databaseUrl,
|
|
617
658
|
pool: { min: 0, max: 5 },
|
|
659
|
+
autoMigrate: isDev ? 'safe' : undefined, // #2186 dev loosen-only self-heal
|
|
618
660
|
})));
|
|
619
661
|
trackPlugin('PostgresDriver');
|
|
620
662
|
resolvedDriverLabel = 'SqlDriver(pg)';
|
|
@@ -626,99 +668,33 @@ export default class Serve extends Command {
|
|
|
626
668
|
client: 'mysql2',
|
|
627
669
|
connection: databaseUrl,
|
|
628
670
|
pool: { min: 0, max: 5 },
|
|
671
|
+
autoMigrate: isDev ? 'safe' : undefined, // #2186 dev loosen-only self-heal
|
|
629
672
|
})));
|
|
630
673
|
trackPlugin('MySQLDriver');
|
|
631
674
|
resolvedDriverLabel = 'SqlDriver(mysql2)';
|
|
632
675
|
resolvedDatabaseUrl = databaseUrl;
|
|
633
676
|
}
|
|
634
677
|
else if (isDev) {
|
|
635
|
-
// Default in dev: prefer native SQLite for
|
|
636
|
-
//
|
|
637
|
-
//
|
|
638
|
-
//
|
|
639
|
-
//
|
|
640
|
-
//
|
|
641
|
-
//
|
|
642
|
-
//
|
|
643
|
-
//
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
const { SqlDriver } = await import('@objectstack/driver-sql');
|
|
656
|
-
sqliteDriver = new SqlDriver({
|
|
657
|
-
client: 'better-sqlite3',
|
|
658
|
-
connection: { filename: ':memory:' },
|
|
659
|
-
useNullAsDefault: true,
|
|
660
|
-
});
|
|
661
|
-
await sqliteDriver.connect();
|
|
662
|
-
sqliteOk = true;
|
|
663
|
-
}
|
|
664
|
-
catch {
|
|
665
|
-
sqliteOk = false;
|
|
666
|
-
if (sqliteDriver?.disconnect) {
|
|
667
|
-
try {
|
|
668
|
-
await sqliteDriver.disconnect();
|
|
669
|
-
}
|
|
670
|
-
catch { /* ignore */ }
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
if (sqliteOk) {
|
|
674
|
-
await kernel.use(new DriverPlugin(sqliteDriver));
|
|
675
|
-
trackPlugin('SqlDriver');
|
|
676
|
-
resolvedDriverLabel = 'SqlDriver(sqlite)';
|
|
677
|
-
resolvedDatabaseUrl = ':memory:';
|
|
678
|
-
}
|
|
679
|
-
else {
|
|
680
|
-
// Native unavailable → try the pure-JS wasm SQLite driver before
|
|
681
|
-
// giving up on SQL fidelity entirely. Same probe-by-connect
|
|
682
|
-
// approach: actually open the connection so a load failure is
|
|
683
|
-
// caught here rather than on the first real query.
|
|
684
|
-
let wasmDriver;
|
|
685
|
-
let wasmOk = false;
|
|
686
|
-
try {
|
|
687
|
-
const { SqliteWasmDriver } = await import('@objectstack/driver-sqlite-wasm');
|
|
688
|
-
wasmDriver = new SqliteWasmDriver({
|
|
689
|
-
filename: ':memory:',
|
|
690
|
-
persist: 'on-disconnect',
|
|
691
|
-
});
|
|
692
|
-
await wasmDriver.connect();
|
|
693
|
-
wasmOk = true;
|
|
694
|
-
}
|
|
695
|
-
catch {
|
|
696
|
-
wasmOk = false;
|
|
697
|
-
if (wasmDriver?.disconnect) {
|
|
698
|
-
try {
|
|
699
|
-
await wasmDriver.disconnect();
|
|
700
|
-
}
|
|
701
|
-
catch { /* ignore */ }
|
|
702
|
-
}
|
|
703
|
-
}
|
|
704
|
-
if (wasmOk) {
|
|
705
|
-
await kernel.use(new DriverPlugin(wasmDriver));
|
|
706
|
-
trackPlugin('SqliteWasmDriver');
|
|
707
|
-
resolvedDriverLabel = 'SqliteWasmDriver';
|
|
708
|
-
resolvedDatabaseUrl = ':memory:';
|
|
709
|
-
console.warn(chalk.yellow(' ⚠ native better-sqlite3 unavailable (ABI mismatch or not built) — dev using wasm SQLite (real SQL, slower).\n' +
|
|
710
|
-
' Rebuild better-sqlite3 for native speed, or set OS_DATABASE_DRIVER=sqlite-wasm to silence this.'));
|
|
711
|
-
}
|
|
712
|
-
else {
|
|
713
|
-
const { InMemoryDriver } = await import('@objectstack/driver-memory');
|
|
714
|
-
await kernel.use(new DriverPlugin(new InMemoryDriver()));
|
|
715
|
-
trackPlugin('MemoryDriver');
|
|
716
|
-
resolvedDriverLabel = 'InMemoryDriver';
|
|
717
|
-
resolvedDatabaseUrl = '(in-memory)';
|
|
718
|
-
console.warn(chalk.yellow(' ⚠ neither native nor wasm SQLite available — dev falling back to InMemoryDriver (mingo, not real SQL).\n' +
|
|
719
|
-
' Rebuild better-sqlite3, or set OS_DATABASE_URL / OS_DATABASE_DRIVER for SQL fidelity.'));
|
|
720
|
-
}
|
|
721
|
-
}
|
|
678
|
+
// Default in dev (no DB configured): prefer native SQLite for
|
|
679
|
+
// production-like SQL at native speed, with a graceful step-down to
|
|
680
|
+
// wasm SQLite (real SQL + on-disk persistence) then in-memory when the
|
|
681
|
+
// native better-sqlite3 binary is unavailable — not built, ABI mismatch
|
|
682
|
+
// after a Node upgrade (e.g. NODE_MODULE_VERSION change), or a blocked
|
|
683
|
+
// prebuild download. Shared with the explicit-file branch and the
|
|
684
|
+
// datasource factory via resolveSqliteDriver (#2229), which probes by
|
|
685
|
+
// actually opening a connection + running SELECT 1 (better-sqlite3 loads
|
|
686
|
+
// its native addon lazily at first query, not at construction).
|
|
687
|
+
const { resolveSqliteDriver } = await import('@objectstack/service-datasource');
|
|
688
|
+
const resolved = await resolveSqliteDriver({
|
|
689
|
+
filename: ':memory:',
|
|
690
|
+
dev: true,
|
|
691
|
+
autoMigrate: 'safe', // #2186 dev loosen-only self-heal
|
|
692
|
+
warn: (m) => console.warn(chalk.yellow(m)),
|
|
693
|
+
});
|
|
694
|
+
await kernel.use(new DriverPlugin(resolved.driver));
|
|
695
|
+
trackPlugin(resolved.engine === 'memory' ? 'MemoryDriver' : resolved.engine === 'sqlite-wasm' ? 'SqliteWasmDriver' : 'SqlDriver');
|
|
696
|
+
resolvedDriverLabel = resolved.label;
|
|
697
|
+
resolvedDatabaseUrl = resolved.engine === 'memory' ? '(in-memory)' : ':memory:';
|
|
722
698
|
}
|
|
723
699
|
}
|
|
724
700
|
catch (e) {
|