@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/es-ops/runner.js
CHANGED
|
@@ -1,24 +1,23 @@
|
|
|
1
|
-
// Runner
|
|
1
|
+
// Runner for pending seed migrations during boot.
|
|
2
2
|
//
|
|
3
3
|
// Flow:
|
|
4
|
-
// 1. List seeds/<*.ts> sorted ascending (filename =
|
|
4
|
+
// 1. List seeds/<*.ts> sorted ascending (filename = chronological ID)
|
|
5
5
|
// 2. SELECT id FROM kumiko_es_operations WHERE operation_type='seed-migration'
|
|
6
|
-
// 3. pending = files
|
|
7
|
-
// 4.
|
|
8
|
-
// a. dynamic import → default
|
|
9
|
-
// b.
|
|
10
|
-
// →
|
|
11
|
-
// c.
|
|
6
|
+
// 3. pending = files \\ applied
|
|
7
|
+
// 4. For each pending migration in order:
|
|
8
|
+
// a. dynamic import → default export (SeedMigration)
|
|
9
|
+
// b. if migration.skippable && env[KUMIKO_SKIP_ES_OPS_<sanitized>]='1':
|
|
10
|
+
// → log line + continue (no marker written)
|
|
11
|
+
// c. Start transaction
|
|
12
12
|
// d. await migration.run(ctx)
|
|
13
|
-
// e. INSERT marker
|
|
14
|
-
// f.
|
|
15
|
-
// 5. On any failure:
|
|
16
|
-
// → App
|
|
13
|
+
// e. INSERT marker with duration_ms + appliedBy
|
|
14
|
+
// f. Commit transaction
|
|
15
|
+
// 5. On any failure: transaction rollback + error log + throw
|
|
16
|
+
// → App boot aborts. The operator must fix the failure and retry.
|
|
17
17
|
//
|
|
18
|
-
//
|
|
19
|
-
// `KUMIKO_SKIP_ES_OPS_<id>=1` env
|
|
20
|
-
//
|
|
21
|
-
// Standard-Workflow — wirklich Notfall.
|
|
18
|
+
// Emergency skip pattern: seed.skippable=true permits a
|
|
19
|
+
// `KUMIKO_SKIP_ES_OPS_<id>=1` env flag to skip a broken migration
|
|
20
|
+
// without touching its code. NOT a standard workflow — emergency use only.
|
|
22
21
|
var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
|
|
23
22
|
if (typeof path === "string" && /^\.\.?\//.test(path)) {
|
|
24
23
|
return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
|
|
@@ -31,32 +30,30 @@ import { readdir, readFile } from "node:fs/promises";
|
|
|
31
30
|
import path from "node:path";
|
|
32
31
|
import { acquireEsOpsAdvisoryLock, esOperationExists } from "../db/queries/es-ops.js";
|
|
33
32
|
import { insertOne, selectMany } from "../db/query.js";
|
|
33
|
+
import { createFallbackLogger } from "../logging/utils.js";
|
|
34
34
|
import { esOperationsTable } from "./operations-schema.js";
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
console.log(line);
|
|
38
|
-
};
|
|
39
|
-
const LOG_PREFIX = "[es-ops/seed-migration]";
|
|
35
|
+
const seedMigrationLog = createFallbackLogger("es-ops/seed-migration");
|
|
36
|
+
const DEFAULT_LOGGER = (line) => seedMigrationLog.info(line);
|
|
40
37
|
export async function runPendingSeedMigrations(args) {
|
|
41
38
|
const log = args.logger ?? DEFAULT_LOGGER;
|
|
42
39
|
const onDisk = await listSeedFiles(args.seedsDir);
|
|
43
40
|
if (onDisk.length === 0) {
|
|
44
|
-
log(
|
|
41
|
+
log(`no seed files in ${args.seedsDir} — skipping`);
|
|
45
42
|
return { appliedIds: [], skippedIds: [] };
|
|
46
43
|
}
|
|
47
44
|
const applied = await loadAppliedIds(args.db);
|
|
48
45
|
const pending = onDisk.filter((entry) => !applied.has(entry.id));
|
|
49
46
|
if (pending.length === 0) {
|
|
50
|
-
log(`${
|
|
47
|
+
log(`${onDisk.length} on disk, all applied — nothing to do`);
|
|
51
48
|
return { appliedIds: [], skippedIds: [] };
|
|
52
49
|
}
|
|
53
|
-
log(`${
|
|
50
|
+
log(`${pending.length}/${onDisk.length} pending`);
|
|
54
51
|
const appliedIds = [];
|
|
55
52
|
const skippedIds = [];
|
|
56
|
-
// Dry-
|
|
57
|
-
//
|
|
58
|
-
// write
|
|
59
|
-
// "handler not found"
|
|
53
|
+
// Dry-run pass: before any migration, parse all handler QNs from the seed
|
|
54
|
+
// files and check them against the registry. Fail fast before the first
|
|
55
|
+
// write, with a clear error containing the file and QN instead of a runtime
|
|
56
|
+
// "handler not found" error in the middle of the migration flow.
|
|
60
57
|
if (args.registry !== undefined) {
|
|
61
58
|
const unknownQns = [];
|
|
62
59
|
for (const entry of pending) {
|
|
@@ -71,27 +68,26 @@ export async function runPendingSeedMigrations(args) {
|
|
|
71
68
|
const lines = unknownQns.map((u) => ` - ${u.id}: "${u.qn}" not registered`);
|
|
72
69
|
throw new Error(`[es-ops/seed-migration] dry-run found ${unknownQns.length} unknown handler-QN(s):\n${lines.join("\n")}\n Check spelling against your TenantHandlers/AuthHandlers constants (kebab-case after the colon).`);
|
|
73
70
|
}
|
|
74
|
-
log(
|
|
71
|
+
log(`dry-run ok — all referenced handler-QNs registered`);
|
|
75
72
|
}
|
|
76
73
|
for (const entry of pending) {
|
|
77
74
|
const migration = await loadSeedModule(entry.filePath);
|
|
78
75
|
const envFlag = `KUMIKO_SKIP_ES_OPS_${sanitizeForEnv(entry.id)}`;
|
|
79
76
|
if (migration.skippable === true && process.env[envFlag] === "1") {
|
|
80
|
-
log(
|
|
77
|
+
log(`skip "${entry.id}" — ${envFlag}=1`);
|
|
81
78
|
skippedIds.push(entry.id);
|
|
82
79
|
continue;
|
|
83
80
|
}
|
|
84
81
|
const start = Date.now();
|
|
85
82
|
try {
|
|
86
83
|
await args.db.begin(async (tx) => {
|
|
87
|
-
// Advisory
|
|
88
|
-
//
|
|
89
|
-
//
|
|
90
|
-
//
|
|
91
|
-
// automatisch released (xact-scope).
|
|
84
|
+
// Advisory lock: serializes multi-replica boots. The second pod blocks until
|
|
85
|
+
// the first finishes, then rechecks its applied set in the next
|
|
86
|
+
// iteration and finds the marker, so it skips the migration. The lock
|
|
87
|
+
// is released automatically when the transaction commits (xact scope).
|
|
92
88
|
await acquireEsOpsAdvisoryLock(tx);
|
|
93
89
|
if (await esOperationExists(tx, entry.id)) {
|
|
94
|
-
log(
|
|
90
|
+
log(`race-skip "${entry.id}" — applied by parallel boot`);
|
|
95
91
|
// skip: parallel boot won the advisory-lock and already applied
|
|
96
92
|
// this migration. Nothing more to do in this tx.
|
|
97
93
|
return;
|
|
@@ -107,13 +103,13 @@ export async function runPendingSeedMigrations(args) {
|
|
|
107
103
|
});
|
|
108
104
|
});
|
|
109
105
|
const elapsed = Date.now() - start;
|
|
110
|
-
log(
|
|
106
|
+
log(`✓ ${entry.id} (${elapsed}ms) — ${migration.description}`);
|
|
111
107
|
appliedIds.push(entry.id);
|
|
112
108
|
}
|
|
113
109
|
catch (err) {
|
|
114
110
|
const elapsed = Date.now() - start;
|
|
115
|
-
log(
|
|
116
|
-
log(
|
|
111
|
+
log(`✗ ${entry.id} (${elapsed}ms) — ${stringifyError(err)}`);
|
|
112
|
+
log(`ABORT — ${pending.length - appliedIds.length - skippedIds.length - 1} pending migrations were NOT attempted`);
|
|
117
113
|
throw err;
|
|
118
114
|
}
|
|
119
115
|
}
|
|
@@ -125,8 +121,8 @@ async function listSeedFiles(seedsDir) {
|
|
|
125
121
|
entries = await readdir(seedsDir);
|
|
126
122
|
}
|
|
127
123
|
catch {
|
|
128
|
-
//
|
|
129
|
-
//
|
|
124
|
+
// A missing directory is treated as empty. An app without a seeds directory
|
|
125
|
+
// is a valid no-op state.
|
|
130
126
|
return [];
|
|
131
127
|
}
|
|
132
128
|
return entries
|
|
@@ -135,11 +131,11 @@ async function listSeedFiles(seedsDir) {
|
|
|
135
131
|
.sort() // filename = chronologische ID (date-prefix-convention)
|
|
136
132
|
.map((name) => ({
|
|
137
133
|
id: name.replace(/\.(ts|mts|js)$/, ""),
|
|
138
|
-
//
|
|
139
|
-
//
|
|
140
|
-
//
|
|
141
|
-
//
|
|
142
|
-
// "Cannot find module 'seeds/...' from '<runner-path>'".
|
|
134
|
+
// Resolve rather than join: Bun's await import() requires absolute paths.
|
|
135
|
+
// If seedsDir is relative (for example "./seeds" from a runProdApp
|
|
136
|
+
// option), the joined path would also be relative. Bun's import resolver
|
|
137
|
+
// would resolve it relative to runner.ts rather than process.cwd(),
|
|
138
|
+
// causing "Cannot find module 'seeds/...' from '<runner-path>'".
|
|
143
139
|
filePath: path.resolve(seedsDir, name),
|
|
144
140
|
}));
|
|
145
141
|
}
|
|
@@ -171,16 +167,16 @@ function isSeedMigration(value) {
|
|
|
171
167
|
function sanitizeForEnv(id) {
|
|
172
168
|
return id.toUpperCase().replace(/[^A-Z0-9]+/g, "_");
|
|
173
169
|
}
|
|
174
|
-
// Parse seed-file source
|
|
175
|
-
//
|
|
170
|
+
// Parse seed-file source and extract handler QNs from `systemWriteAs(...)`
|
|
171
|
+
// calls. A regex (rather than an AST) handles common inline string cases:
|
|
176
172
|
// ctx.systemWriteAs("foo:write:bar", payload)
|
|
177
173
|
// systemWriteAs("foo:write:bar", ...) (destructured)
|
|
178
174
|
//
|
|
179
|
-
//
|
|
180
|
-
// - QN
|
|
181
|
-
// - String
|
|
182
|
-
//
|
|
183
|
-
//
|
|
175
|
+
// Unsupported cases:
|
|
176
|
+
// - QN from a variable: `const qn = "..."; ctx.systemWriteAs(qn, ...)`
|
|
177
|
+
// - String concatenation or template literals with dynamic variables
|
|
178
|
+
// These patterns are uncommon in real seed migrations. This limitation is
|
|
179
|
+
// documented so callers requiring dynamic QNs understand the constraint.
|
|
184
180
|
function extractWriteHandlerQns(source) {
|
|
185
181
|
const pattern = /systemWriteAs\s*\(\s*["']([^"']+)["']/g;
|
|
186
182
|
const out = new Set();
|
package/dist/es-ops/types.d.ts
CHANGED
|
@@ -3,49 +3,50 @@ import type { TenantId, WriteResult } from "../engine/index.ts";
|
|
|
3
3
|
export type EsOperationAppliedBy = "boot" | "cli" | "ci-pipeline";
|
|
4
4
|
/** Default-Export einer seed-Migration-File. */
|
|
5
5
|
export type SeedMigration = {
|
|
6
|
-
/**
|
|
7
|
-
*
|
|
6
|
+
/** A short description of what the migration does. Shown in
|
|
7
|
+
* kumiko_es_operations.notes and in the `ops seed:status` output. */
|
|
8
8
|
readonly description: string;
|
|
9
|
-
/** Optional:
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
/** Optional: skip this seed when the env var
|
|
10
|
+
* `KUMIKO_SKIP_ES_OPS_<sanitized-filename>=1` is set (for recovery or
|
|
11
|
+
* debug boots). Default false = always-run-pending. */
|
|
12
12
|
readonly skippable?: boolean;
|
|
13
|
-
/**
|
|
14
|
-
*
|
|
13
|
+
/** Main migration work. ctx provides systemWriteAs (the Event Store-compliant
|
|
14
|
+
* path, bypassing access checks through the system user) plus read helpers.
|
|
15
15
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
16
|
+
* If this throws, the marker is NOT written, app boot aborts, and the
|
|
17
|
+
* migration is retried on the next boot. Each migration has its own
|
|
18
|
+
* transaction; a failure stops all subsequent pending migrations, preserving
|
|
19
|
+
* their order. */
|
|
19
20
|
readonly run: (ctx: SeedMigrationContext) => Promise<void>;
|
|
20
21
|
};
|
|
21
|
-
/** Read
|
|
22
|
-
*
|
|
23
|
-
*
|
|
22
|
+
/** Read shape of a user entry exposed to seed helpers.
|
|
23
|
+
* A narrow subset of AuthUserRow, since seeds typically need only
|
|
24
|
+
* these fields for identification. */
|
|
24
25
|
export type SeedUserRow = {
|
|
25
26
|
readonly id: string;
|
|
26
27
|
readonly email: string;
|
|
27
28
|
readonly tenantId: string;
|
|
28
29
|
};
|
|
29
|
-
/** Read
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
30
|
+
/** Read shape of a membership entry exposed to seed helpers.
|
|
31
|
+
* Distinguishes two tenantIds: the logical one from the read projection
|
|
32
|
+
* (`tenantId`) and the physical one from the aggregate stream
|
|
33
|
+
* (`streamTenantId`). They differ when the aggregate was created by an
|
|
34
|
+
* executor with a different tenantId (for example,
|
|
35
|
+
* seedTenantMembership-by=systemAdmin), which is a typical
|
|
36
|
+
* publicstatus driver use case. */
|
|
36
37
|
export type SeedMembershipRow = {
|
|
37
38
|
readonly userId: string;
|
|
38
|
-
/** Payload
|
|
39
|
-
*
|
|
39
|
+
/** Payload tenant from `read_tenant_memberships.tenant_id`; it is used
|
|
40
|
+
* as `tenantId` in the write payload. */
|
|
40
41
|
readonly tenantId: string;
|
|
41
|
-
/** Stream
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
42
|
+
/** Stream tenant from `kumiko_events.tenant_id` in the v1 row. MUST be
|
|
43
|
+
* passed as `tenantIdOverride` to `systemWriteAs`; otherwise the Event
|
|
44
|
+
* Store executor looks for the stream in the wrong tenant and returns
|
|
45
|
+
* `version_conflict`. */
|
|
45
46
|
readonly streamTenantId: string;
|
|
46
47
|
readonly roles: readonly string[];
|
|
47
48
|
};
|
|
48
|
-
/** Read
|
|
49
|
+
/** Read shape of a tenant entry exposed to seed helpers. */
|
|
49
50
|
export type SeedTenantRow = {
|
|
50
51
|
readonly id: string;
|
|
51
52
|
readonly name: string;
|
|
@@ -69,34 +70,32 @@ export type SeedTemplateResourceFilter = {
|
|
|
69
70
|
readonly locale?: string;
|
|
70
71
|
};
|
|
71
72
|
export type SeedMigrationContext = {
|
|
72
|
-
/** Event
|
|
73
|
-
*
|
|
74
|
-
*
|
|
73
|
+
/** Event Store-compliant write through the existing write handler. The system
|
|
74
|
+
* user executes it while bypassing access checks. Events have
|
|
75
|
+
* inserted_by_id = SYSTEM_TENANT_ID-user, making them auditable.
|
|
75
76
|
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
77
|
+
* The type signature follows existing ctx.writeAs (with payload as unknown).
|
|
78
|
+
* Type safety comes from handler-specific wrappers in the caller; do not
|
|
79
|
+
* attempt generic magic.
|
|
79
80
|
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
* `feedback_event_store_tenant_consistency.md` + Driver-Use-Case
|
|
86
|
-
* publicstatus-admin-roles in `project_es_ops_phase1_retro.md`).
|
|
81
|
+
* **tenantIdOverride:** when the target aggregate lives in a specific
|
|
82
|
+
* tenant stream (rather than SYSTEM_TENANT_ID, the default), the caller
|
|
83
|
+
* MUST provide the stream tenantId. Otherwise the Event Store executor
|
|
84
|
+
* looks for the aggregate stream under `SYSTEM_TENANT_ID` and returns
|
|
85
|
+
* `version_conflict`.
|
|
87
86
|
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
87
|
+
* Typical patterns:
|
|
88
|
+
* - System-scope aggregates (config-values, system text-content): omit
|
|
89
|
+
* tenantIdOverride (default SYSTEM_TENANT_ID).
|
|
90
|
+
* - Tenant-scope aggregates (memberships, tenant-config, app-data): use
|
|
91
|
+
* `tenantIdOverride: m.tenantId` or the stream tenant from a find*
|
|
92
|
+
* helper.
|
|
94
93
|
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
94
|
+
* **extraRoles:** hasAccess has no system bypass. A handler with an
|
|
95
|
+
* explicit role gate (for example, `access: { roles: ["SystemAdmin"] }` or
|
|
96
|
+
* `["anonymous"]`) otherwise rejects the bare `system` actor with
|
|
97
|
+
* `access_denied`. Pass the required role(s) here; see
|
|
98
|
+
* `createSystemUser`'s `extraRoles` documentation. */
|
|
100
99
|
readonly systemWriteAs: (handlerQualifiedName: string, payload: unknown, tenantIdOverride?: TenantId, extraRoles?: readonly string[]) => Promise<WriteResult>;
|
|
101
100
|
readonly findUserByEmail: (email: string) => Promise<SeedUserRow | null>;
|
|
102
101
|
readonly findMembershipsOfUser: (userId: string) => Promise<readonly SeedMembershipRow[]>;
|
|
@@ -104,9 +103,10 @@ export type SeedMigrationContext = {
|
|
|
104
103
|
/** Template rows ordered by slug, locale. Empty list when the
|
|
105
104
|
* template-resolver feature (its table) is not present. */
|
|
106
105
|
readonly findTemplateResources: (filter?: SeedTemplateResourceFilter) => Promise<readonly SeedTemplateResourceRow[]>;
|
|
107
|
-
/** Escape
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
106
|
+
/** Escape hatch for direct database access. Use only for READ-only lookups
|
|
107
|
+
* that the context does not provide by default; WRITES must use
|
|
108
|
+
* systemWriteAs. The type is DbRunner (a connection or active transaction)
|
|
109
|
+
* because the runner creates the context within each migration's
|
|
110
|
+
* transaction scope. */
|
|
111
111
|
readonly db: DbRunner;
|
|
112
112
|
};
|
package/dist/es-ops/types.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
// Public API
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
// kumiko_es_operations
|
|
1
|
+
// Public API for seed migrations. Files in /seeds/<date>-<slug>.ts
|
|
2
|
+
// export a default object of this type. The runner calls `run(ctx)` in
|
|
3
|
+
// chronological order, once per app boot, tracking runs in
|
|
4
|
+
// kumiko_es_operations.
|
|
5
5
|
//
|
|
6
|
-
// API
|
|
7
|
-
// (findUserByEmail, etc.)
|
|
8
|
-
//
|
|
9
|
-
//
|
|
6
|
+
// API stability: the initial API is intentionally minimal. Context helpers
|
|
7
|
+
// (findUserByEmail, etc.) grow on demand: when a real migration needs a
|
|
8
|
+
// lookup the context does not provide, add it there. This avoids speculative
|
|
9
|
+
// helper APIs.
|
|
10
10
|
//
|
|
11
|
-
// ctx.db
|
|
12
|
-
//
|
|
13
|
-
// (
|
|
11
|
+
// ctx.db is permitted as an escape hatch for READ-only lookups. WRITES must
|
|
12
|
+
// always go through ctx.systemWriteAs so Event Store invariants remain intact
|
|
13
|
+
// (source of truth plus automatic projection).
|
|
14
14
|
export {};
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
/// <reference types="temporal-polyfill/global" preserve="true" />
|
|
2
|
-
// Event-
|
|
3
|
-
//
|
|
2
|
+
// Event-store admin API — bypasses Marten for legacy-data imports.
|
|
3
|
+
// Specification and behavior: docs/plans/features/event-store-admin-api.md
|
|
4
4
|
//
|
|
5
|
-
// Guard
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
// selbst, das Guard-Script selbst.
|
|
5
|
+
// Guard rail: this module is NOT re-exported from event-store/index.ts. Import only via the deep
|
|
6
|
+
// path `@cosmicdrift/kumiko-framework/event-store/admin-api`. The guard script
|
|
7
|
+
// `scripts/guard-admin-api.ts` blocks calls from application code. Allowlist: samples/*/migration/,
|
|
8
|
+
// scripts/migrations/, this definition, and the guard script itself.
|
|
10
9
|
import { constraintOf, isUniqueViolation } from "../db/pg-error.js";
|
|
11
10
|
import { claimXactId, runAtomicEventWrite } from "../db/queries/event-store.js";
|
|
12
11
|
import { eventPredecessorExists, findExistingEventVersion, insertRawEventBatch, insertRawFirstEvent, insertRawSubsequentEvent, } from "../db/queries/event-store-admin.js";
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// errors so the executor layer can map them to the framework's
|
|
4
4
|
// WriteResult error contract (version_conflict).
|
|
5
5
|
//
|
|
6
|
-
// These live here and not in kumiko-types
|
|
6
|
+
// These live here and not in kumiko-types: `instanceof` needs a
|
|
7
7
|
// single copy of the class, which forced kumiko-types to be a peerDependency
|
|
8
8
|
// and made every minor changeset resolve to a major bump.
|
|
9
9
|
export class VersionConflictError extends Error {
|
|
@@ -35,7 +35,7 @@ import { currentSeedCreatedAt } from "./seed-clock.js";
|
|
|
35
35
|
// by TCP round-trip instead of pollIntervalMs.
|
|
36
36
|
export const EVENTS_PUBSUB_CHANNEL = "kumiko_events_new";
|
|
37
37
|
export async function append(db, event) {
|
|
38
|
-
// Event-PII
|
|
38
|
+
// Event-PII: stored payload AND returned echo carry ciphertext, so
|
|
39
39
|
// inline projections and rebuilds materialize identical rows.
|
|
40
40
|
const payload = await encryptEventPayloadPii(event.type, event.payload, {
|
|
41
41
|
tenantId: event.tenantId,
|
|
@@ -80,7 +80,7 @@ export async function append(db, event) {
|
|
|
80
80
|
throw e;
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
|
-
//
|
|
83
|
+
// Attribution is derived from the execution scope, never taken from
|
|
84
84
|
// the caller: a passed-in value can lie about who wrote the row, a derived
|
|
85
85
|
// one cannot. Hence overwrite rather than merge. appendRaw/appendRawBatch
|
|
86
86
|
// bypass this deliberately — they replay historical rows verbatim.
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// whose (possibly years-old) payload makes its apply throw rolls the whole
|
|
6
6
|
// replay back — the projection stays permanently un-rebuildable until the
|
|
7
7
|
// event is repaired by hand. The upcaster dead-letter only quarantines
|
|
8
|
-
// upcast-TRANSFORM failures, not apply failures
|
|
8
|
+
// upcast-TRANSFORM failures, not apply failures.
|
|
9
9
|
//
|
|
10
10
|
// Quarantine mode (errorPolicy.skipApplyErrors on RebuildDeps, or
|
|
11
11
|
// MspErrorMode.rebuild.skipApplyErrors on the MSP definition) confines each
|
package/dist/files/feature.js
CHANGED
|
@@ -4,17 +4,16 @@ import { FILE_REF_ENTITY_NAME, fileRefEntity } from "./file-ref-entity.js";
|
|
|
4
4
|
import { DEFAULT_UPLOAD_SWEEP_GRACE_HOURS, MIN_UPLOAD_SWEEP_GRACE_HOURS, sweepOrphanedUploads, } from "./sweep-orphaned-uploads.js";
|
|
5
5
|
import { fileUploadLedgerTable } from "./upload-ledger.js";
|
|
6
6
|
export { fileRefEntity } from "./file-ref-entity.js";
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
// `fileRef.
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
// file-spezifische Lösch-Logik.
|
|
7
|
+
// `fileRef` is a regular ES entity. Uploads and deletes go through the standard
|
|
8
|
+
// entity executor (file-routes.ts: executor.create/delete → `fileRef.created` /
|
|
9
|
+
// `fileRef.deleted` → applyEntityEvent materializes `file_refs`), so soft-delete,
|
|
10
|
+
// anonymization, restore and retention come generically from the entity lifecycle
|
|
11
|
+
// and data-retention, with no file-specific deletion logic.
|
|
13
12
|
//
|
|
14
|
-
// `r.entity`
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
13
|
+
// `r.entity` exposes the table to PII boot validation and the userData/tenantData
|
|
14
|
+
// extensions, and registers the implicit entity projection used by
|
|
15
|
+
// rebuildProjection. It lives in the framework next to file-routes and
|
|
16
|
+
// fileRefsTable; bundled-features/files only re-exports it.
|
|
18
17
|
export const FILES_FEATURE = "files";
|
|
19
18
|
function assertValidSweepGraceHours(hours) {
|
|
20
19
|
if (!Number.isInteger(hours) || hours < MIN_UPLOAD_SWEEP_GRACE_HOURS) {
|
|
@@ -121,12 +121,11 @@ export function deriveKey(key, suffix) {
|
|
|
121
121
|
const { stem, ext } = splitKeyExtension(key);
|
|
122
122
|
return ext === "" ? `${key}.${suffix}` : `${stem}.${suffix}${ext}`;
|
|
123
123
|
}
|
|
124
|
-
// The prefix that covers a key AND every `derive()`d variant of it —
|
|
125
|
-
//
|
|
126
|
-
//
|
|
127
|
-
//
|
|
128
|
-
//
|
|
129
|
-
// none of which have their own `file_refs` row.
|
|
124
|
+
// The prefix that covers a key AND every `derive()`d variant of it — `foo/bar.jpg` → `foo/bar.`,
|
|
125
|
+
// matching `foo/bar.jpg` and `foo/bar.medium.jpg` alike, since derive() only ever inserts a suffix
|
|
126
|
+
// between the stem and the extension. Used by the tenant-handover destroy sweep to reach a moved
|
|
127
|
+
// fileRef's original plus every already-rendered derivative, none of which have their own
|
|
128
|
+
// `file_refs` row.
|
|
130
129
|
export function storageKeyStemPrefix(key) {
|
|
131
130
|
const { stem, ext } = splitKeyExtension(key);
|
|
132
131
|
return ext === "" ? `${key}.` : `${stem}.`;
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
import { createBigIntField, createEntity, createTextField } from "../engine/index.js";
|
|
2
|
-
// fileRef —
|
|
3
|
-
//
|
|
4
|
-
//
|
|
2
|
+
// fileRef — the file-metadata entity. A standard ES entity: uploads/deletes
|
|
3
|
+
// run through the standard executor (file-routes.ts), and the `file_refs` table
|
|
4
|
+
// is built from this definition via buildEntityTable.
|
|
5
5
|
//
|
|
6
|
-
// softDelete: true —
|
|
7
|
-
//
|
|
8
|
-
//
|
|
6
|
+
// softDelete: true — as with the `user` entity and the data-retention strategies.
|
|
7
|
+
// A delete sets `isDeleted=true` (recoverable, not "gone immediately"); actual
|
|
8
|
+
// erasure (Art. 17) runs through the Forget hook and retention cleanup.
|
|
9
9
|
//
|
|
10
|
-
// `insertedAt`/`insertedById`
|
|
11
|
-
// buildBaseColumns in table-builder.ts)
|
|
12
|
-
//
|
|
13
|
-
//
|
|
10
|
+
// `insertedAt`/`insertedById` are framework-managed base columns (see
|
|
11
|
+
// buildBaseColumns in table-builder.ts) and must NOT be duplicated as entity
|
|
12
|
+
// fields — fieldColumns win during the merge, and the field variant without
|
|
13
|
+
// `.default(now()).notNull()` silently makes inserted_at nullable.
|
|
14
14
|
//
|
|
15
|
-
// PII
|
|
16
|
-
// - fileName → personal: "self" (original filename often carries a
|
|
17
|
-
// reference: "Marc-Lebenslauf.pdf", "Krankheitsattest-Mai.pdf").
|
|
18
|
-
// fields (storageKey, mimeType, entityType, entityId, fieldName)
|
|
19
|
-
// technical references or system metadata, not subject content.
|
|
15
|
+
// PII annotations:
|
|
16
|
+
// - fileName → personal: "self" (the original filename often carries a
|
|
17
|
+
// personal reference: "Marc-Lebenslauf.pdf", "Krankheitsattest-Mai.pdf").
|
|
18
|
+
// The other fields (storageKey, mimeType, entityType, entityId, fieldName)
|
|
19
|
+
// are technical references or system metadata, not subject content.
|
|
20
20
|
export { FILE_REF_ENTITY_NAME } from "./types.js";
|
|
21
21
|
export const fileRefEntity = createEntity({
|
|
22
22
|
table: "file_refs",
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { buildEntityTable } from "../db/table-builder.js";
|
|
2
2
|
import { FILE_REF_ENTITY_NAME, fileRefEntity } from "./file-ref-entity.js";
|
|
3
|
-
// `file_refs`
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
3
|
+
// `file_refs` is the read table for the `fileRef` entity. It is built from the entity
|
|
4
|
+
// definition rather than a manually maintained `pgTable`, so the implicit entity
|
|
5
|
+
// projection (rebuild) and live executor writes use the same columns and table—one
|
|
6
|
+
// source of truth, with no drift between duplicate definitions.
|
|
7
7
|
//
|
|
8
|
-
// `id`
|
|
9
|
-
//
|
|
10
|
-
// (
|
|
11
|
-
// auf /files/:id.
|
|
8
|
+
// `id` is a UUID and also serves as the aggregate ID for the `fileRef` event stream:
|
|
9
|
+
// each upload appends `fileRef.created`, and each deletion appends `fileRef.deleted`
|
|
10
|
+
// (standard entity auto-verbs). UUIDs prevent enumeration attacks against /files/:id.
|
|
12
11
|
export const fileRefsTable = buildEntityTable(FILE_REF_ENTITY_NAME, fileRefEntity);
|
|
@@ -1,9 +1,15 @@
|
|
|
1
|
+
import type { WriteErrorInfo } from "@cosmicdrift/kumiko-types/write-error-info-types";
|
|
1
2
|
import { Hono } from "hono";
|
|
2
3
|
import type { DbConnection } from "../db/connection.ts";
|
|
3
4
|
import { type Registry, type SessionUser, type TenantId } from "../engine/types/index.ts";
|
|
4
5
|
import type { FileProviderResolver } from "./provider-resolver.ts";
|
|
5
6
|
import type { RecordReadCheck } from "./record-read-check.ts";
|
|
6
7
|
import { type RecordWriteCheck } from "./record-write-check.ts";
|
|
8
|
+
declare const ERROR_STATUSES: readonly [400, 401, 403, 404, 409, 422, 429, 500, 503, 504];
|
|
9
|
+
export declare function deleteFailureResponseFor(error: WriteErrorInfo): {
|
|
10
|
+
readonly status: (typeof ERROR_STATUSES)[number];
|
|
11
|
+
readonly code: string;
|
|
12
|
+
};
|
|
7
13
|
export type FileAccessDecision = "allow" | "deny";
|
|
8
14
|
export type FileRef = {
|
|
9
15
|
id: string;
|
|
@@ -36,3 +42,4 @@ export type FileRoutesOptions = {
|
|
|
36
42
|
export declare function resolveMaxUploadBodyBytes(options: Pick<FileRoutesOptions, "registry" | "maxUploadSize">): number;
|
|
37
43
|
export declare function readFilesRouteOptions(registry: Registry): Pick<FileRoutesOptions, "accessGuard" | "privilegedRoles" | "unclaimedUploadReaders" | "maxUploadSize">;
|
|
38
44
|
export declare function createFileRoutes(options: FileRoutesOptions): Hono;
|
|
45
|
+
export {};
|