@starci/hfs 4.0.10 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1 -1
- package/emit/operations.mjs +1 -1
- package/emit/type-schema.mjs +2 -1
- package/lint/run.mjs +3 -2
- package/package.json +1 -1
- package/report/order.mjs +2 -0
- package/runtime/config.example.yaml +187 -0
- package/runtime/engine/by-code-unit.mjs +12 -0
- package/runtime/engine/config.mjs +221 -239
- package/runtime/engine/invalid-config.mjs +5 -5
- package/runtime/engine/model-config.mjs +90 -0
- package/runtime/engine/orca-config.mjs +3 -1
- package/runtime/engine/removed-vocabulary.mjs +28 -0
- package/runtime/engine/resources-config.mjs +52 -0
- package/runtime/engine/runtime-root.mjs +17 -0
- package/runtime/engine/secrets.mjs +35 -31
- package/runtime/engine/sonar-config.mjs +21 -0
- package/runtime/engine/temp-root.mjs +32 -0
- package/runtime/knowledge/hfs/canon-pins.yaml +14 -14
- package/runtime/knowledge/hfs/peer-integrations.yaml +2 -3
- package/runtime/knowledge/hfs/rules.yaml +98 -65
- package/runtime/knowledge/hfs/slots.yaml +11 -14
- package/runtime/knowledge/patterns/be/api.yaml +7 -24
- package/runtime/knowledge/patterns/be/cli.yaml +3 -12
- package/runtime/knowledge/patterns/be/realtime.yaml +1 -14
- package/runtime/knowledge/patterns/be/webhooks.yaml +0 -19
- package/runtime/modules/kernel/failure-codes.yaml +1 -41
- package/runtime/modules/kernel/removed-vocabulary.yaml +168 -0
- package/runtime/modules/models/registry.yaml +25 -359
- package/runtime/modules/models/runtimes.yaml +92 -256
- package/runtime/modules/models/tiers.yaml +65 -0
- package/runtime/scripts/api/fs/claim-file.mjs +30 -0
- package/runtime/scripts/api/fs/ensure-temp-root.mjs +27 -0
- package/runtime/scripts/api/fs/forbidden-root.mjs +2 -1
- package/runtime/scripts/api/fs/make-temp-dir.mjs +11 -0
- package/runtime/scripts/api/fs/safe-remove.mjs +52 -35
- package/runtime/scripts/api/git/lib.mjs +34 -1
- package/runtime/scripts/api/process/resolve-real-tool.mjs +14 -8
- package/runtime/scripts/api/process/run-program.mjs +2 -1
- package/runtime/scripts/api/sops/lib.mjs +198 -120
- package/runtime/scripts/hfs/allows.mjs +3 -3
- package/runtime/scripts/hfs/architecture/ast-walks.mjs +63 -50
- package/runtime/scripts/hfs/architecture/automatic-gates.mjs +96 -0
- package/runtime/scripts/hfs/architecture/backend.mjs +261 -175
- package/runtime/scripts/hfs/architecture/background-unowned.mjs +69 -34
- package/runtime/scripts/hfs/architecture/client-reaches-server.mjs +55 -54
- package/runtime/scripts/hfs/architecture/clones.mjs +179 -106
- package/runtime/scripts/hfs/architecture/config-unread.mjs +5 -22
- package/runtime/scripts/hfs/architecture/config.mjs +95 -80
- package/runtime/scripts/hfs/architecture/connection-map.mjs +233 -141
- package/runtime/scripts/hfs/architecture/constructor-deps.mjs +15 -9
- package/runtime/scripts/hfs/architecture/context-coupling.mjs +76 -52
- package/runtime/scripts/hfs/architecture/context-map.mjs +215 -141
- package/runtime/scripts/hfs/architecture/context-owner.mjs +39 -30
- package/runtime/scripts/hfs/architecture/context-platform-tables.mjs +31 -17
- package/runtime/scripts/hfs/architecture/context-transaction.mjs +38 -25
- package/runtime/scripts/hfs/architecture/contract-fixture-guard.mjs +86 -40
- package/runtime/scripts/hfs/architecture/contracts-readonly.mjs +342 -0
- package/runtime/scripts/hfs/architecture/contracts.mjs +263 -455
- package/runtime/scripts/hfs/architecture/cross-app-duplicate.mjs +43 -34
- package/runtime/scripts/hfs/architecture/dead-exports.mjs +201 -154
- package/runtime/scripts/hfs/architecture/default-deny.mjs +118 -93
- package/runtime/scripts/hfs/architecture/doc-language.mjs +2 -1
- package/runtime/scripts/hfs/architecture/entrypoint.mjs +45 -30
- package/runtime/scripts/hfs/architecture/error-codes.mjs +19 -12
- package/runtime/scripts/hfs/architecture/error-masked.mjs +39 -26
- package/runtime/scripts/hfs/architecture/fe-slot-allows.mjs +2 -1
- package/runtime/scripts/hfs/architecture/feature-shape.mjs +26 -20
- package/runtime/scripts/hfs/architecture/framework-pinned.mjs +1 -1
- package/runtime/scripts/hfs/architecture/frontend-grammar.mjs +190 -0
- package/runtime/scripts/hfs/architecture/frontend-routing.mjs +186 -0
- package/runtime/scripts/hfs/architecture/frontend-world-render.mjs +311 -0
- package/runtime/scripts/hfs/architecture/frontend-world.mjs +328 -0
- package/runtime/scripts/hfs/architecture/frontend.mjs +109 -829
- package/runtime/scripts/hfs/architecture/hfs-graph.mjs +14 -2
- package/runtime/scripts/hfs/architecture/hfs.mjs +120 -288
- package/runtime/scripts/hfs/architecture/hooks-are-hooks.mjs +46 -26
- package/runtime/scripts/hfs/architecture/i18n-keys.mjs +114 -76
- package/runtime/scripts/hfs/architecture/index.mjs +158 -102
- package/runtime/scripts/hfs/architecture/injection-token-exported.mjs +33 -28
- package/runtime/scripts/hfs/architecture/machine-ast.mjs +180 -144
- package/runtime/scripts/hfs/architecture/managed-scripts.mjs +8 -2
- package/runtime/scripts/hfs/architecture/module-per-transport.mjs +150 -101
- package/runtime/scripts/hfs/architecture/next-data-contract.mjs +51 -43
- package/runtime/scripts/hfs/architecture/next-data-key.mjs +338 -0
- package/runtime/scripts/hfs/architecture/next-data.mjs +179 -427
- package/runtime/scripts/hfs/architecture/owners.mjs +27 -13
- package/runtime/scripts/hfs/architecture/package-shape.mjs +51 -23
- package/runtime/scripts/hfs/architecture/presentation.mjs +165 -0
- package/runtime/scripts/hfs/architecture/reachability.mjs +127 -61
- package/runtime/scripts/hfs/architecture/register-once.mjs +112 -63
- package/runtime/scripts/hfs/architecture/registration.mjs +187 -110
- package/runtime/scripts/hfs/architecture/required-files.mjs +100 -68
- package/runtime/scripts/hfs/architecture/route-files-thin.mjs +83 -58
- package/runtime/scripts/hfs/architecture/schema-owner.mjs +182 -108
- package/runtime/scripts/hfs/architecture/source-names-shape.mjs +318 -0
- package/runtime/scripts/hfs/architecture/source-names.mjs +83 -248
- package/runtime/scripts/hfs/architecture/sql-owner.mjs +175 -118
- package/runtime/scripts/hfs/architecture/sql-returning.mjs +39 -28
- package/runtime/scripts/hfs/architecture/sql-tokens.mjs +302 -172
- package/runtime/scripts/hfs/architecture/supabase-ast.mjs +1 -1
- package/runtime/scripts/hfs/architecture/supabase-be.mjs +55 -45
- package/runtime/scripts/hfs/architecture/supabase-results.mjs +182 -0
- package/runtime/scripts/hfs/architecture/supabase-tables.mjs +20 -12
- package/runtime/scripts/hfs/architecture/supabase.mjs +191 -263
- package/runtime/scripts/hfs/architecture/symbols.mjs +175 -99
- package/runtime/scripts/hfs/architecture/test-world-files.mjs +82 -41
- package/runtime/scripts/hfs/architecture/tiers.mjs +120 -69
- package/runtime/scripts/hfs/architecture/transport-owner.mjs +84 -57
- package/runtime/scripts/hfs/architecture/type-context.mjs +282 -0
- package/runtime/scripts/hfs/architecture/typescript.mjs +116 -276
- package/runtime/scripts/hfs/architecture/unit-spec-providers.mjs +84 -62
- package/runtime/scripts/hfs/architecture.mjs +3 -4
- package/runtime/scripts/hfs/check.mjs +61 -70
- package/runtime/scripts/hfs/coverage-scope.mjs +84 -38
- package/runtime/scripts/hfs/declaration-shape.mjs +77 -36
- package/runtime/scripts/hfs/declaration-slots.mjs +1 -1
- package/runtime/scripts/hfs/edition-slots.mjs +5 -3
- package/runtime/scripts/hfs/linear-text.mjs +30 -0
- package/runtime/scripts/hfs/manifest-shape.mjs +164 -63
- package/runtime/scripts/hfs/path-findings.mjs +58 -38
- package/runtime/scripts/hfs/pin-findings.mjs +31 -0
- package/runtime/scripts/hfs/repo-identity.mjs +5 -2
- package/runtime/scripts/hfs/rule-catalog.mjs +194 -0
- package/runtime/scripts/hfs/rule-params-shape.mjs +33 -27
- package/runtime/scripts/hfs/rules/cli.mjs +5 -1
- package/runtime/scripts/hfs/rules/contract-compat.mjs +40 -22
- package/runtime/scripts/hfs/rules/contract.mjs +35 -27
- package/runtime/scripts/hfs/rules/database-config.mjs +57 -32
- package/runtime/scripts/hfs/rules/database-migrations.mjs +52 -31
- package/runtime/scripts/hfs/rules/database-plpgsql.mjs +106 -91
- package/runtime/scripts/hfs/rules/database-sql.mjs +254 -173
- package/runtime/scripts/hfs/rules/database.mjs +36 -23
- package/runtime/scripts/hfs/rules/deps.mjs +64 -32
- package/runtime/scripts/hfs/rules/docker.mjs +113 -65
- package/runtime/scripts/hfs/rules/edition.mjs +118 -111
- package/runtime/scripts/hfs/rules/event-bus.mjs +91 -45
- package/runtime/scripts/hfs/rules/fe-contract-documents.mjs +17 -16
- package/runtime/scripts/hfs/rules/fe-no-tests.mjs +36 -16
- package/runtime/scripts/hfs/rules/frontend-tree.mjs +4 -3
- package/runtime/scripts/hfs/rules/integration-specs.mjs +75 -41
- package/runtime/scripts/hfs/rules/kinds.mjs +59 -25
- package/runtime/scripts/hfs/rules/lint-suppression.mjs +9 -3
- package/runtime/scripts/hfs/rules/monorepo.mjs +82 -45
- package/runtime/scripts/hfs/rules/peer-integrations.mjs +24 -3
- package/runtime/scripts/hfs/rules/pipeline.mjs +58 -9
- package/runtime/scripts/hfs/rules/proof-commands.mjs +14 -8
- package/runtime/scripts/hfs/rules/repo-local-checks.mjs +11 -6
- package/runtime/scripts/hfs/rules/saga.mjs +128 -61
- package/runtime/scripts/hfs/rules/secrets.mjs +4 -3
- package/runtime/scripts/hfs/rules/services.mjs +34 -16
- package/runtime/scripts/hfs/rules/stacks.mjs +21 -14
- package/runtime/scripts/hfs/rules/supabase-secrets.mjs +32 -23
- package/runtime/scripts/hfs/rules/test-topology.mjs +25 -11
- package/runtime/scripts/hfs/secret.mjs +59 -48
- package/runtime/scripts/hfs/slot-app-view.mjs +102 -0
- package/runtime/scripts/hfs/slot-classify.mjs +65 -0
- package/runtime/scripts/hfs/slot-errors.mjs +11 -0
- package/runtime/scripts/hfs/slot-imports.mjs +76 -0
- package/runtime/scripts/hfs/slot-manifest-shape.mjs +73 -0
- package/runtime/scripts/hfs/slot-match.mjs +153 -0
- package/runtime/scripts/hfs/slot-path.mjs +8 -0
- package/runtime/scripts/hfs/slot-required.mjs +76 -0
- package/runtime/scripts/hfs/slot-semantic-problems.mjs +148 -0
- package/runtime/scripts/hfs/slot-side-problems.mjs +28 -0
- package/runtime/scripts/hfs/slots.mjs +72 -693
- package/runtime/scripts/hfs/sql/pg-parse.mjs +5 -2
- package/runtime/scripts/hfs/trailing-slashes.mjs +8 -0
- package/runtime/scripts/hfs/tree.mjs +19 -14
- package/runtime/scripts/hfs/typescript-programs.mjs +6 -4
- package/runtime/scripts/hfs/view.mjs +1 -1
- package/runtime/scripts/lib/dockerfile.mjs +56 -33
- package/runtime/scripts/lib/event-contract.mjs +17 -13
- package/runtime/scripts/lib/git.mjs +2 -2
- package/runtime/scripts/lib/graphql-contract.mjs +158 -322
- package/runtime/scripts/lib/graphql-sdl.mjs +262 -0
- package/runtime/scripts/lib/i18n.mjs +3 -2
- package/runtime/scripts/lib/in-order.mjs +71 -0
- package/runtime/scripts/lib/language.mjs +3 -3
- package/runtime/scripts/lib/list.mjs +8 -1
- package/runtime/scripts/lib/path-key.mjs +38 -5
- package/runtime/scripts/lib/pid-alive.mjs +7 -0
- package/runtime/scripts/lib/regex.mjs +1 -1
- package/runtime/scripts/lib/same-text.mjs +1 -1
- package/runtime/scripts/lib/secret-patterns.mjs +6 -2
- package/runtime/scripts/lib/sleep-sync.mjs +2 -2
- package/runtime/scripts/lib/sops-envelope.mjs +95 -48
- package/runtime/scripts/lib/stack-services.mjs +1 -2
- package/runtime/scripts/lib/ts-ast.mjs +9 -0
- package/runtime/scripts/lib/walk.mjs +6 -1
- package/scaffold/add-cli-lite.mjs +2 -1
- package/scaffold/add-table.mjs +2 -2
- package/scaffold/add.mjs +3 -2
- package/scaffold/app.mjs +4 -2
- package/scaffold/edition-gate.mjs +25 -25
- package/scaffold/lite-exports.mjs +2 -1
- package/scaffold/service.mjs +5 -4
- package/sync/hygiene.mjs +15 -9
- package/sync/index.mjs +7 -3
- package/templates/app/ci-workflows/github/workflows/ci.yml +4 -4
- package/templates/app/ci-workflows/github/workflows/e2e.yml +3 -3
- package/templates/app/ci-workflows/github/workflows/images.yml +3 -3
- package/templates/app/ci-workflows/sonar-steps.yml +2 -2
- package/templates/app/ci-workflows-lite/github/workflows/ci.yml +6 -6
- package/templates/app/ci-workflows-lite/github/workflows/db-deploy.yml +3 -3
- package/templates/app/ci-workflows-lite/github/workflows/images.yml +3 -3
- package/templates/app/starciwork.gitignore +1 -1
- package/templates/be/image/api/Dockerfile +1 -1
- package/templates/be/image/cli/Dockerfile +1 -1
- package/templates/be/image/worker/Dockerfile +1 -1
- package/templates/be/patterns/cli/group.cli.spec.ts.tpl +1 -1
- package/templates/be/patterns/cli/group.cli.ts.tpl +2 -2
- package/templates/be/patterns/event-bus/platform/event-runner.service.spec.ts.tpl +13 -0
- package/templates/be/patterns/event-bus/platform/event-runner.service.ts.tpl +7 -2
- package/templates/be/patterns/event-bus/platform/event.policy.ts.tpl +4 -1
- package/templates/be/patterns/event-bus/platform/kafka-event-transport.client.ts.tpl +2 -1
- package/templates/be/patterns/outbox/platform/outbox-relay.policy.ts.tpl +24 -9
- package/templates/be/patterns/queues/platform/queue-relay.service.ts.tpl +3 -2
- package/templates/be/patterns/queues/platform/queue-worker.service.ts.tpl +8 -5
- package/templates/be/skeleton/src/features/cli/migrate/migrate.cli.spec.ts +1 -1
- package/templates/be/skeleton/src/features/cli/migrate/migrate.cli.ts +2 -2
- package/templates/be/skeleton/src/features/cli/seed/seed.cli.spec.ts +1 -1
- package/templates/be/skeleton/src/features/cli/seed/seed.cli.ts +2 -2
- package/templates/be/skeleton/src/modules/platform/database/migrate-connections.client.ts +3 -2
- package/templates/be/skeleton/src/modules/platform/database/seed-connections.client.ts +5 -4
- package/templates/be/skeleton/src/modules/platform/primitives/index.ts +1 -0
- package/templates/be/skeleton/src/modules/platform/primitives/sequence.policy.spec.ts +47 -0
- package/templates/be/skeleton/src/modules/platform/primitives/sequence.policy.ts +16 -0
- package/templates/be/skeleton-lite/apps/api/Dockerfile +1 -0
- package/templates/be/skeleton-lite/src/modules/platform/primitives/index.ts +2 -0
- package/templates/fe/image/next/Dockerfile +2 -1
- package/templates/fe/skeleton/apps/app/src/app/[locale]/page.tsx +2 -2
- package/templates/fe/skeleton/apps/landing/src/app/[locale]/page.tsx +2 -2
- package/templates/fe/skeleton/packages/__project__-i18n/src/index.ts +1 -2
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/layout.tsx +1 -2
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/page.tsx +2 -2
- package/templates/fe/skeleton-lite/apps/web/src/components/blocks/SignInForm/index.tsx +7 -1
- package/templates/fe/skeleton-lite/apps/web/src/modules/db/auth/write-sign-out.ts +1 -1
- package/templates/fe/skeleton-lite/apps/web/src/modules/db/validation/validation.mapper.ts +10 -2
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/request.ts +10 -3
- package/upgrade/index.mjs +9 -2
- package/runtime/engine/admission.mjs +0 -308
- package/runtime/engine/canonical-json.mjs +0 -10
- package/runtime/engine/db/blob.mjs +0 -315
- package/runtime/engine/db/ledger-paths.mjs +0 -83
- package/runtime/engine/db/ledger.mjs +0 -1118
- package/runtime/engine/db/machine-connection.mjs +0 -135
- package/runtime/engine/db/machine-schema.mjs +0 -84
- package/runtime/engine/db/machine.mjs +0 -1367
- package/runtime/engine/db/migrations/machine/0001-init.sql +0 -924
- package/runtime/engine/db/migrations/runtime/0001-init.sql +0 -1108
- package/runtime/engine/db/provider-reservations.mjs +0 -101
- package/runtime/engine/digest.mjs +0 -16
- package/runtime/engine/refuse.mjs +0 -11
- package/runtime/modules/ops/_labels.yaml +0 -53
- package/runtime/scripts/api/node/lib.mjs +0 -14
- package/runtime/scripts/api/node/spawn-node.mjs +0 -6
- package/runtime/scripts/api/process/lib.mjs +0 -111
- package/runtime/scripts/api/process/owned-process.mjs +0 -78
- package/runtime/scripts/api/process/stop-owned-process.mjs +0 -9
- package/runtime/scripts/connectors/lib.mjs +0 -488
- package/runtime/scripts/lib/clip.mjs +0 -19
- package/runtime/scripts/lib/display-names.mjs +0 -259
- package/runtime/scripts/lib/example-refs.mjs +0 -158
- package/runtime/scripts/lib/json-schema.mjs +0 -52
- package/runtime/scripts/lib/process-identity.mjs +0 -6
- package/runtime/scripts/lib/read-yaml.mjs +0 -18
- package/runtime/scripts/lib/redact.mjs +0 -161
- package/runtime/scripts/lib/sleep.mjs +0 -7
- package/runtime/scripts/lib/source-phrases.mjs +0 -34
- package/runtime/scripts/lib/sqlite.mjs +0 -21
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
import path from 'node:path';
|
|
2
|
-
import { createRequire } from 'node:module';
|
|
3
|
-
import { sleepSync as scaledSleepSync } from '../../scripts/lib/sleep-sync.mjs';
|
|
4
|
-
|
|
5
|
-
const require = createRequire(import.meta.url);
|
|
6
|
-
export const MACHINE_BUSY_TIMEOUT_MS = 15000;
|
|
7
|
-
export const MACHINE_BUSY_CODE = 'STARCI_MACHINE_BUSY';
|
|
8
|
-
export const MACHINE_CORRUPT_CODE = 'STARCI_MACHINE_CORRUPT';
|
|
9
|
-
const OPEN_RETRY_DELAYS_MS = Object.freeze([0, 300, 900]);
|
|
10
|
-
const BUSY_RETRY_DELAYS_MS = Object.freeze([100, 300, 900, 2000]);
|
|
11
|
-
export const CORRUPT_RETRY_DELAYS_MS = Object.freeze([25, 150, 600]);
|
|
12
|
-
export const waitForRetry = (ms) => Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
13
|
-
export const isMachineBusy = (error) => error?.code === MACHINE_BUSY_CODE;
|
|
14
|
-
export const isBusyError = (error) => error?.errcode === 5 || error?.errcode === 6 || /SQLITE_BUSY|database is (?:locked|busy)/i.test(String(error?.message ?? error));
|
|
15
|
-
export const isCorruptError = (error) => {
|
|
16
|
-
if (!error) return false;
|
|
17
|
-
if (error.code === MACHINE_CORRUPT_CODE) return true;
|
|
18
|
-
const code = Number(error.errcode);
|
|
19
|
-
if (Number.isInteger(code) && ((code & 0xff) === 11 || (code & 0xff) === 26)) return true;
|
|
20
|
-
return /database disk image is malformed|file is not a database|SQLITE_CORRUPT|SQLITE_NOTADB/i.test(String(error.message ?? error));
|
|
21
|
-
};
|
|
22
|
-
export const errText = (error) => String(error?.message ?? error).slice(0, 500);
|
|
23
|
-
const busyError = (file, error, { retries, where }) => Object.assign(Error(`machine-db-busy: ${file} ${where}: database still locked after busy_timeout and ${retries} retries: ${String(error?.message ?? error).slice(0, 200)}`),
|
|
24
|
-
{ code: MACHINE_BUSY_CODE, cause: error, file, retries, where });
|
|
25
|
-
|
|
26
|
-
/** Persistent corruption diagnostics observe the store and stderr without recording an incident. */
|
|
27
|
-
export function corruptDiagnostic(file, error, { retries, where }) {
|
|
28
|
-
if (error?.code === MACHINE_CORRUPT_CODE) return error;
|
|
29
|
-
const out = Object.assign(Error(`machine-db-corrupt: ${file} ${where}: SQLITE_CORRUPT persisted after ${retries} reopen(s): ${errText(error)}`),
|
|
30
|
-
{ code: MACHINE_CORRUPT_CODE, cause: error, file, retries, where });
|
|
31
|
-
let check = null;
|
|
32
|
-
try {
|
|
33
|
-
const { DatabaseSync } = require('node:sqlite');
|
|
34
|
-
const db = new DatabaseSync(file, { readOnly: true, timeout: MACHINE_BUSY_TIMEOUT_MS });
|
|
35
|
-
try { check = db.prepare('PRAGMA quick_check').all().map((r) => r.quick_check).slice(0, 20); } finally { db.close(); }
|
|
36
|
-
} catch (e) { check = [`quick_check failed: ${errText(e)}`]; }
|
|
37
|
-
out.quickCheck = check;
|
|
38
|
-
process.stderr.write(`[machine-db] INCIDENT ${out.message} (quick_check: ${JSON.stringify(check)})\n`);
|
|
39
|
-
return out;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/** Connection retries share one policy; the machine owner supplies operational incident persistence. */
|
|
43
|
-
export function machineConnectionMethods({ reportIncident }) {
|
|
44
|
-
function openWithRetry(openRaw, { file, onCorrupt = reportIncident }) {
|
|
45
|
-
let lastError, busyOpens = 0, failures = 0;
|
|
46
|
-
for (;;) {
|
|
47
|
-
if (failures && OPEN_RETRY_DELAYS_MS[failures]) waitForRetry(OPEN_RETRY_DELAYS_MS[failures]);
|
|
48
|
-
try { return openRaw(); }
|
|
49
|
-
catch (error) {
|
|
50
|
-
const busy = isBusyError(error) && !isCorruptError(error);
|
|
51
|
-
if (!busy && !/unable to open/i.test(String(error?.message ?? '')) && !(isCorruptError(error) && error.code !== MACHINE_CORRUPT_CODE)) throw error;
|
|
52
|
-
lastError = error;
|
|
53
|
-
if (busy) {
|
|
54
|
-
if (busyOpens >= BUSY_RETRY_DELAYS_MS.length) throw busyError(path.resolve(file), error, { retries: busyOpens, where: 'open' });
|
|
55
|
-
scaledSleepSync(BUSY_RETRY_DELAYS_MS[busyOpens]);
|
|
56
|
-
busyOpens += 1;
|
|
57
|
-
} else if ((failures += 1) >= OPEN_RETRY_DELAYS_MS.length) break;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
if (isCorruptError(lastError)) throw onCorrupt(path.resolve(file), lastError, { retries: OPEN_RETRY_DELAYS_MS.length - 1, where: 'open' });
|
|
61
|
-
throw lastError;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function resilientConnection(openRaw, { file, inTransaction, onRecovered, onCorrupt }) {
|
|
65
|
-
let raw = openRaw(), generation = 0;
|
|
66
|
-
const reopen = () => { try { raw.close(); } catch { /* closed */ } raw = openRaw(); generation += 1; };
|
|
67
|
-
const retrying = (where, op) => {
|
|
68
|
-
let busyRetries = 0;
|
|
69
|
-
for (let retries = 0; ; retries += 1) {
|
|
70
|
-
try {
|
|
71
|
-
const out = op();
|
|
72
|
-
if (retries) onRecovered({ where, retries });
|
|
73
|
-
return out;
|
|
74
|
-
} catch (error) {
|
|
75
|
-
if (isBusyError(error) && !isMachineBusy(error) && !isCorruptError(error)) {
|
|
76
|
-
if (inTransaction() || raw.isTransaction) throw error;
|
|
77
|
-
if (busyRetries >= BUSY_RETRY_DELAYS_MS.length) throw busyError(file, error, { retries: busyRetries, where });
|
|
78
|
-
scaledSleepSync(BUSY_RETRY_DELAYS_MS[busyRetries]);
|
|
79
|
-
busyRetries += 1;
|
|
80
|
-
retries -= 1;
|
|
81
|
-
continue;
|
|
82
|
-
}
|
|
83
|
-
if (!isCorruptError(error) || error.code === MACHINE_CORRUPT_CODE) throw error;
|
|
84
|
-
if (inTransaction() || raw.isTransaction) throw error;
|
|
85
|
-
if (retries >= CORRUPT_RETRY_DELAYS_MS.length) throw onCorrupt(file, error, { retries, where });
|
|
86
|
-
waitForRetry(CORRUPT_RETRY_DELAYS_MS[retries]);
|
|
87
|
-
try { reopen(); } catch (openError) { if (!isCorruptError(openError)) throw openError; }
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
};
|
|
91
|
-
const prepare = (sql) => {
|
|
92
|
-
let stmt = null, gen = -1;
|
|
93
|
-
const settings = [];
|
|
94
|
-
const current = () => {
|
|
95
|
-
if (gen !== generation) {
|
|
96
|
-
stmt = raw.prepare(sql);
|
|
97
|
-
for (const [k, a] of settings) stmt[k](...a);
|
|
98
|
-
gen = generation;
|
|
99
|
-
}
|
|
100
|
-
return stmt;
|
|
101
|
-
};
|
|
102
|
-
retrying(`prepare ${sql.slice(0, 80)}`, current);
|
|
103
|
-
return new Proxy({}, {
|
|
104
|
-
get(_, prop) {
|
|
105
|
-
if (prop === 'run' || prop === 'get' || prop === 'all' || prop === 'iterate') return (...args) => retrying(`${prop} ${sql.slice(0, 80)}`, () => current()[prop](...args));
|
|
106
|
-
if (typeof prop === 'string' && /^set[A-Z]/.test(prop)) return (...args) => { settings.push([prop, args]); return current()[prop](...args); };
|
|
107
|
-
const value = current()[prop];
|
|
108
|
-
return typeof value === 'function' ? value.bind(current()) : value;
|
|
109
|
-
},
|
|
110
|
-
});
|
|
111
|
-
};
|
|
112
|
-
return new Proxy({}, {
|
|
113
|
-
get(_, prop) {
|
|
114
|
-
if (prop === 'prepare') return prepare;
|
|
115
|
-
if (prop === 'exec') return (sql) => retrying(`exec ${String(sql).slice(0, 80)}`, () => raw.exec(sql));
|
|
116
|
-
if (prop === 'reopen') return reopen;
|
|
117
|
-
if (prop === 'raw') return raw;
|
|
118
|
-
const value = raw[prop];
|
|
119
|
-
return typeof value === 'function' ? value.bind(raw) : value;
|
|
120
|
-
},
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function connectionState(openRaw, { file, inTransaction = () => false, onCorrupt = reportIncident }) {
|
|
125
|
-
const recovered = [];
|
|
126
|
-
const noteRecovered = (r) => {
|
|
127
|
-
recovered.push({ ...r, at: Date.now() });
|
|
128
|
-
process.stderr.write(`[machine-db] transient SQLITE_CORRUPT recovered after ${r.retries} reopen(s) at ${r.where} (${file})\n`);
|
|
129
|
-
};
|
|
130
|
-
const db = resilientConnection(openRaw, { file, inTransaction, onRecovered: noteRecovered, onCorrupt });
|
|
131
|
-
return { db, recovered, noteRecovered, close() { try { db.close(); } catch { /* closed */ } } };
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
return { openWithRetry, connectionState };
|
|
135
|
-
}
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
// Machine schema lifecycle runs only through the machine store's connection owner.
|
|
2
|
-
import fs from 'node:fs';
|
|
3
|
-
import os from 'node:os';
|
|
4
|
-
import path from 'node:path';
|
|
5
|
-
import { createRequire } from 'node:module';
|
|
6
|
-
import { artifactRoot } from './blob.mjs';
|
|
7
|
-
import { sha256 } from '../digest.mjs';
|
|
8
|
-
import { hasTable } from '../../scripts/lib/sqlite.mjs';
|
|
9
|
-
|
|
10
|
-
const require = createRequire(import.meta.url);
|
|
11
|
-
// SQLite ALTER RENAME quotes identifiers. Preserve literals/constraints while ignoring that spelling and formatting.
|
|
12
|
-
function sqlIdentity(sql) {
|
|
13
|
-
const tokens = String(sql).match(/--[^\r\n]*|\/\*[\s\S]*?\*\/|'(?:''|[^'])*'|"(?:[^"]|"")*"|`(?:[^`]|``)*`|\[[^\]]*\]|[a-z_][a-z_0-9]*|\d+(?:\.\d+)?|[^\s]/gi) ?? [];
|
|
14
|
-
const normalized = tokens.filter(token => !token.startsWith('--') && !token.startsWith('/*')).map(token => {
|
|
15
|
-
if (token.startsWith("'")) return token;
|
|
16
|
-
if (token.startsWith('"')) return token.slice(1, -1).replaceAll('""', '"').toLowerCase();
|
|
17
|
-
if (token.startsWith('`')) return token.slice(1, -1).replaceAll('``', '`').toLowerCase();
|
|
18
|
-
if (token.startsWith('[')) return token.slice(1, -1).toLowerCase();
|
|
19
|
-
return token.toLowerCase();
|
|
20
|
-
});
|
|
21
|
-
for (const at of [2, 3]) if (normalized.slice(at, at + 3).join(' ') === 'if not exists') normalized.splice(at, 3);
|
|
22
|
-
return JSON.stringify(normalized);
|
|
23
|
-
}
|
|
24
|
-
const objects = db => db.prepare("SELECT type,name,sql FROM sqlite_master WHERE sql IS NOT NULL AND name NOT GLOB 'sqlite_*' ORDER BY type,name").all();
|
|
25
|
-
|
|
26
|
-
/** Create or validate the current host schema; an existing store's journal is historical evidence, never a rewrite target. */
|
|
27
|
-
export function machineSchemaMethods({ schema: MACHINE_SCHEMA, version: MACHINE_VERSION, initSqlFile: INIT_SQL_FILE, controllers: CONTROLLERS, runtimeRev, pragma }) {
|
|
28
|
-
let requiredObjects;
|
|
29
|
-
function expectedObjects() {
|
|
30
|
-
if (requiredObjects) return requiredObjects;
|
|
31
|
-
const { DatabaseSync } = require('node:sqlite'), reference = new DatabaseSync(':memory:');
|
|
32
|
-
try {
|
|
33
|
-
reference.exec(fs.readFileSync(INIT_SQL_FILE, 'utf8'));
|
|
34
|
-
requiredObjects = objects(reference).map(row => ({ ...row, identity: sqlIdentity(row.sql) }));
|
|
35
|
-
return requiredObjects;
|
|
36
|
-
} finally { reference.close(); }
|
|
37
|
-
}
|
|
38
|
-
/** Refuse unsupported identity or a missing/changed current object without replacing the caller's store. */
|
|
39
|
-
function refuseOld(file, why) {
|
|
40
|
-
throw Object.assign(Error(`machine-schema-old: ${path.resolve(file)} ${why}; expected '${MACHINE_SCHEMA}' (user_version ${MACHINE_VERSION}) with its current physical schema; the refused store remains unchanged`),
|
|
41
|
-
{ code: 'STARCI_MACHINE_SCHEMA_OLD' });
|
|
42
|
-
}
|
|
43
|
-
function checkSchema(db, file) {
|
|
44
|
-
const version = Number(pragma(db, 'user_version'));
|
|
45
|
-
if (!hasTable(db, 'machine_meta')) refuseOld(file, `has no machine_meta (user_version ${version})`);
|
|
46
|
-
const schema = db.prepare("SELECT value FROM machine_meta WHERE key='schema'").get()?.value;
|
|
47
|
-
if (schema !== MACHINE_SCHEMA) refuseOld(file, `is schema '${schema ?? 'none'}'`);
|
|
48
|
-
if (version !== MACHINE_VERSION) refuseOld(file, `is user_version ${version}`);
|
|
49
|
-
const actual = new Map(objects(db).map(row => [`${row.type}:${row.name}`, sqlIdentity(row.sql)]));
|
|
50
|
-
for (const row of expectedObjects()) if (actual.get(`${row.type}:${row.name}`) !== row.identity)
|
|
51
|
-
refuseOld(file, `has a missing or changed ${row.type} '${row.name}'`);
|
|
52
|
-
}
|
|
53
|
-
function rollbackSchema(db, error) {
|
|
54
|
-
try { db.exec('ROLLBACK'); }
|
|
55
|
-
catch (rollbackError) { throw new AggregateError([error, rollbackError], 'machine schema failed and rollback failed', { cause: error }); }
|
|
56
|
-
throw error;
|
|
57
|
-
}
|
|
58
|
-
function createSchema(db, { file, env, now }) {
|
|
59
|
-
const sql = fs.readFileSync(INIT_SQL_FILE, 'utf8'), at = now();
|
|
60
|
-
db.exec('BEGIN IMMEDIATE');
|
|
61
|
-
try {
|
|
62
|
-
if (Number(pragma(db, 'user_version')) !== 0 || db.prepare("SELECT 1 FROM sqlite_master WHERE name NOT GLOB 'sqlite_*' LIMIT 1").get()) {
|
|
63
|
-
// Another initializer may have committed while this connection waited for BEGIN; keep its current rows/journal.
|
|
64
|
-
checkSchema(db, file); db.exec('COMMIT'); return;
|
|
65
|
-
}
|
|
66
|
-
db.exec(sql);
|
|
67
|
-
const meta = db.prepare('INSERT INTO machine_meta(key,value) VALUES(?,?) ON CONFLICT(key) DO UPDATE SET value=excluded.value');
|
|
68
|
-
for (const [key, value] of Object.entries({ host_id: os.hostname(), schema: MACHINE_SCHEMA, created_at: String(at), blob_root: artifactRoot(env),
|
|
69
|
-
runtime_rev: runtimeRev(), sqlite_version: db.prepare('select sqlite_version() v').get().v,
|
|
70
|
-
node_version: process.version, journal_mode: 'wal' })) meta.run(key, String(value));
|
|
71
|
-
db.prepare("INSERT INTO schema_migrations(version,name,runtime_rev,sql_sha256,started_at,finished_at,status) VALUES(?,'0001-init',?,?,?,?,'done')")
|
|
72
|
-
.run(MACHINE_VERSION, runtimeRev(), sha256(sql), at, now());
|
|
73
|
-
// Every controller starts in shadow until the owner switches it.
|
|
74
|
-
for (const controller of CONTROLLERS) {
|
|
75
|
-
db.prepare("INSERT INTO mode_changes(controller,from_mode,to_mode,by,reason,at) VALUES(?,NULL,'shadow','machine-db:init','0001-init',?)").run(controller, at);
|
|
76
|
-
db.prepare("INSERT INTO controller_modes(controller,mode,set_at,set_by) VALUES(?,'shadow',?,'machine-db:init')").run(controller, at);
|
|
77
|
-
}
|
|
78
|
-
db.exec(`PRAGMA user_version=${MACHINE_VERSION}`);
|
|
79
|
-
checkSchema(db, file);
|
|
80
|
-
db.exec('COMMIT');
|
|
81
|
-
} catch (error) { rollbackSchema(db, error); }
|
|
82
|
-
}
|
|
83
|
-
return { checkSchema, createSchema };
|
|
84
|
-
}
|