@elench/testkit 0.1.114 → 0.1.116
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/README.md +33 -8
- package/lib/cli/args.mjs +3 -3
- package/lib/cli/assistant/app.mjs +4 -2
- package/lib/cli/assistant/session.mjs +5 -1
- package/lib/cli/assistant/state.mjs +1 -2
- package/lib/cli/command-flags.mjs +4 -0
- package/lib/cli/commands/db/schema/refresh.mjs +21 -0
- package/lib/cli/commands/db/schema/verify.mjs +27 -0
- package/lib/cli/components/blocks/run-tree.mjs +7 -2
- package/lib/cli/components/hooks/use-element-layout.mjs +63 -0
- package/lib/cli/components/hooks/use-spinner-frame.mjs +26 -0
- package/lib/cli/entrypoint.mjs +1 -0
- package/lib/cli/operations/db/schema/refresh/operation.mjs +56 -0
- package/lib/cli/operations/db/{snapshot/capture → schema/verify}/operation.mjs +6 -27
- package/lib/cli/operations/run/operation.mjs +1 -0
- package/lib/cli/renderers/db-schema/text.mjs +7 -0
- package/lib/config/database.mjs +64 -0
- package/lib/config-api/index.d.ts +16 -1
- package/lib/config-api/index.mjs +31 -16
- package/lib/database/fingerprint.mjs +2 -0
- package/lib/database/index.mjs +142 -104
- package/lib/database/schema-source.mjs +295 -0
- package/lib/database/template-steps.mjs +158 -38
- package/lib/runner/orchestrator.mjs +4 -3
- package/lib/runner/template-steps.mjs +12 -1
- package/lib/runner/template.mjs +16 -1
- package/node_modules/@alcalzone/ansi-tokenize/README.md +0 -5
- package/node_modules/@alcalzone/ansi-tokenize/build/ansiCodes.d.ts +8 -0
- package/node_modules/@alcalzone/ansi-tokenize/build/ansiCodes.js +10 -8
- package/node_modules/@alcalzone/ansi-tokenize/build/ansiCodes.js.map +1 -1
- package/node_modules/@alcalzone/ansi-tokenize/build/tokenize.d.ts +1 -5
- package/node_modules/@alcalzone/ansi-tokenize/build/tokenize.js +9 -45
- package/node_modules/@alcalzone/ansi-tokenize/build/tokenize.js.map +1 -1
- package/node_modules/@alcalzone/ansi-tokenize/package.json +1 -1
- package/node_modules/@elench/next-analysis/package.json +1 -1
- package/node_modules/@elench/testkit-bridge/package.json +2 -2
- package/node_modules/@elench/testkit-protocol/package.json +1 -1
- package/node_modules/@elench/ts-analysis/package.json +1 -1
- package/node_modules/cli-boxes/index.d.ts +95 -90
- package/node_modules/cli-boxes/index.js +5 -2
- package/node_modules/cli-boxes/package.json +6 -13
- package/node_modules/cli-boxes/readme.md +15 -3
- package/node_modules/cli-truncate/index.d.ts +1 -1
- package/node_modules/cli-truncate/package.json +4 -4
- package/node_modules/cli-truncate/readme.md +1 -0
- package/node_modules/es-toolkit/CHANGELOG.md +801 -0
- package/node_modules/es-toolkit/src/compat/_internal/Equals.d.ts +1 -0
- package/node_modules/es-toolkit/src/compat/_internal/IsWritable.d.ts +3 -0
- package/node_modules/es-toolkit/src/compat/_internal/MutableList.d.ts +4 -0
- package/node_modules/es-toolkit/src/compat/_internal/RejectReadonly.d.ts +4 -0
- package/node_modules/esprima/ChangeLog +235 -0
- package/node_modules/ink/build/apply-styles.js +175 -0
- package/node_modules/ink/build/build-layout.js +77 -0
- package/node_modules/ink/build/calculate-wrapped-text.js +53 -0
- package/node_modules/ink/build/components/App.d.ts +1 -4
- package/node_modules/ink/build/components/App.js +22 -142
- package/node_modules/ink/build/components/App.js.map +1 -1
- package/node_modules/ink/build/components/AppContext.d.ts +3 -23
- package/node_modules/ink/build/components/AppContext.js +4 -7
- package/node_modules/ink/build/components/AppContext.js.map +1 -1
- package/node_modules/ink/build/components/Box.d.ts +3 -16
- package/node_modules/ink/build/components/Color.js +62 -0
- package/node_modules/ink/build/components/Cursor.d.ts +83 -0
- package/node_modules/ink/build/components/Cursor.js +53 -0
- package/node_modules/ink/build/components/Cursor.js.map +1 -0
- package/node_modules/ink/build/components/ErrorBoundary.d.ts +2 -2
- package/node_modules/ink/build/components/ErrorOverview.js +6 -6
- package/node_modules/ink/build/components/ErrorOverview.js.map +1 -1
- package/node_modules/ink/build/components/Static.js.map +1 -1
- package/node_modules/ink/build/components/StdinContext.d.ts +1 -7
- package/node_modules/ink/build/components/StdinContext.js +0 -1
- package/node_modules/ink/build/components/StdinContext.js.map +1 -1
- package/node_modules/ink/build/components/Text.d.ts +1 -1
- package/node_modules/ink/build/components/Text.js +1 -1
- package/node_modules/ink/build/components/Text.js.map +1 -1
- package/node_modules/ink/build/components/Transform.d.ts +1 -1
- package/node_modules/ink/build/devtools-window-polyfill.js +4 -7
- package/node_modules/ink/build/devtools-window-polyfill.js.map +1 -1
- package/node_modules/ink/build/devtools.js +6 -31
- package/node_modules/ink/build/devtools.js.map +1 -1
- package/node_modules/ink/build/dom.d.ts +1 -5
- package/node_modules/ink/build/dom.js +1 -20
- package/node_modules/ink/build/dom.js.map +1 -1
- package/node_modules/ink/build/experimental/apply-style.js +140 -0
- package/node_modules/ink/build/experimental/dom.js +123 -0
- package/node_modules/ink/build/experimental/output.js +91 -0
- package/node_modules/ink/build/experimental/reconciler.js +141 -0
- package/node_modules/ink/build/experimental/renderer.js +81 -0
- package/node_modules/ink/build/hooks/use-app.d.ts +1 -1
- package/node_modules/ink/build/hooks/use-app.js +1 -1
- package/node_modules/ink/build/hooks/use-cursor.d.ts +1 -1
- package/node_modules/ink/build/hooks/use-cursor.js +1 -1
- package/node_modules/ink/build/hooks/use-focus-manager.d.ts +2 -17
- package/node_modules/ink/build/hooks/use-focus-manager.js +1 -2
- package/node_modules/ink/build/hooks/use-focus-manager.js.map +1 -1
- package/node_modules/ink/build/hooks/use-focus.d.ts +1 -2
- package/node_modules/ink/build/hooks/use-focus.js +4 -5
- package/node_modules/ink/build/hooks/use-focus.js.map +1 -1
- package/node_modules/ink/build/hooks/use-input.d.ts +1 -2
- package/node_modules/ink/build/hooks/use-input.js +80 -82
- package/node_modules/ink/build/hooks/use-input.js.map +1 -1
- package/node_modules/ink/build/hooks/use-is-screen-reader-enabled.d.ts +1 -2
- package/node_modules/ink/build/hooks/use-is-screen-reader-enabled.js +1 -2
- package/node_modules/ink/build/hooks/use-is-screen-reader-enabled.js.map +1 -1
- package/node_modules/ink/build/hooks/use-stderr.d.ts +1 -1
- package/node_modules/ink/build/hooks/use-stderr.js +1 -1
- package/node_modules/ink/build/hooks/use-stdin.d.ts +2 -4
- package/node_modules/ink/build/hooks/use-stdin.js +1 -2
- package/node_modules/ink/build/hooks/use-stdin.js.map +1 -1
- package/node_modules/ink/build/hooks/use-stdout.d.ts +1 -1
- package/node_modules/ink/build/hooks/use-stdout.js +1 -1
- package/node_modules/ink/build/hooks/useInput.js +38 -0
- package/node_modules/ink/build/index.d.ts +1 -8
- package/node_modules/ink/build/index.js +0 -4
- package/node_modules/ink/build/index.js.map +1 -1
- package/node_modules/ink/build/ink.d.ts +3 -48
- package/node_modules/ink/build/ink.js +155 -325
- package/node_modules/ink/build/ink.js.map +1 -1
- package/node_modules/ink/build/input-parser.d.ts +1 -4
- package/node_modules/ink/build/input-parser.js +30 -70
- package/node_modules/ink/build/input-parser.js.map +1 -1
- package/node_modules/ink/build/instance.js +205 -0
- package/node_modules/ink/build/layout.d.ts +7 -0
- package/node_modules/ink/build/layout.js +33 -0
- package/node_modules/ink/build/layout.js.map +1 -0
- package/node_modules/ink/build/log-update.d.ts +0 -1
- package/node_modules/ink/build/log-update.js +1 -13
- package/node_modules/ink/build/log-update.js.map +1 -1
- package/node_modules/ink/build/measure-element.d.ts +0 -4
- package/node_modules/ink/build/measure-element.js +0 -4
- package/node_modules/ink/build/measure-element.js.map +1 -1
- package/node_modules/ink/build/options.d.ts +52 -0
- package/node_modules/ink/build/options.js +2 -0
- package/node_modules/ink/build/options.js.map +1 -0
- package/node_modules/ink/build/output.js +0 -25
- package/node_modules/ink/build/output.js.map +1 -1
- package/node_modules/ink/build/parse-keypress.d.ts +3 -1
- package/node_modules/ink/build/parse-keypress.js +17 -19
- package/node_modules/ink/build/parse-keypress.js.map +1 -1
- package/node_modules/ink/build/reconciler.js +27 -46
- package/node_modules/ink/build/reconciler.js.map +1 -1
- package/node_modules/ink/build/render-border.js +18 -29
- package/node_modules/ink/build/render-border.js.map +1 -1
- package/node_modules/ink/build/render-to-string.js +1 -2
- package/node_modules/ink/build/render-to-string.js.map +1 -1
- package/node_modules/ink/build/render.d.ts +2 -57
- package/node_modules/ink/build/render.js +11 -18
- package/node_modules/ink/build/render.js.map +1 -1
- package/node_modules/ink/build/screen-reader-update.d.ts +13 -0
- package/node_modules/ink/build/screen-reader-update.js +38 -0
- package/node_modules/ink/build/screen-reader-update.js.map +1 -0
- package/node_modules/ink/build/styles.d.ts +16 -78
- package/node_modules/ink/build/styles.js +31 -102
- package/node_modules/ink/build/styles.js.map +1 -1
- package/node_modules/ink/build/utils.d.ts +2 -9
- package/node_modules/ink/build/utils.js +3 -18
- package/node_modules/ink/build/utils.js.map +1 -1
- package/node_modules/ink/build/wrap-text.js +0 -7
- package/node_modules/ink/build/wrap-text.js.map +1 -1
- package/node_modules/ink/build/write-synchronized.d.ts +1 -1
- package/node_modules/ink/build/write-synchronized.js +2 -4
- package/node_modules/ink/build/write-synchronized.js.map +1 -1
- package/node_modules/ink/node_modules/emoji-regex/LICENSE-MIT.txt +20 -0
- package/node_modules/ink/node_modules/emoji-regex/README.md +107 -0
- package/node_modules/ink/node_modules/emoji-regex/index.d.ts +3 -0
- package/node_modules/ink/node_modules/emoji-regex/index.js +4 -0
- package/node_modules/ink/node_modules/emoji-regex/index.mjs +4 -0
- package/node_modules/ink/node_modules/emoji-regex/package.json +45 -0
- package/node_modules/{wrap-ansi → ink/node_modules/wrap-ansi}/index.d.ts +1 -1
- package/node_modules/ink/node_modules/wrap-ansi/index.js +222 -0
- package/node_modules/ink/node_modules/wrap-ansi/node_modules/string-width/index.d.ts +39 -0
- package/node_modules/ink/node_modules/wrap-ansi/node_modules/string-width/index.js +82 -0
- package/node_modules/ink/node_modules/wrap-ansi/node_modules/string-width/license +9 -0
- package/node_modules/ink/node_modules/wrap-ansi/node_modules/string-width/package.json +64 -0
- package/node_modules/ink/node_modules/wrap-ansi/node_modules/string-width/readme.md +66 -0
- package/node_modules/{wrap-ansi → ink/node_modules/wrap-ansi}/package.json +11 -11
- package/node_modules/{wrap-ansi → ink/node_modules/wrap-ansi}/readme.md +0 -2
- package/node_modules/ink/package.json +98 -34
- package/node_modules/ink/readme.md +48 -554
- package/node_modules/slice-ansi/index.d.ts +1 -1
- package/node_modules/slice-ansi/index.js +89 -146
- package/node_modules/slice-ansi/package.json +5 -5
- package/node_modules/slice-ansi/readme.md +0 -1
- package/node_modules/slice-ansi/tokenize-ansi.js +1 -1
- package/package.json +14 -10
- package/packages/testkit-bridge/node_modules/@elench/testkit-protocol/dist/index.d.ts +188 -0
- package/packages/testkit-bridge/node_modules/@elench/testkit-protocol/dist/index.d.ts.map +1 -0
- package/packages/testkit-bridge/node_modules/@elench/testkit-protocol/dist/index.js +293 -0
- package/packages/testkit-bridge/node_modules/@elench/testkit-protocol/dist/index.js.map +1 -0
- package/packages/testkit-bridge/node_modules/@elench/testkit-protocol/package.json +25 -0
- package/lib/cli/commands/db/snapshot/capture.mjs +0 -26
- package/lib/cli/renderers/db-snapshot-capture/text.mjs +0 -3
- package/node_modules/@alcalzone/ansi-tokenize/build/consts.d.ts +0 -17
- package/node_modules/@alcalzone/ansi-tokenize/build/consts.js +0 -28
- package/node_modules/@alcalzone/ansi-tokenize/build/consts.js.map +0 -1
- package/node_modules/ink/build/components/AnimationContext.d.ts +0 -9
- package/node_modules/ink/build/components/AnimationContext.js +0 -13
- package/node_modules/ink/build/components/AnimationContext.js.map +0 -1
- package/node_modules/ink/build/hooks/use-animation.d.ts +0 -49
- package/node_modules/ink/build/hooks/use-animation.js +0 -87
- package/node_modules/ink/build/hooks/use-animation.js.map +0 -1
- package/node_modules/ink/build/hooks/use-box-metrics.d.ts +0 -59
- package/node_modules/ink/build/hooks/use-box-metrics.js +0 -88
- package/node_modules/ink/build/hooks/use-box-metrics.js.map +0 -1
- package/node_modules/ink/build/hooks/use-paste.d.ts +0 -35
- package/node_modules/ink/build/hooks/use-paste.js +0 -62
- package/node_modules/ink/build/hooks/use-paste.js.map +0 -1
- package/node_modules/ink/build/hooks/use-window-size.d.ts +0 -18
- package/node_modules/ink/build/hooks/use-window-size.js +0 -22
- package/node_modules/ink/build/hooks/use-window-size.js.map +0 -1
- package/node_modules/wrap-ansi/index.js +0 -468
- /package/node_modules/{wrap-ansi → ink/node_modules/wrap-ansi}/license +0 -0
package/lib/config-api/index.mjs
CHANGED
|
@@ -34,19 +34,36 @@ function postgresDatabase(options = {}) {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
function buildDatabaseTemplateConfig(options = {}) {
|
|
37
|
+
for (const legacyKey of ["schema"]) {
|
|
38
|
+
if (Object.prototype.hasOwnProperty.call(options, legacyKey)) {
|
|
39
|
+
throw new Error(
|
|
40
|
+
`database.postgres({ template }) no longer accepts "${legacyKey}". Use database.postgres({ sourceSchema: database.schema.fromEnv(...) }) instead.`
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
37
44
|
const migrate = normalizeTemplateStepList(options.migrate);
|
|
38
45
|
const seed = normalizeTemplateStepList(options.seed);
|
|
39
46
|
const verify = normalizeTemplateStepList(options.verify);
|
|
40
|
-
const schema = normalizeSchemaStep(options.schema);
|
|
41
47
|
|
|
42
48
|
return {
|
|
43
49
|
inputs: Array.isArray(options.inputs) ? [...options.inputs] : undefined,
|
|
44
|
-
migrate
|
|
50
|
+
migrate,
|
|
45
51
|
seed,
|
|
46
52
|
verify,
|
|
47
53
|
};
|
|
48
54
|
}
|
|
49
55
|
|
|
56
|
+
function sourceSchemaFromEnv(envName, options = {}) {
|
|
57
|
+
if (typeof envName !== "string" || envName.trim().length === 0) {
|
|
58
|
+
throw new Error("database.schema.fromEnv(...) requires a non-empty env var name");
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
kind: "env",
|
|
62
|
+
env: envName.trim(),
|
|
63
|
+
...options,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
50
67
|
function postgresFixture(options = {}) {
|
|
51
68
|
const { discovery, envFiles, template, ...databaseOptions } = options;
|
|
52
69
|
for (const legacyKey of ["inputs", "schema", "migrate", "seed", "verify"]) {
|
|
@@ -241,8 +258,11 @@ export const app = {
|
|
|
241
258
|
};
|
|
242
259
|
|
|
243
260
|
export const database = {
|
|
261
|
+
schema: {
|
|
262
|
+
fromEnv: sourceSchemaFromEnv,
|
|
263
|
+
},
|
|
244
264
|
postgres(options = {}) {
|
|
245
|
-
const { template, ...databaseOptions } = options;
|
|
265
|
+
const { template, sourceSchema, ...databaseOptions } = options;
|
|
246
266
|
for (const legacyKey of ["inputs", "schema", "migrate", "seed", "verify"]) {
|
|
247
267
|
if (Object.prototype.hasOwnProperty.call(options, legacyKey)) {
|
|
248
268
|
throw new Error(
|
|
@@ -250,13 +270,19 @@ export const database = {
|
|
|
250
270
|
);
|
|
251
271
|
}
|
|
252
272
|
}
|
|
273
|
+
const normalizedDatabaseOptions = sourceSchema === undefined
|
|
274
|
+
? databaseOptions
|
|
275
|
+
: {
|
|
276
|
+
...databaseOptions,
|
|
277
|
+
sourceSchema,
|
|
278
|
+
};
|
|
253
279
|
return postgresDatabase(
|
|
254
280
|
template
|
|
255
281
|
? {
|
|
256
|
-
...
|
|
282
|
+
...normalizedDatabaseOptions,
|
|
257
283
|
template: buildDatabaseTemplateConfig(template),
|
|
258
284
|
}
|
|
259
|
-
:
|
|
285
|
+
: normalizedDatabaseOptions
|
|
260
286
|
);
|
|
261
287
|
},
|
|
262
288
|
fixture: postgresFixture,
|
|
@@ -365,17 +391,6 @@ function normalizeTemplateStepList(value) {
|
|
|
365
391
|
return Array.isArray(value) ? [...value] : [value];
|
|
366
392
|
}
|
|
367
393
|
|
|
368
|
-
function normalizeSchemaStep(value) {
|
|
369
|
-
if (value == null) return null;
|
|
370
|
-
if (typeof value === "string") {
|
|
371
|
-
return {
|
|
372
|
-
kind: "sql-file",
|
|
373
|
-
path: value,
|
|
374
|
-
};
|
|
375
|
-
}
|
|
376
|
-
return value;
|
|
377
|
-
}
|
|
378
|
-
|
|
379
394
|
function compiledEntryFromSource(entry, outDir) {
|
|
380
395
|
const normalized = String(entry || "src/index.ts").replaceAll("\\", "/");
|
|
381
396
|
const compiled = normalized.replace(/\.[cm]?[jt]sx?$/i, ".js");
|
|
@@ -3,6 +3,7 @@ import fs from "fs";
|
|
|
3
3
|
import path from "path";
|
|
4
4
|
import { resolveServiceCwd } from "../config/paths.mjs";
|
|
5
5
|
import { collectTemplateInputs } from "./template-steps.mjs";
|
|
6
|
+
import { appendSourceSchemaCacheToHash } from "./schema-source.mjs";
|
|
6
7
|
|
|
7
8
|
const LOCAL_IMAGE = "pgvector/pgvector:pg16";
|
|
8
9
|
const LOCAL_USER = "testkit";
|
|
@@ -24,6 +25,7 @@ export async function computeTemplateFingerprint(config) {
|
|
|
24
25
|
for (const input of collectTemplateInputs(config.productDir, db.template || {})) {
|
|
25
26
|
appendResolvedInputToHash(hash, config.productDir, input);
|
|
26
27
|
}
|
|
28
|
+
appendSourceSchemaCacheToHash(hash, config);
|
|
27
29
|
|
|
28
30
|
return hash.digest("hex");
|
|
29
31
|
}
|
package/lib/database/index.mjs
CHANGED
|
@@ -25,7 +25,17 @@ import {
|
|
|
25
25
|
readStateValue as readStateValueModel,
|
|
26
26
|
visitDirs as visitDirsModel,
|
|
27
27
|
} from "./state.mjs";
|
|
28
|
-
import {
|
|
28
|
+
import {
|
|
29
|
+
runTemplateStage,
|
|
30
|
+
runTemplateStep,
|
|
31
|
+
} from "./template-steps.mjs";
|
|
32
|
+
import {
|
|
33
|
+
applySourceSchemaCache,
|
|
34
|
+
createSourceSchemaMismatchError,
|
|
35
|
+
forceRefreshSourceSchemaCache,
|
|
36
|
+
prepareSourceSchemaCache,
|
|
37
|
+
verifyLocalSchemaMatchesSource,
|
|
38
|
+
} from "./schema-source.mjs";
|
|
29
39
|
import { collectStateDirLines } from "../runner/state-io.mjs";
|
|
30
40
|
|
|
31
41
|
const LOCAL_IMAGE = "pgvector/pgvector:pg16";
|
|
@@ -50,60 +60,6 @@ export async function prepareDatabaseRuntime(config, options = {}) {
|
|
|
50
60
|
throw new Error(`Unsupported database provider "${db.provider}"`);
|
|
51
61
|
}
|
|
52
62
|
|
|
53
|
-
export async function captureDatabaseTemplateSnapshot(config, outputPath, options = {}) {
|
|
54
|
-
if (!config.testkit.database || config.testkit.database.provider !== "local") {
|
|
55
|
-
throw new Error(`Service "${config.name}" does not use a local testkit database`);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
await prepareDatabaseRuntime(config, options);
|
|
59
|
-
const cacheDir = getLocalServiceCacheDir(config.productDir, config.name);
|
|
60
|
-
const templateDbName = readStateValue(path.join(cacheDir, "template_database_name"));
|
|
61
|
-
if (!templateDbName) {
|
|
62
|
-
throw new Error(`Missing template database for service "${config.name}"`);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const infra = await loadExistingLocalContainer(config.productDir);
|
|
66
|
-
if (!infra) {
|
|
67
|
-
throw new Error(`Missing local database container for service "${config.name}"`);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const snapshotOperation = options.setupRegistry?.start({
|
|
71
|
-
config,
|
|
72
|
-
stage: "template:snapshot",
|
|
73
|
-
kind: "database-snapshot",
|
|
74
|
-
summary: `snapshot: ${path.relative(config.productDir, outputPath)}`,
|
|
75
|
-
});
|
|
76
|
-
try {
|
|
77
|
-
const output = await captureTemplateSnapshot(
|
|
78
|
-
config,
|
|
79
|
-
outputPath,
|
|
80
|
-
buildDatabaseUrl(infra, templateDbName),
|
|
81
|
-
{
|
|
82
|
-
reporter: options.reporter || null,
|
|
83
|
-
logRecord: snapshotOperation?._logRecord || null,
|
|
84
|
-
}
|
|
85
|
-
);
|
|
86
|
-
const finished = snapshotOperation
|
|
87
|
-
? options.setupRegistry.finish(snapshotOperation, {
|
|
88
|
-
status: "passed",
|
|
89
|
-
summary: `snapshot: ${path.relative(config.productDir, outputPath)}`,
|
|
90
|
-
})
|
|
91
|
-
: null;
|
|
92
|
-
if (finished) options.reporter?.setupOperationFinished?.(finished);
|
|
93
|
-
return output;
|
|
94
|
-
} catch (error) {
|
|
95
|
-
const finished = snapshotOperation
|
|
96
|
-
? options.setupRegistry.finish(snapshotOperation, {
|
|
97
|
-
status: "failed",
|
|
98
|
-
summary: `snapshot: ${path.relative(config.productDir, outputPath)}`,
|
|
99
|
-
error: error?.message || error,
|
|
100
|
-
})
|
|
101
|
-
: null;
|
|
102
|
-
if (finished) options.reporter?.setupOperationFinished?.(finished);
|
|
103
|
-
throw error;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
63
|
export async function destroyRuntimeDatabase({ productDir, stateDir }) {
|
|
108
64
|
const backend = readStateValue(path.join(stateDir, "database_backend"));
|
|
109
65
|
if (backend === "local") {
|
|
@@ -184,13 +140,24 @@ async function prepareLocalDatabase(config, options = {}) {
|
|
|
184
140
|
fs.mkdirSync(lockDir, { recursive: true });
|
|
185
141
|
fs.mkdirSync(cacheDir, { recursive: true });
|
|
186
142
|
|
|
187
|
-
|
|
143
|
+
let templateFingerprint = null;
|
|
188
144
|
const infra = await withLock(path.join(lockDir, "container.lock"), () =>
|
|
189
145
|
ensureLocalContainer(productDir, db)
|
|
190
146
|
);
|
|
191
147
|
|
|
192
148
|
await withLock(path.join(lockDir, `template-${serviceName}.lock`), async () => {
|
|
193
|
-
await
|
|
149
|
+
const sourceSchemaState = await prepareSourceSchemaCache(config, options);
|
|
150
|
+
templateFingerprint = await computeTemplateFingerprint(config);
|
|
151
|
+
templateFingerprint = await ensureTemplateDatabase(
|
|
152
|
+
config,
|
|
153
|
+
infra,
|
|
154
|
+
cacheDir,
|
|
155
|
+
templateFingerprint,
|
|
156
|
+
{
|
|
157
|
+
...options,
|
|
158
|
+
sourceSchemaState,
|
|
159
|
+
}
|
|
160
|
+
);
|
|
194
161
|
});
|
|
195
162
|
|
|
196
163
|
await withLock(path.join(lockDir, `runtime-${serviceName}-${hashString(bindingKey, 10)}.lock`), async () => {
|
|
@@ -200,34 +167,58 @@ async function prepareLocalDatabase(config, options = {}) {
|
|
|
200
167
|
|
|
201
168
|
async function ensureTemplateDatabase(config, infra, cacheDir, templateFingerprint, options = {}) {
|
|
202
169
|
const serviceName = config.name;
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
existingFingerprint
|
|
209
|
-
existingDbName
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
170
|
+
let activeFingerprint = templateFingerprint;
|
|
171
|
+
let sourceSchemaState = options.sourceSchemaState || null;
|
|
172
|
+
let refreshedSourceAfterMismatch = false;
|
|
173
|
+
|
|
174
|
+
while (true) {
|
|
175
|
+
const existingFingerprint = readStateValue(path.join(cacheDir, "template_fingerprint"));
|
|
176
|
+
const existingDbName = readStateValue(path.join(cacheDir, "template_database_name"));
|
|
177
|
+
const desiredDbName = buildTemplateDatabaseName(serviceName, activeFingerprint);
|
|
178
|
+
|
|
179
|
+
if (
|
|
180
|
+
existingFingerprint === activeFingerprint &&
|
|
181
|
+
existingDbName &&
|
|
182
|
+
(await databaseExists(infra, existingDbName))
|
|
183
|
+
) {
|
|
184
|
+
options.setupRegistry?.recordCached({
|
|
185
|
+
config,
|
|
186
|
+
stage: "template",
|
|
187
|
+
kind: "database-template",
|
|
188
|
+
summary: "template cache hit",
|
|
189
|
+
});
|
|
190
|
+
writeLocalCacheState(cacheDir, infra, existingDbName, activeFingerprint);
|
|
191
|
+
return activeFingerprint;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (existingDbName && existingDbName !== desiredDbName) {
|
|
195
|
+
await dropDatabaseIfExists(infra, existingDbName);
|
|
196
|
+
}
|
|
197
|
+
if (await databaseExists(infra, desiredDbName)) {
|
|
198
|
+
await dropDatabaseIfExists(infra, desiredDbName);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const templateUrl = buildDatabaseUrl(infra, desiredDbName);
|
|
202
|
+
await createEmptyDatabase(infra, desiredDbName);
|
|
203
|
+
const buildResult = await rebuildTemplateDatabase(config, infra, desiredDbName, templateUrl, {
|
|
204
|
+
...options,
|
|
205
|
+
sourceSchemaState,
|
|
206
|
+
refreshedSourceAfterMismatch,
|
|
217
207
|
});
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
208
|
+
if (buildResult.refreshSourceSchema) {
|
|
209
|
+
await dropDatabaseIfExists(infra, desiredDbName);
|
|
210
|
+
sourceSchemaState = await forceRefreshSourceSchemaCache(config, sourceSchemaState, options);
|
|
211
|
+
refreshedSourceAfterMismatch = true;
|
|
212
|
+
activeFingerprint = await computeTemplateFingerprint(config);
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
221
215
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
}
|
|
225
|
-
if (await databaseExists(infra, desiredDbName)) {
|
|
226
|
-
await dropDatabaseIfExists(infra, desiredDbName);
|
|
216
|
+
writeLocalCacheState(cacheDir, infra, desiredDbName, activeFingerprint);
|
|
217
|
+
return activeFingerprint;
|
|
227
218
|
}
|
|
219
|
+
}
|
|
228
220
|
|
|
229
|
-
|
|
230
|
-
await createEmptyDatabase(infra, desiredDbName);
|
|
221
|
+
async function rebuildTemplateDatabase(config, infra, templateDbName, templateUrl, options = {}) {
|
|
231
222
|
const templateOperation = options.setupRegistry?.start({
|
|
232
223
|
config,
|
|
233
224
|
stage: "template",
|
|
@@ -236,42 +227,89 @@ async function ensureTemplateDatabase(config, infra, cacheDir, templateFingerpri
|
|
|
236
227
|
recordLog: false,
|
|
237
228
|
});
|
|
238
229
|
try {
|
|
239
|
-
await
|
|
240
|
-
reporter: options.reporter || null,
|
|
241
|
-
setupRegistry: options.setupRegistry || null,
|
|
242
|
-
parentOperation: templateOperation,
|
|
243
|
-
});
|
|
244
|
-
await runTemplateStage(config, "seed", templateUrl, {
|
|
230
|
+
await applySourceSchemaCache(config, templateUrl, options.sourceSchemaState, {
|
|
245
231
|
reporter: options.reporter || null,
|
|
246
232
|
setupRegistry: options.setupRegistry || null,
|
|
247
233
|
parentOperation: templateOperation,
|
|
248
234
|
});
|
|
235
|
+
|
|
236
|
+
const verifySchema = async () => {
|
|
237
|
+
const verification = await verifyLocalSchemaMatchesSource(
|
|
238
|
+
config,
|
|
239
|
+
templateUrl,
|
|
240
|
+
options.sourceSchemaState,
|
|
241
|
+
{
|
|
242
|
+
reporter: options.reporter || null,
|
|
243
|
+
setupRegistry: options.setupRegistry || null,
|
|
244
|
+
skipSchemaSourceVerify: options.skipSchemaSourceVerify,
|
|
245
|
+
}
|
|
246
|
+
);
|
|
247
|
+
if (verification.status !== "mismatch") return null;
|
|
248
|
+
if (options.refreshedSourceAfterMismatch) {
|
|
249
|
+
throw createSourceSchemaMismatchError(config, options.sourceSchemaState, verification);
|
|
250
|
+
}
|
|
251
|
+
return verification;
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
const runStageWithDriftChecks = async (stageName) => {
|
|
255
|
+
const steps = config.testkit.database?.template?.[stageName] || [];
|
|
256
|
+
for (const [index, step] of steps.entries()) {
|
|
257
|
+
await runTemplateStep(config, stageName, step, index, templateUrl, {
|
|
258
|
+
reporter: options.reporter || null,
|
|
259
|
+
setupRegistry: options.setupRegistry || null,
|
|
260
|
+
parentOperation: templateOperation,
|
|
261
|
+
});
|
|
262
|
+
if (await verifySchema()) return true;
|
|
263
|
+
}
|
|
264
|
+
return false;
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
if (await runStageWithDriftChecks("migrate")) {
|
|
268
|
+
finishTemplateOperation(templateOperation, options, {
|
|
269
|
+
status: "passed",
|
|
270
|
+
summary: "source schema refresh requested",
|
|
271
|
+
});
|
|
272
|
+
return { refreshSourceSchema: true };
|
|
273
|
+
}
|
|
274
|
+
if (await runStageWithDriftChecks("seed")) {
|
|
275
|
+
finishTemplateOperation(templateOperation, options, {
|
|
276
|
+
status: "passed",
|
|
277
|
+
summary: "source schema refresh requested",
|
|
278
|
+
});
|
|
279
|
+
return { refreshSourceSchema: true };
|
|
280
|
+
}
|
|
281
|
+
if (await verifySchema()) {
|
|
282
|
+
finishTemplateOperation(templateOperation, options, {
|
|
283
|
+
status: "passed",
|
|
284
|
+
summary: "source schema refresh requested",
|
|
285
|
+
});
|
|
286
|
+
return { refreshSourceSchema: true };
|
|
287
|
+
}
|
|
288
|
+
|
|
249
289
|
await runTemplateStage(config, "verify", templateUrl, {
|
|
250
290
|
reporter: options.reporter || null,
|
|
251
291
|
setupRegistry: options.setupRegistry || null,
|
|
252
292
|
parentOperation: templateOperation,
|
|
253
293
|
});
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
: null;
|
|
260
|
-
if (finished) options.reporter?.setupOperationFinished?.(finished);
|
|
294
|
+
finishTemplateOperation(templateOperation, options, {
|
|
295
|
+
status: "passed",
|
|
296
|
+
summary: "template rebuild",
|
|
297
|
+
});
|
|
298
|
+
return { refreshSourceSchema: false };
|
|
261
299
|
} catch (error) {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
: null;
|
|
269
|
-
if (finished) options.reporter?.setupOperationFinished?.(finished);
|
|
270
|
-
await dropDatabaseIfExists(infra, desiredDbName);
|
|
300
|
+
finishTemplateOperation(templateOperation, options, {
|
|
301
|
+
status: "failed",
|
|
302
|
+
summary: "template rebuild",
|
|
303
|
+
error: error?.message || error,
|
|
304
|
+
});
|
|
305
|
+
await dropDatabaseIfExists(infra, templateDbName);
|
|
271
306
|
throw error;
|
|
272
307
|
}
|
|
308
|
+
}
|
|
273
309
|
|
|
274
|
-
|
|
310
|
+
function finishTemplateOperation(templateOperation, options, result) {
|
|
311
|
+
const finished = templateOperation ? options.setupRegistry.finish(templateOperation, result) : null;
|
|
312
|
+
if (finished) options.reporter?.setupOperationFinished?.(finished);
|
|
275
313
|
}
|
|
276
314
|
|
|
277
315
|
async function ensureRuntimeClone(config, infra, cacheDir, templateFingerprint, bindingKey) {
|