@cosmicdrift/kumiko-framework 0.385.0 → 0.387.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/LICENSE +21 -57
- package/README.md +2 -2
- package/agents/widgets.md +39 -39
- package/dist/api/api-constants.js +2 -2
- package/dist/api/auth-middleware.js +7 -7
- package/dist/api/auth-rate-limiters.js +7 -3
- package/dist/api/auth-routes-mfa.js +1 -1
- package/dist/api/auth-routes-session.js +1 -1
- package/dist/api/auth-routes-shared.js +7 -3
- package/dist/api/auth-routes-signup.js +1 -1
- package/dist/api/client-ip.js +3 -1
- package/dist/api/extra-route.js +1 -1
- package/dist/api/pii-leak-guard.js +4 -2
- package/dist/api/readiness.js +5 -5
- package/dist/api/redis-sse-broker.js +8 -6
- package/dist/api/request-context.d.ts +2 -0
- package/dist/api/request-context.js +16 -7
- package/dist/api/routes.js +2 -2
- package/dist/api/server.js +34 -33
- package/dist/api/sse-broker.js +1 -1
- package/dist/api/sse-route.d.ts +13 -13
- package/dist/api/sse-route.js +15 -15
- package/dist/auth/index.js +4 -4
- package/dist/auth/roles.d.ts +4 -4
- package/dist/auth/roles.js +24 -24
- package/dist/compliance/duration-spec.js +18 -20
- package/dist/compliance/index.js +2 -3
- package/dist/compliance/override-schema.d.ts +4 -4
- package/dist/compliance/override-schema.js +19 -22
- package/dist/compliance/profiles.d.ts +26 -29
- package/dist/compliance/profiles.js +62 -69
- package/dist/compliance/sub-processors.d.ts +28 -30
- package/dist/compliance/sub-processors.js +17 -21
- package/dist/consumer-cli.js +4 -4
- package/dist/crypto/blind-index.js +18 -17
- package/dist/crypto/ciphertext-pattern.js +1 -1
- package/dist/crypto/event-pii.js +3 -3
- package/dist/crypto/kek-source.d.ts +1 -1
- package/dist/crypto/kek-source.js +3 -2
- package/dist/crypto/kms-adapter.d.ts +1 -1
- package/dist/crypto/kms-adapter.js +4 -6
- package/dist/crypto/kms-wiring.d.ts +1 -2
- package/dist/crypto/kms-wiring.js +2 -2
- package/dist/crypto/pg-kms-adapter.js +2 -6
- package/dist/crypto/pii-field-encryption.js +4 -4
- package/dist/crypto/subject-resolver.d.ts +1 -1
- package/dist/crypto/subject-resolver.js +2 -2
- package/dist/db/apply-entity-event.d.ts +6 -6
- package/dist/db/apply-entity-event.js +55 -55
- package/dist/db/blind-index-cleanup.js +7 -7
- package/dist/db/collect-table-metas.js +10 -11
- package/dist/db/compound-types.d.ts +1 -1
- package/dist/db/compound-types.js +12 -12
- package/dist/db/cross-tenant-reads.d.ts +8 -0
- package/dist/db/cross-tenant-reads.js +23 -0
- package/dist/db/cursor.js +3 -4
- package/dist/db/dialect.d.ts +2 -2
- package/dist/db/dialect.js +2 -2
- package/dist/db/eagerload.d.ts +3 -3
- package/dist/db/eagerload.js +21 -12
- package/dist/db/entity-table-meta.d.ts +2 -2
- package/dist/db/entity-table-meta.js +4 -4
- package/dist/db/event-store-executor-context.d.ts +1 -1
- package/dist/db/event-store-executor-context.js +13 -13
- package/dist/db/event-store-executor-read.js +27 -28
- package/dist/db/event-store-executor-write.js +65 -70
- package/dist/db/located-timestamp.d.ts +11 -11
- package/dist/db/located-timestamp.js +29 -30
- package/dist/db/migrate-generator.d.ts +1 -1
- package/dist/db/migrate-generator.js +26 -19
- package/dist/db/migrate-runner.js +25 -25
- package/dist/db/money.d.ts +6 -6
- package/dist/db/money.js +29 -27
- package/dist/db/queries/backfill-pii.js +5 -5
- package/dist/db/queries/ddl.js +1 -1
- package/dist/db/queries/es-ops.d.ts +1 -1
- package/dist/db/queries/es-ops.js +1 -1
- package/dist/db/queries/event-consumer.js +3 -3
- package/dist/db/queries/event-store.js +4 -2
- package/dist/db/queries/projection-rebuild.js +3 -3
- package/dist/db/queries/raw-sql.d.ts +2 -2
- package/dist/db/queries/raw-sql.js +2 -2
- package/dist/db/queries/shadow-swap.js +19 -19
- package/dist/db/queries/test-stack.d.ts +4 -1
- package/dist/db/queries/test-stack.js +15 -2
- package/dist/db/query.d.ts +1 -1
- package/dist/db/query.js +6 -6
- package/dist/db/rebuild-marker.js +20 -21
- package/dist/db/replay-migration-sql.js +2 -2
- package/dist/db/schema-inspection.js +4 -5
- package/dist/db/table-builder.d.ts +1 -2
- package/dist/db/table-builder.js +82 -82
- package/dist/db/tenant-db.d.ts +2 -2
- package/dist/db/tenant-db.js +9 -2
- package/dist/derivatives/delete-stored-file.js +5 -2
- package/dist/derivatives/derivatives-context.js +1 -1
- package/dist/engine/boot-validator/access-roles.js +4 -3
- package/dist/engine/boot-validator/api-ext.js +14 -14
- package/dist/engine/boot-validator/config-deps.js +7 -6
- package/dist/engine/boot-validator/detail-screens.js +3 -3
- package/dist/engine/boot-validator/entity-handler.js +60 -61
- package/dist/engine/boot-validator/gdpr-storage.js +4 -4
- package/dist/engine/boot-validator/i18n-keys.js +1 -1
- package/dist/engine/boot-validator/index.d.ts +2 -2
- package/dist/engine/boot-validator/index.js +20 -20
- package/dist/engine/boot-validator/nav.js +5 -5
- package/dist/engine/boot-validator/ownership.js +12 -9
- package/dist/engine/boot-validator/parent-ref.js +1 -1
- package/dist/engine/boot-validator/pii-retention.js +50 -57
- package/dist/engine/boot-validator/projection-list-screens.js +7 -7
- package/dist/engine/boot-validator/query-refs.js +1 -1
- package/dist/engine/boot-validator/screens-actions.js +15 -16
- package/dist/engine/boot-validator/screens-forms.js +10 -11
- package/dist/engine/boot-validator/screens-lists.js +14 -15
- package/dist/engine/boot-validator/screens.js +34 -40
- package/dist/engine/boot-validator/security-baseline.js +3 -2
- package/dist/engine/boot-validator/transfer-graph.js +2 -2
- package/dist/engine/boot-validator/workspaces.js +3 -3
- package/dist/engine/bridge-stub.js +3 -4
- package/dist/engine/build-app-schema.js +75 -75
- package/dist/engine/build-config-feature-schema.js +50 -50
- package/dist/engine/build-target.js +20 -23
- package/dist/engine/codemod/pipeline-codemod.js +20 -13
- package/dist/engine/config-helpers.js +4 -4
- package/dist/engine/constants.js +2 -2
- package/dist/engine/create-app.js +2 -2
- package/dist/engine/define-feature.js +12 -11
- package/dist/engine/define-handler.js +12 -11
- package/dist/engine/define-step.js +1 -1
- package/dist/engine/define-workflow.d.ts +5 -5
- package/dist/engine/define-workflow.js +6 -6
- package/dist/engine/entity-handlers.js +13 -14
- package/dist/engine/event-def.js +2 -2
- package/dist/engine/extension-names.d.ts +16 -17
- package/dist/engine/extension-names.js +37 -38
- package/dist/engine/extensions/escape-hatch-usage.js +1 -1
- package/dist/engine/extensions/tenant-data.js +2 -2
- package/dist/engine/extensions/user-data.d.ts +23 -24
- package/dist/engine/factories.d.ts +14 -13
- package/dist/engine/factories.js +38 -37
- package/dist/engine/feature-ast/entity-field-types.js +1 -1
- package/dist/engine/feature-ast/extractors/handlers.js +1 -1
- package/dist/engine/feature-ast/extractors/hooks.js +1 -1
- package/dist/engine/feature-ast/extractors/shared.d.ts +2 -3
- package/dist/engine/feature-ast/extractors/shared.js +2 -3
- package/dist/engine/feature-ast/parse.js +13 -13
- package/dist/engine/feature-ast/patch.js +1 -1
- package/dist/engine/feature-ast/pattern-change-schema.d.ts +8 -0
- package/dist/engine/feature-ast/pattern-change-schema.js +1 -1
- package/dist/engine/feature-ast/patterns.d.ts +2 -2
- package/dist/engine/feature-ast/render.js +2 -2
- package/dist/engine/feature-config-events-jobs.js +15 -12
- package/dist/engine/feature-entity-handlers.js +9 -5
- package/dist/engine/feature-manifest.d.ts +9 -9
- package/dist/engine/feature-manifest.js +6 -7
- package/dist/engine/feature-ui-extensions.d.ts +3 -3
- package/dist/engine/feature-ui-extensions.js +30 -22
- package/dist/engine/field-access.js +5 -6
- package/dist/engine/index.d.ts +1 -0
- package/dist/engine/index.js +4 -4
- package/dist/engine/object-form.d.ts +1 -0
- package/dist/engine/object-form.js +14 -4
- package/dist/engine/ownership.js +3 -3
- package/dist/engine/parse-ref-target.js +5 -7
- package/dist/engine/pattern-library/index.js +2 -2
- package/dist/engine/pattern-library/library.js +2 -2
- package/dist/engine/pattern-library/types.js +3 -3
- package/dist/engine/pipeline.js +2 -2
- package/dist/engine/project-app-schema-for-roles.js +1 -1
- package/dist/engine/projection-helpers.js +9 -9
- package/dist/engine/qualified-name.js +2 -2
- package/dist/engine/registry-facade.js +1 -1
- package/dist/engine/registry-ingest.js +12 -12
- package/dist/engine/registry-state.js +17 -19
- package/dist/engine/registry-validate.js +12 -12
- package/dist/engine/run-in.js +2 -2
- package/dist/engine/run-pipeline.js +7 -0
- package/dist/engine/schema-builder.js +40 -38
- package/dist/engine/screen-filter-ops.js +17 -20
- package/dist/engine/screen-helpers.js +4 -3
- package/dist/engine/state-machine.d.ts +12 -12
- package/dist/engine/state-machine.js +7 -7
- package/dist/engine/steps/_resolver-utils.js +4 -3
- package/dist/engine/steps/for-each.js +4 -4
- package/dist/engine/steps/unsafe-projection-delete.js +1 -1
- package/dist/engine/steps/unsafe-projection-upsert.js +4 -4
- package/dist/engine/steps/wait-for-event.js +4 -4
- package/dist/engine/steps/workflow-system-events.js +3 -3
- package/dist/engine/tier-resolver-extension.d.ts +29 -27
- package/dist/engine/tier-resolver-extension.js +26 -26
- package/dist/engine/transfer-adjacency.js +2 -2
- package/dist/engine/validate-projection-allowlist.js +7 -27
- package/dist/engine/walk-step-instances.d.ts +2 -0
- package/dist/engine/walk-step-instances.js +38 -0
- package/dist/engine/where-rule-lint.js +1 -1
- package/dist/engine/write-handler-qn-extract.js +1 -1
- package/dist/entrypoint/index.js +3 -3
- package/dist/env/index.d.ts +7 -6
- package/dist/env/index.js +10 -9
- package/dist/errors/transition-details.js +7 -7
- package/dist/errors/write-error-info.d.ts +6 -6
- package/dist/errors/write-error-info.js +6 -6
- package/dist/es-ops/context.d.ts +9 -9
- package/dist/es-ops/context.js +28 -28
- package/dist/es-ops/index.js +7 -9
- package/dist/es-ops/operations-schema.js +4 -10
- package/dist/es-ops/runner.d.ts +11 -11
- package/dist/es-ops/runner.js +49 -53
- package/dist/es-ops/types.d.ts +55 -55
- package/dist/es-ops/types.js +11 -11
- package/dist/event-store/admin-api.js +6 -7
- package/dist/event-store/errors.js +1 -1
- package/dist/event-store/event-store.js +2 -2
- package/dist/event-store/rebuild-dead-letter.js +1 -1
- package/dist/files/feature.js +9 -10
- package/dist/files/file-handle.js +5 -6
- package/dist/files/file-ref-entity.js +15 -15
- package/dist/files/file-ref-table.js +7 -8
- package/dist/files/file-routes.d.ts +7 -0
- package/dist/files/file-routes.js +11 -5
- package/dist/files/in-memory-provider.js +8 -9
- package/dist/files/local-provider.js +18 -25
- package/dist/files/provider-resolver.d.ts +7 -7
- package/dist/files/storage-tracking.js +2 -2
- package/dist/files/zip-stream.d.ts +12 -19
- package/dist/files/zip-stream.js +44 -63
- package/dist/i18n/required-surface-keys.d.ts +1 -1
- package/dist/i18n/required-surface-keys.js +8 -8
- package/dist/internal/stack-support.d.ts +1 -1
- package/dist/internal/stack-support.js +1 -1
- package/dist/jobs/job-runner-helpers.d.ts +1 -2
- package/dist/jobs/job-runner-helpers.js +8 -18
- package/dist/jobs/job-runner-schedulers.d.ts +1 -1
- package/dist/jobs/job-runner-schedulers.js +3 -4
- package/dist/jobs/job-runner.js +17 -18
- package/dist/lifecycle/lifecycle.js +3 -5
- package/dist/lifecycle/signal-handlers.js +5 -4
- package/dist/logging/utils.d.ts +2 -1
- package/dist/logging/utils.js +38 -10
- package/dist/migrations/index.js +1 -1
- package/dist/migrations/kumiko-drift.js +14 -13
- package/dist/migrations/pending-rebuilds.d.ts +21 -20
- package/dist/migrations/pending-rebuilds.js +27 -27
- package/dist/migrations/projection-table-index.d.ts +3 -3
- package/dist/migrations/projection-table-index.js +8 -8
- package/dist/observability/metric-validator.js +1 -1
- package/dist/observability/metrics-handle.js +3 -3
- package/dist/pipeline/append-event-core.js +11 -2
- package/dist/pipeline/cascade-handler.d.ts +1 -1
- package/dist/pipeline/dispatch-batch.js +2 -2
- package/dist/pipeline/dispatch-query.js +10 -10
- package/dist/pipeline/dispatch-shared.d.ts +1 -0
- package/dist/pipeline/dispatch-shared.js +42 -19
- package/dist/pipeline/dispatch-stream.js +1 -1
- package/dist/pipeline/dispatch-write.js +3 -3
- package/dist/pipeline/dispatcher-utils.d.ts +1 -1
- package/dist/pipeline/dispatcher-utils.js +1 -1
- package/dist/pipeline/event-consumer-state.js +2 -2
- package/dist/pipeline/event-dispatcher-delivery.js +1 -1
- package/dist/pipeline/event-dispatcher.js +17 -28
- package/dist/pipeline/irreversible-operation-gate.js +1 -1
- package/dist/pipeline/lifecycle-pipeline.js +9 -9
- package/dist/pipeline/member-read-only-transaction.js +1 -3
- package/dist/pipeline/msp-rebuild.js +1 -1
- package/dist/pipeline/projection-rebuild.js +13 -12
- package/dist/pipeline/system-hooks.js +39 -41
- package/dist/pipeline/system-identity-switch.js +1 -3
- package/dist/pipeline/tenant-timezone-cache.js +1 -1
- package/dist/random/generate.d.ts +29 -29
- package/dist/random/generate.js +30 -30
- package/dist/random/words.js +20 -20
- package/dist/rate-limit/middleware.js +9 -5
- package/dist/redis/client.js +2 -2
- package/dist/redis/pubsub-signal.js +14 -17
- package/dist/schema-cli.js +116 -22
- package/dist/search/index.js +4 -4
- package/dist/search/purge-subject.js +4 -5
- package/dist/search/reindex-entity.js +6 -9
- package/dist/time/boot-tz-warning.d.ts +2 -2
- package/dist/time/boot-tz-warning.js +8 -8
- package/dist/time/iana.js +10 -10
- package/dist/time/index.js +9 -9
- package/dist/time/legacy-date.d.ts +2 -2
- package/dist/time/legacy-date.js +6 -8
- package/dist/time/polyfill.js +1 -1
- package/dist/time/tz-context.d.ts +1 -1
- package/dist/time/tz-context.js +1 -1
- package/dist/ui-types/app-schema.js +7 -7
- package/dist/ui-types/list-row-meta.js +4 -4
- package/dist/upgrade-cli.js +10 -9
- package/dist/utils/compare.js +1 -1
- package/package.json +4 -4
- package/src/changes.json +54 -0
- package/src/scripts/codemod/crypto-shredding-testing-move.ts +2 -2
- package/src/scripts/codemod/migrate-cross-tenant.ts +1 -1
- package/src/scripts/codemod/migrate-open-to-all.ts +1 -1
- package/src/scripts/codemod/pii-personal-migration.ts +17 -8
- package/src/scripts/codemod/unprocessable-error-details-reason.ts +2 -2
package/dist/schema-cli.js
CHANGED
|
@@ -17,8 +17,10 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
|
|
|
17
17
|
};
|
|
18
18
|
import { existsSync, mkdirSync, readdirSync, writeFileSync } from "node:fs";
|
|
19
19
|
import { join, resolve as resolvePath } from "node:path";
|
|
20
|
+
import { isPlainObject } from "@cosmicdrift/kumiko-types/type-guards";
|
|
20
21
|
import { configureBlindIndexKey, configurePiiSubjectKms, resolveKmsWiringAsync, } from "./crypto/index.js";
|
|
21
22
|
import { assertUniqueTableNames, assertValidMigrationName, baselineMigrations, createDbConnection, diffReplayAgainstSnapshot, fetchAppliedMigrations, findCommentedDropTables, generateMigration, isRetiredFrameworkTable, loadMigrationsFromDir, loadSnapshotJson, readMigrationSqlTexts, rebuildTablesFromDiff, replayMigrationsDir, runMigrationsFromDir, tableExists, writeRebuildMarker, writeSnapshotJson, } from "./db/index.js";
|
|
23
|
+
import { isEntityTableMeta } from "./db/table-introspection.js";
|
|
22
24
|
import { validateBoot } from "./engine/boot-validator.js";
|
|
23
25
|
import { FILE_STORAGE_PROVIDER_BOOT_SENTINEL, FILE_STORAGE_PROVIDER_ENV, } from "./engine/extension-names.js";
|
|
24
26
|
import { createRegistry } from "./engine/registry.js";
|
|
@@ -75,6 +77,79 @@ function parseValidateBootOptions(raw) {
|
|
|
75
77
|
...(warnOnUniqueAccessRoles !== undefined && { warnOnUniqueAccessRoles }),
|
|
76
78
|
};
|
|
77
79
|
}
|
|
80
|
+
function tableNameHint(entry) {
|
|
81
|
+
if (isPlainObject(entry) && typeof entry["tableName"] === "string")
|
|
82
|
+
return entry["tableName"];
|
|
83
|
+
return "unknown";
|
|
84
|
+
}
|
|
85
|
+
// Every non-optional top-level property of FeatureDefinition, grouped by the runtime shape the
|
|
86
|
+
// boot validators rely on; a hand-built entry missing one would crash far from the cause.
|
|
87
|
+
const FEATURE_ARRAY_KEYS = [
|
|
88
|
+
"requires",
|
|
89
|
+
"optionalRequires",
|
|
90
|
+
"configSeeds",
|
|
91
|
+
"extensionUsages",
|
|
92
|
+
"extensionSelectors",
|
|
93
|
+
"bootChecks",
|
|
94
|
+
"referenceData",
|
|
95
|
+
"configReads",
|
|
96
|
+
"authClaimsHooks",
|
|
97
|
+
];
|
|
98
|
+
const FEATURE_SET_KEYS = [
|
|
99
|
+
"requiredProjections",
|
|
100
|
+
"requiredSteps",
|
|
101
|
+
"exposedApis",
|
|
102
|
+
"usedApis",
|
|
103
|
+
];
|
|
104
|
+
const FEATURE_RECORD_KEYS = [
|
|
105
|
+
"relations",
|
|
106
|
+
"writeHandlers",
|
|
107
|
+
"queryHandlers",
|
|
108
|
+
"streamHandlers",
|
|
109
|
+
"translations",
|
|
110
|
+
"configKeys",
|
|
111
|
+
"jobs",
|
|
112
|
+
"registrarExtensions",
|
|
113
|
+
"notifications",
|
|
114
|
+
"events",
|
|
115
|
+
"eventMigrations",
|
|
116
|
+
"handlerEntityMappings",
|
|
117
|
+
"metrics",
|
|
118
|
+
"secretKeys",
|
|
119
|
+
"secretNamespaces",
|
|
120
|
+
"projections",
|
|
121
|
+
"multiStreamProjections",
|
|
122
|
+
"claimKeys",
|
|
123
|
+
"screens",
|
|
124
|
+
"navs",
|
|
125
|
+
"workspaces",
|
|
126
|
+
"httpRoutes",
|
|
127
|
+
"webSocketRoutes",
|
|
128
|
+
"storeTables",
|
|
129
|
+
];
|
|
130
|
+
function isFeatureDefinitionShape(entry) {
|
|
131
|
+
if (!isPlainObject(entry))
|
|
132
|
+
return false;
|
|
133
|
+
return (typeof entry["name"] === "string" &&
|
|
134
|
+
typeof entry["systemScope"] === "boolean" &&
|
|
135
|
+
FEATURE_ARRAY_KEYS.every((key) => Array.isArray(entry[key])) &&
|
|
136
|
+
FEATURE_SET_KEYS.every((key) => entry[key] instanceof Set) &&
|
|
137
|
+
FEATURE_RECORD_KEYS.every((key) => isPlainObject(entry[key])));
|
|
138
|
+
}
|
|
139
|
+
// FEATURES comes from the app's schema module; one malformed entry would make
|
|
140
|
+
// validateBoot fail with an unrelated error far from the cause, so name the index here.
|
|
141
|
+
function featureDefinitionsOf(value) {
|
|
142
|
+
if (!Array.isArray(value))
|
|
143
|
+
return undefined;
|
|
144
|
+
const definitions = [];
|
|
145
|
+
for (const [index, entry] of value.entries()) {
|
|
146
|
+
if (!isFeatureDefinitionShape(entry)) {
|
|
147
|
+
throw new Error(`FEATURES[${index}] is not a FeatureDefinition (expected an object with a string \`name\`)`);
|
|
148
|
+
}
|
|
149
|
+
definitions.push(entry);
|
|
150
|
+
}
|
|
151
|
+
return definitions;
|
|
152
|
+
}
|
|
78
153
|
// Single load-path for kumiko/schema.ts (512/3) — `generate` and `validate`
|
|
79
154
|
// both need ENTITY_METAS (and validate additionally wants FEATURES); this is
|
|
80
155
|
// the one place that enforces "ENTITY_METAS must be an array" so the two
|
|
@@ -83,13 +158,20 @@ function parseValidateBootOptions(raw) {
|
|
|
83
158
|
async function loadSchemaModFromApp(schemaFile) {
|
|
84
159
|
// bun imports TS directly — no spawn needed.
|
|
85
160
|
const mod = await import(__rewriteRelativeImportExtension(schemaFile));
|
|
86
|
-
|
|
161
|
+
const entityMetas = mod["ENTITY_METAS"];
|
|
162
|
+
if (!Array.isArray(entityMetas)) {
|
|
87
163
|
throw new Error(`Schema file ${schemaFile} muss \`export const ENTITY_METAS: EntityTableMeta[]\` haben.`);
|
|
88
164
|
}
|
|
89
|
-
const
|
|
90
|
-
|
|
165
|
+
const validMetas = [];
|
|
166
|
+
for (const [index, entry] of entityMetas.entries()) {
|
|
167
|
+
if (!isEntityTableMeta(entry)) {
|
|
168
|
+
throw new Error(`Schema file ${schemaFile}: ENTITY_METAS[${index}] is not an EntityTableMeta (table: ${tableNameHint(entry)}).`);
|
|
169
|
+
}
|
|
170
|
+
validMetas.push(entry);
|
|
171
|
+
}
|
|
172
|
+
assertUniqueTableNames(validMetas);
|
|
91
173
|
return {
|
|
92
|
-
entityMetas,
|
|
174
|
+
entityMetas: validMetas,
|
|
93
175
|
features: mod["FEATURES"],
|
|
94
176
|
validateBootOptions: mod["VALIDATE_BOOT_OPTIONS"],
|
|
95
177
|
};
|
|
@@ -223,9 +305,9 @@ export async function runSchemaCli(argv, appCwd, out, options = {}) {
|
|
|
223
305
|
mkdirSync(migrationsDir, { recursive: true });
|
|
224
306
|
writeFileSync(join(migrationsDir, result.filename), result.sqlContent);
|
|
225
307
|
writeSnapshotJson(snapshotPath, result.snapshot);
|
|
226
|
-
// Rebuild
|
|
227
|
-
// (prevSnapshot===null)
|
|
228
|
-
//
|
|
308
|
+
// Rebuild markers are written only for incremental migrations — the initial
|
|
309
|
+
// migration (prevSnapshot===null) only creates tables, so there are no historical
|
|
310
|
+
// events to replay.
|
|
229
311
|
const rebuildTables = prevSnapshot === null ? [] : rebuildTablesFromDiff(result.diff);
|
|
230
312
|
writeRebuildMarker(migrationsDir, result.filename, rebuildTables);
|
|
231
313
|
out.log("");
|
|
@@ -269,7 +351,6 @@ export async function runSchemaCli(argv, appCwd, out, options = {}) {
|
|
|
269
351
|
out.err(` ${e instanceof Error ? e.message : String(e)}`);
|
|
270
352
|
return 1;
|
|
271
353
|
}
|
|
272
|
-
const mod = { FEATURES: schemaMod.features };
|
|
273
354
|
let ok = true;
|
|
274
355
|
// 1. Schema drift — compute the diff, never write.
|
|
275
356
|
const metas = schemaMod.entityMetas;
|
|
@@ -299,9 +380,21 @@ export async function runSchemaCli(argv, appCwd, out, options = {}) {
|
|
|
299
380
|
out.err(" Fix: `kumiko-schema generate <name>`, then commit the migration.");
|
|
300
381
|
}
|
|
301
382
|
// 2. Boot validity — needs the composed feature set.
|
|
302
|
-
|
|
383
|
+
let bootFeatures;
|
|
384
|
+
let bootFeaturesError;
|
|
385
|
+
try {
|
|
386
|
+
bootFeatures = featureDefinitionsOf(schemaMod.features);
|
|
387
|
+
}
|
|
388
|
+
catch (e) {
|
|
389
|
+
bootFeaturesError = e instanceof Error ? e.message : String(e);
|
|
390
|
+
}
|
|
391
|
+
if (bootFeaturesError !== undefined) {
|
|
392
|
+
ok = false;
|
|
393
|
+
out.err(` ✗ boot: ${bootFeaturesError}`);
|
|
394
|
+
}
|
|
395
|
+
else if (bootFeatures) {
|
|
303
396
|
try {
|
|
304
|
-
validateBoot(
|
|
397
|
+
validateBoot(bootFeatures, {
|
|
305
398
|
...parseValidateBootOptions(schemaMod.validateBootOptions),
|
|
306
399
|
env: withFileStoragePresence(process.env),
|
|
307
400
|
});
|
|
@@ -350,7 +443,7 @@ export async function runSchemaCli(argv, appCwd, out, options = {}) {
|
|
|
350
443
|
// replayMigrationsDir fail-loud's on a table-DDL statement it can't
|
|
351
444
|
// parse — that must surface as a normal ✗ line like every other
|
|
352
445
|
// check here, not a raw stack trace that kills the process before
|
|
353
|
-
// any later check (or the Fix: help) ever runs
|
|
446
|
+
// any later check (or the Fix: help) ever runs.
|
|
354
447
|
ok = false;
|
|
355
448
|
out.err(` ✗ migration-content: ${e instanceof Error ? e.message : String(e)}`);
|
|
356
449
|
out.err(" Fix: a statement in a migration file isn't recognizable to the replay parser — extend the parser's recognized patterns, or reword the migration to one it understands.");
|
|
@@ -372,11 +465,12 @@ export async function runSchemaCli(argv, appCwd, out, options = {}) {
|
|
|
372
465
|
let wiring;
|
|
373
466
|
try {
|
|
374
467
|
const result = await runMigrationsFromDir(db, migrationsDir);
|
|
375
|
-
// Framework
|
|
376
|
-
// `generate
|
|
377
|
-
//
|
|
378
|
-
//
|
|
379
|
-
//
|
|
468
|
+
// Framework infrastructure tables (event store + pipeline state) are not captured by
|
|
469
|
+
// `generate`, which handles only entity read tables. Existing databases have them
|
|
470
|
+
// from the legacy-drizzle foundation; without them, a greenfield database (the
|
|
471
|
+
// first app without a cutover) would lack kumiko_events and runProdApp would fail
|
|
472
|
+
// during boot. All three are idempotent (tableExists-gated), so they are no-ops for
|
|
473
|
+
// existing databases.
|
|
380
474
|
await createEventsTable(db);
|
|
381
475
|
await createEventConsumerStateTable(db);
|
|
382
476
|
await createProjectionStateTable(db);
|
|
@@ -400,14 +494,14 @@ export async function runSchemaCli(argv, appCwd, out, options = {}) {
|
|
|
400
494
|
// (kumiko_pending_rebuilds) survives a failed/crashed rebuild from an
|
|
401
495
|
// earlier apply, so a later apply with zero new migrations still must
|
|
402
496
|
// retry it — otherwise a failed rebuild is silently never retried,
|
|
403
|
-
// since the migration itself is already tracked applied
|
|
497
|
+
// since the migration itself is already tracked applied.
|
|
404
498
|
if (options.features) {
|
|
405
499
|
// A rebuild replays applyEntityEvent, which computes bidx columns from
|
|
406
500
|
// configureBlindIndexKey/configurePiiSubjectKms — both boot-injected by
|
|
407
501
|
// run{Prod,Dev}App but never by this standalone CLI (the migrate-db
|
|
408
502
|
// initContainer). Without wiring them here, a rebuild would silently
|
|
409
503
|
// null out bidx columns and, for ciphertext, throw only once it hits
|
|
410
|
-
// the row
|
|
504
|
+
// the row.
|
|
411
505
|
wiring = await resolveKmsWiringAsync(process.env, {
|
|
412
506
|
logPrefix: "[kumiko schema apply]",
|
|
413
507
|
log: out.log,
|
|
@@ -485,10 +579,10 @@ export async function runSchemaCli(argv, appCwd, out, options = {}) {
|
|
|
485
579
|
const local = loadMigrationsFromDir(migrationsDir);
|
|
486
580
|
const { db, close } = createDbConnection({ url: dbUrl });
|
|
487
581
|
try {
|
|
488
|
-
//
|
|
489
|
-
//
|
|
490
|
-
//
|
|
491
|
-
//
|
|
582
|
+
// On a fresh database where `kumiko schema apply` has never run, the tracking table is
|
|
583
|
+
// absent, which means "nothing applied". Connection and permission errors must not
|
|
584
|
+
// be swallowed (false pending results would hide the problem) — tableExists checks
|
|
585
|
+
// only for existence, while all other errors propagate.
|
|
492
586
|
const trackingExists = await tableExists(db, "_kumiko_migrations");
|
|
493
587
|
const applied = trackingExists
|
|
494
588
|
? new Set((await fetchAppliedMigrations(db)).map((a) => a.id))
|
package/dist/search/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
// Meilisearch
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
1
|
+
// The Meilisearch adapter is exposed through the subpath export
|
|
2
|
+
// `@cosmicdrift/kumiko-framework/search/meilisearch`. This keeps the main barrel from loading a
|
|
3
|
+
// Meilisearch client when consumers only use SearchAdapter types, so apps that do not use
|
|
4
|
+
// Meilisearch do not pull in the client code.
|
|
5
5
|
export { collectSearchableFieldNames, configureTenantSearchIndex, } from "./configure-tenant-index.js";
|
|
6
6
|
export { deriveSearchAdapterConfig } from "./derive-search-adapter-config.js";
|
|
7
7
|
export { createInMemorySearchAdapter } from "./in-memory-adapter.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Purge derived search documents for an erased PII subject
|
|
1
|
+
// Purge derived search documents for an erased PII subject.
|
|
2
2
|
//
|
|
3
3
|
// After kms.eraseKey the projection/event ciphertext is unreadable, but Meili
|
|
4
4
|
// still holds the plaintext that createSearchEventConsumer decrypted into the
|
|
@@ -152,7 +152,7 @@ subject) {
|
|
|
152
152
|
if (declaredFields.length === 0)
|
|
153
153
|
continue;
|
|
154
154
|
const tableName = resolveTableName(entityName, entity, undefined);
|
|
155
|
-
// Same post-eraseKey hazard as nullBlindIndexesForSubject
|
|
155
|
+
// Same post-eraseKey hazard as nullBlindIndexesForSubject:
|
|
156
156
|
// a mounted feature without its migration must not abort the purge
|
|
157
157
|
// (and the audit event after it).
|
|
158
158
|
if (!(await tableExists(db, tableName)))
|
|
@@ -178,13 +178,12 @@ subject) {
|
|
|
178
178
|
for (const [tenantId, items] of byTenant) {
|
|
179
179
|
if (items.length === 0)
|
|
180
180
|
continue;
|
|
181
|
-
const tid = tenantId;
|
|
182
181
|
if (search.removeBatch) {
|
|
183
|
-
await search.removeBatch(
|
|
182
|
+
await search.removeBatch(tenantId, items);
|
|
184
183
|
}
|
|
185
184
|
else {
|
|
186
185
|
for (const item of items) {
|
|
187
|
-
await search.remove(
|
|
186
|
+
await search.remove(tenantId, item.entityType, item.entityId);
|
|
188
187
|
}
|
|
189
188
|
}
|
|
190
189
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// One-time backfill for entities that only got `searchable: true` after rows
|
|
2
|
-
// already existed
|
|
2
|
+
// already existed. buildSearchDocument() only runs on the write path
|
|
3
3
|
// (createSearchEventConsumer, system-hooks.ts) — pre-existing rows never
|
|
4
4
|
// pass through it, so they stay unfindable until their next write. This
|
|
5
5
|
// re-derives a SearchDocument straight from the read-table row for every
|
|
@@ -47,12 +47,8 @@ async function scanAndIndexEntity(db, registry, searchAdapter, entityName, tenan
|
|
|
47
47
|
// calls searchAdapter.remove()) — resurrecting them here would make
|
|
48
48
|
// erased entities findable again.
|
|
49
49
|
const deletedFilter = entity.softDelete === true ? `AND ${quoteIdent("is_deleted")} IS NOT TRUE` : "";
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
indexedRows: 0,
|
|
53
|
-
wouldIndexRows: 0,
|
|
54
|
-
failures: [],
|
|
55
|
-
};
|
|
50
|
+
const failures = [];
|
|
51
|
+
const result = { scannedRows: 0, indexedRows: 0, wouldIndexRows: 0, failures };
|
|
56
52
|
let lastId;
|
|
57
53
|
// Whether a searchable field lacks a matching read-table column depends
|
|
58
54
|
// only on the table's column set (rowToState/Object.hasOwn), not on any
|
|
@@ -69,11 +65,12 @@ async function scanAndIndexEntity(db, registry, searchAdapter, entityName, tenan
|
|
|
69
65
|
WHERE ${quoteIdent("tenant_id")} = $1 ${deletedFilter} ${afterLastId}
|
|
70
66
|
ORDER BY ${quoteIdent("id")} ASC
|
|
71
67
|
LIMIT $2`, lastId === undefined ? [tenantId, batchSize] : [tenantId, batchSize, lastId]);
|
|
72
|
-
|
|
68
|
+
const [firstRow] = rows;
|
|
69
|
+
if (firstRow === undefined)
|
|
73
70
|
break;
|
|
74
71
|
if (uncheckedSchema) {
|
|
75
72
|
uncheckedSchema = false;
|
|
76
|
-
const sampleState = rowToState(
|
|
73
|
+
const sampleState = rowToState(firstRow, fieldNames);
|
|
77
74
|
const unmappedField = searchableFields.find((fieldName) => !(fieldName in sampleState));
|
|
78
75
|
if (unmappedField !== undefined) {
|
|
79
76
|
throw new Error(`reindexEntity: searchable field "${unmappedField}" is not mappable from read-table ` +
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Warn once at server startup when the process time zone is not UTC.
|
|
3
|
+
* Parameters are injectable for tests. Returns whether a warning was emitted.
|
|
4
4
|
*/
|
|
5
5
|
export declare function warnIfNonUtcServerTimeZone(resolvedTimeZone?: string, warn?: (message: string) => void): boolean;
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
1
|
+
// Warn at boot when the process time zone is not UTC. All server code assumes UTC
|
|
2
|
+
// (the system time zone is not a framework concept); a different process time zone
|
|
3
|
+
// can make accidental new Date() paths behave differently locally ("green in UTC-CI,
|
|
4
|
+
// broken in Berlin production"). This is a soft check: non-UTC is legitimate in
|
|
5
|
+
// development, so warn instead of failing boot.
|
|
6
6
|
function resolvedServerTimeZone() {
|
|
7
7
|
return Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
8
8
|
}
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
10
|
+
* Warn once at server startup when the process time zone is not UTC.
|
|
11
|
+
* Parameters are injectable for tests. Returns whether a warning was emitted.
|
|
12
12
|
*/
|
|
13
13
|
export function warnIfNonUtcServerTimeZone(resolvedTimeZone = resolvedServerTimeZone(),
|
|
14
|
-
// biome-ignore lint/suspicious/noConsole:
|
|
14
|
+
// biome-ignore lint/suspicious/noConsole: runs before any logger exists; the default is the only console sink
|
|
15
15
|
warn = console.warn) {
|
|
16
16
|
// GMT and Etc/UTC are UTC-equivalent (no offset, no DST) — TZ=GMT is a
|
|
17
17
|
// legitimate way to pin a process to UTC and must not trip this warning.
|
package/dist/time/iana.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
// IANA
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
// ("europe/berlin"
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
1
|
+
// IANA time zone name validation. Intl.supportedValuesOf("timeZone") returns only
|
|
2
|
+
// CANONICAL names — valid aliases such as "US/Pacific", "GMT", and "Etc/UTC" are
|
|
3
|
+
// missing, even though Intl.DateTimeFormat, Temporal, and ctx.tz.parse all
|
|
4
|
+
// accept them without issue. Intl.DateTimeFormat itself is case-INSENSITIVE
|
|
5
|
+
// ("europe/berlin" resolves to "Europe/Berlin") — a simple try/catch would
|
|
6
|
+
// therefore allow capitalization errors through.
|
|
7
|
+
// The exact comparison with resolvedOptions().timeZone prevents this: a valid
|
|
8
|
+
// canonical name OR alias ALWAYS resolves to itself (verified empirically for
|
|
9
|
+
// US/Pacific, GMT, and Etc/UTC), while a incorrectly cased string resolves to
|
|
10
|
+
// the canonical form and no longer matches exactly.
|
|
11
11
|
export function isValidIanaTimeZone(value) {
|
|
12
12
|
try {
|
|
13
13
|
return (new Intl.DateTimeFormat(undefined, { timeZone: value }).resolvedOptions().timeZone === value);
|
package/dist/time/index.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
// Time
|
|
1
|
+
// Time module: Temporal polyfill + ctx.tz helper.
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
// - ensureTemporalPolyfill:
|
|
5
|
-
// - getTemporal: type-
|
|
6
|
-
// - createTzContext: ctx.tz
|
|
7
|
-
// - LocatedTimestampJson: API-
|
|
8
|
-
// - isValidIanaTimeZone: IANA-
|
|
9
|
-
// - warnIfNonUtcServerTimeZone:
|
|
10
|
-
// - GeoTzProvider:
|
|
3
|
+
// Provides:
|
|
4
|
+
// - ensureTemporalPolyfill: installs Temporal globally when needed
|
|
5
|
+
// - getTemporal: type-safe access to globalThis.Temporal
|
|
6
|
+
// - createTzContext: ctx.tz factory with now/today/parse/toLocatedJson/...
|
|
7
|
+
// - LocatedTimestampJson: API-boundary form { at, tz }
|
|
8
|
+
// - isValidIanaTimeZone: IANA time-zone name validation (type:"tz" fields)
|
|
9
|
+
// - warnIfNonUtcServerTimeZone: boot warning for a non-UTC process time zone
|
|
10
|
+
// - GeoTzProvider: optional geo-to-zone adapter (ctx.tz.fromCoordinates/fromAddress)
|
|
11
11
|
export { warnIfNonUtcServerTimeZone } from "./boot-tz-warning.js";
|
|
12
12
|
export { isValidIanaTimeZone } from "./iana.js";
|
|
13
13
|
export { instantToLegacyDate, legacyDateToInstant } from "./legacy-date.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
2
2
|
export declare function instantToLegacyDate(instant: Temporal.Instant): Date;
|
|
3
|
-
/**
|
|
4
|
-
* mailparser Date
|
|
3
|
+
/** Converts dates from foreign libraries (imapflow INTERNALDATE,
|
|
4
|
+
* mailparser Date headers) to Temporal.Instant. */
|
|
5
5
|
export declare function legacyDateToInstant(date: Date): Temporal.Instant;
|
package/dist/time/legacy-date.js
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
2
|
-
//
|
|
3
|
-
// search({since}), nodemailer
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
// no-date-api-Guard bleibt für Feature-Code scharf, die Konstruktion
|
|
7
|
-
// lebt hier im allowlisteten Time-Layer.
|
|
2
|
+
// Bridge to libraries that require JS `Date` (imapflow
|
|
3
|
+
// search({since}), nodemailer headers, ...). Feature code uses Temporal and
|
|
4
|
+
// converts only at foreign-API boundaries, keeping the no-date-api guard
|
|
5
|
+
// effective outside this allowlisted time layer.
|
|
8
6
|
export function instantToLegacyDate(instant) {
|
|
9
7
|
return new Date(instant.epochMilliseconds);
|
|
10
8
|
}
|
|
11
|
-
/**
|
|
12
|
-
* mailparser Date
|
|
9
|
+
/** Converts dates from foreign libraries (imapflow INTERNALDATE,
|
|
10
|
+
* mailparser Date headers) to Temporal.Instant. */
|
|
13
11
|
export function legacyDateToInstant(date) {
|
|
14
12
|
return Temporal.Instant.fromEpochMilliseconds(date.getTime());
|
|
15
13
|
}
|
package/dist/time/polyfill.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
//
|
|
9
9
|
// Idempotent: if `globalThis.Temporal` already exists the call is a no-op.
|
|
10
10
|
// The check is the live global — not a sticky module flag — so tests that
|
|
11
|
-
// delete the ambient global
|
|
11
|
+
// delete the ambient global still get it back on the next call.
|
|
12
12
|
// Re-install assigns the already-resolved module value: re-importing
|
|
13
13
|
// `temporal-polyfill` here would put a polyfill instance on the global while
|
|
14
14
|
// the rest of the code uses the native classes (instanceof mismatch).
|
|
@@ -3,7 +3,7 @@ export type { Clock, LocatedTimestampJson, TzContext, TzContextOptions, } from "
|
|
|
3
3
|
/**
|
|
4
4
|
* Factory: creates a TzContext for the current request.
|
|
5
5
|
* Uses the kumiko-types Temporal module export (not globalThis.Temporal) so
|
|
6
|
-
* buildHandlerContext stays ambient-free
|
|
6
|
+
* buildHandlerContext stays ambient-free.
|
|
7
7
|
*/
|
|
8
8
|
export declare function createTzContext(options?: TzContextOptions): TzContext;
|
|
9
9
|
/**
|
package/dist/time/tz-context.js
CHANGED
|
@@ -25,7 +25,7 @@ const SYSTEM_CLOCK = {
|
|
|
25
25
|
/**
|
|
26
26
|
* Factory: creates a TzContext for the current request.
|
|
27
27
|
* Uses the kumiko-types Temporal module export (not globalThis.Temporal) so
|
|
28
|
-
* buildHandlerContext stays ambient-free
|
|
28
|
+
* buildHandlerContext stays ambient-free.
|
|
29
29
|
*/
|
|
30
30
|
export function createTzContext(options = {}) {
|
|
31
31
|
const tenant = options.tenant ?? "UTC";
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
// Client-safe
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
// re-
|
|
1
|
+
// Client-safe schema types. Live in framework/ui-types rather than renderer
|
|
2
|
+
// so the server (createKumikoServer / buildAppSchema) can produce them
|
|
3
|
+
// without pulling in renderer as a dependency. Renderer and Renderer-Web
|
|
4
|
+
// re-export the same symbols, so consumers do not notice the move.
|
|
5
5
|
//
|
|
6
|
-
// Pattern:
|
|
7
|
-
// web)
|
|
8
|
-
//
|
|
6
|
+
// Pattern: types flow "downstream" (framework → renderer → renderer-
|
|
7
|
+
// web); runtime helpers (toAppSchema, isAppSchema) remain renderer-side
|
|
8
|
+
// because that is the layer that works with AppSchemas at runtime.
|
|
9
9
|
export {};
|
|
@@ -39,13 +39,13 @@ export const LIST_ROW_META_REFERENCES = {
|
|
|
39
39
|
// user:user — the convention QN is a SystemAdmin cross-tenant roster whose
|
|
40
40
|
// `tenants` join must not reach a TenantAdmin, so it stays SystemAdmin-only and
|
|
41
41
|
// the label lookup goes through the member directory instead: own-tenant
|
|
42
|
-
// members for an admin, every user for a SystemAdmin
|
|
42
|
+
// members for an admin, every user for a SystemAdmin.
|
|
43
43
|
//
|
|
44
44
|
// tenant:tenant — the convention QN, `tenant:query:tenant:list`, is a
|
|
45
45
|
// SystemAdmin-only entity-list handler, so a TenantAdmin got a 403 and every
|
|
46
46
|
// tenant cell fell back to the raw UUID. The label lookup goes through the
|
|
47
47
|
// tenant directory instead: the caller's own tenant for an admin, every
|
|
48
|
-
// tenant for a SystemAdmin
|
|
48
|
+
// tenant for a SystemAdmin.
|
|
49
49
|
export const REFERENCE_LOOKUP_SOURCES = {
|
|
50
50
|
"user:user": { queryQn: "tenant:query:member-directory", labelKey: "label" },
|
|
51
51
|
"tenant:tenant": { queryQn: "tenant:query:tenant-directory", labelKey: "label" },
|
|
@@ -56,10 +56,10 @@ export const REFERENCE_LOOKUP_SOURCES = {
|
|
|
56
56
|
// otherwise. Keyed by `${refFeature}:${refEntity}` so useReferenceLookup can
|
|
57
57
|
// consult this generically instead of any screen/hook hardcoding an entity
|
|
58
58
|
// name. Central so the label applies to every screen that references this
|
|
59
|
-
// entity, not just delivery-log
|
|
59
|
+
// entity, not just delivery-log.
|
|
60
60
|
export const SYSTEM_REFERENCE_LABELS = {
|
|
61
61
|
"tenant:tenant": { id: SYSTEM_TENANT_ID, labelKey: "kumiko.reference.system-tenant" },
|
|
62
|
-
// createdBy on a system write
|
|
62
|
+
// createdBy on a system write — SYSTEM_USER_ID is an alias for
|
|
63
63
|
// "no human caller", so read_users never holds a matching row.
|
|
64
64
|
"user:user": {
|
|
65
65
|
id: SYSTEM_USER_ID,
|
package/dist/upgrade-cli.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { existsSync, mkdirSync, readdirSync, readFileSync, realpathSync, statSync, writeFileSync, } from "node:fs";
|
|
3
3
|
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
4
|
+
import { propertyOf } from "@cosmicdrift/kumiko-types/type-guards";
|
|
4
5
|
import { getFlag, getStringFlag, parseArgs } from "./arg-parser.js";
|
|
5
6
|
import { compareVersions, filterEntriesAfter, parseFeatureChangelog, sortEntries, } from "./engine/index.js";
|
|
6
7
|
import { ensureTemporalPolyfill } from "./time/index.js";
|
|
@@ -96,7 +97,7 @@ export function findFeatureChangelogFiles(featuresDir) {
|
|
|
96
97
|
// Layout is detected per-package, not guessed from a naming convention:
|
|
97
98
|
// enterprise packages keep changes.json under src/, framework's
|
|
98
99
|
// bundled-features keep it flat. A name-prefix heuristic (e.g. "ai-*")
|
|
99
|
-
// silently drops packages that don't match it
|
|
100
|
+
// silently drops packages that don't match it.
|
|
100
101
|
const srcLayout = join(featuresDir, name, "src", "changes.json");
|
|
101
102
|
const flatLayout = join(featuresDir, name, "changes.json");
|
|
102
103
|
const changelogPath = existsSync(srcLayout) ? srcLayout : flatLayout;
|
|
@@ -167,7 +168,7 @@ export function findFeaturesDirs(cwd) {
|
|
|
167
168
|
// Enterprise repo: packages/<name>/src/changes.json or packages/<name>/changes.json.
|
|
168
169
|
// Detected by presence of changes.json, not a package-name prefix — a
|
|
169
170
|
// prefix heuristic silently stops matching once packages are renamed or a
|
|
170
|
-
// differently-named package is added
|
|
171
|
+
// differently-named package is added. Skipped inside the
|
|
171
172
|
// framework repo itself (packages/framework present): every package's own
|
|
172
173
|
// changes.json is already collected via findPackageChangelogFiles, and
|
|
173
174
|
// would otherwise get double-counted as one here.
|
|
@@ -202,7 +203,7 @@ export function findFeaturesDirs(cwd) {
|
|
|
202
203
|
// Consumer-facing codemod scripts ship inside the published
|
|
203
204
|
// @cosmicdrift/kumiko-framework package (src/scripts/codemod), not at the
|
|
204
205
|
// git repo root — only the installed package path exists in a consumer's
|
|
205
|
-
// node_modules after a plain npm/bun install
|
|
206
|
+
// node_modules after a plain npm/bun install.
|
|
206
207
|
export function findCodemodScriptsRoot(repoRoot) {
|
|
207
208
|
const local = join(repoRoot, "packages/framework/src");
|
|
208
209
|
if (existsSync(join(local, CODEMOD_SUBDIR))) {
|
|
@@ -213,7 +214,7 @@ export function findCodemodScriptsRoot(repoRoot) {
|
|
|
213
214
|
if (existsSync(pkgPath)) {
|
|
214
215
|
try {
|
|
215
216
|
const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
|
|
216
|
-
if (pkg
|
|
217
|
+
if (propertyOf(pkg, "name") === "@cosmicdrift/kumiko-framework")
|
|
217
218
|
return local;
|
|
218
219
|
// wrong name → fall through
|
|
219
220
|
}
|
|
@@ -242,7 +243,7 @@ export function findCodemodScriptsRoot(repoRoot) {
|
|
|
242
243
|
// absolute paths, symlinks pointing outward) or that isn't a real .ts file.
|
|
243
244
|
// First arg is the consumer repo root (same as findCodemodScriptsRoot) —
|
|
244
245
|
// not the scripts root — so out-of-tree callers keep compiling against the
|
|
245
|
-
// public upgrade-cli subpath without silently resolving to null
|
|
246
|
+
// public upgrade-cli subpath without silently resolving to null.
|
|
246
247
|
function resolveCodemodScriptAt(codemodScriptsRoot, codemodField) {
|
|
247
248
|
if (!codemodField)
|
|
248
249
|
return null;
|
|
@@ -417,7 +418,7 @@ function parseStoredMarker(raw) {
|
|
|
417
418
|
resolvedManual: dedupeManualById(resolvedManual),
|
|
418
419
|
};
|
|
419
420
|
}
|
|
420
|
-
// A missing marker is a bootstrap app that never ran `--apply
|
|
421
|
+
// A missing marker is a bootstrap app that never ran `--apply`; an
|
|
421
422
|
// existing but unreadable one must not silently fall back to the installed
|
|
422
423
|
// version, or a bare `--apply` would skip every codemod in between.
|
|
423
424
|
function readMarker(targetDir) {
|
|
@@ -512,7 +513,7 @@ function writePartialMarker(out, targetDir, pending, manualEntries, failedVersio
|
|
|
512
513
|
// failure; codemods that already ran are kept in a partial marker. Writes the
|
|
513
514
|
// marker whenever dryRun is false —
|
|
514
515
|
// even with zero pending entries, so an already-current app still gets a
|
|
515
|
-
// bootstrap marker recording its installed version
|
|
516
|
+
// bootstrap marker recording its installed version.
|
|
516
517
|
// kumiko-lint-ignore complexity-budget sequential codemod runner with zero-pending bootstrap marker
|
|
517
518
|
async function applyCodemods(out, pending, repoRoot, targetDir, dryRun,
|
|
518
519
|
// Installed version for the zero-pending bootstrap marker — never a
|
|
@@ -751,8 +752,8 @@ export async function runUpgradeCli(argv, cwd, out, options) {
|
|
|
751
752
|
const installedVersion = readCurrentVersion(cwd);
|
|
752
753
|
// Filter baseline: explicit --from wins, otherwise the marker recorded
|
|
753
754
|
// under targetDir (what was last actually applied there), falling back to
|
|
754
|
-
// the installed version only when no marker exists yet (bootstrap
|
|
755
|
-
//
|
|
755
|
+
// the installed version only when no marker exists yet (bootstrap).
|
|
756
|
+
// Baselining on installedVersion here would silently treat every
|
|
756
757
|
// changelog entry up to the installed version as already handled, even
|
|
757
758
|
// when the marker says otherwise — a bare `--apply` would then always
|
|
758
759
|
// report "Nothing new" and bootstrap the marker onto the installed
|
package/dist/utils/compare.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Locale-independent string ordering for sorts that feed byte-exact serialized
|
|
2
2
|
// artifacts (manifest JSON, snapshot.json, generated migration SQL). Unlike
|
|
3
3
|
// String.localeCompare — whose order depends on the runner's ICU locale and so
|
|
4
|
-
// can drift between a macOS dev box and Linux CI
|
|
4
|
+
// can drift between a macOS dev box and Linux CI — this compares by
|
|
5
5
|
// UTF-16 code unit, which is stable across machines and, for the BMP identifier
|
|
6
6
|
// strings we sort here (table / column / feature / qualified names), equals
|
|
7
7
|
// codepoint order.
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-framework",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.387.0",
|
|
4
4
|
"description": "Framework core — engine, pipeline, API, DB, and every other bit that makes Kumiko go.",
|
|
5
|
-
"license": "
|
|
5
|
+
"license": "MIT",
|
|
6
6
|
"author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
@@ -199,7 +199,7 @@
|
|
|
199
199
|
"./package.json": "./package.json"
|
|
200
200
|
},
|
|
201
201
|
"dependencies": {
|
|
202
|
-
"@cosmicdrift/kumiko-types": "0.
|
|
202
|
+
"@cosmicdrift/kumiko-types": "0.387.0",
|
|
203
203
|
"bullmq": "^5.76.7",
|
|
204
204
|
"bun-types": "^1.3.13",
|
|
205
205
|
"hono": "^4.13.1",
|
|
@@ -215,7 +215,7 @@
|
|
|
215
215
|
"zod": "^4.4.3"
|
|
216
216
|
},
|
|
217
217
|
"devDependencies": {
|
|
218
|
-
"@cosmicdrift/kumiko-testing": "0.
|
|
218
|
+
"@cosmicdrift/kumiko-testing": "0.387.0",
|
|
219
219
|
"bun-types": "^1.3.13",
|
|
220
220
|
"pino-pretty": "^13.1.3"
|
|
221
221
|
},
|