@cosmicdrift/kumiko-framework 0.306.0 → 0.308.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/package.json +4 -4
- package/src/__tests__/anonymous-access.integration.test.ts +1 -1
- package/src/__tests__/error-contract.integration.test.ts +1 -1
- package/src/__tests__/field-access.integration.test.ts +1 -1
- package/src/__tests__/fixtures/migrate-open-to-all/manual-only.expected.ts +1 -1
- package/src/__tests__/fixtures/migrate-open-to-all/manual-only.input.ts +1 -1
- package/src/__tests__/fixtures/migrate-open-to-all/test-simple.expected.ts +1 -1
- package/src/__tests__/fixtures/migrate-open-to-all/test-simple.input.ts +1 -1
- package/src/__tests__/full-stack.integration.test.ts +1 -1
- package/src/__tests__/transition-guard.integration.test.ts +1 -1
- package/src/api/__tests__/api.test.ts +1 -1
- package/src/api/__tests__/batch.integration.test.ts +1 -1
- package/src/api/__tests__/body-limit.test.ts +1 -1
- package/src/api/__tests__/dispatcher-live.integration.test.ts +1 -1
- package/src/api/__tests__/extra-routes.integration.test.ts +1 -1
- package/src/api/__tests__/http-route-rate-limit.integration.test.ts +1 -1
- package/src/api/__tests__/nested-write-ownership.integration.test.ts +1 -1
- package/src/api/__tests__/nested-write.integration.test.ts +1 -1
- package/src/api/__tests__/pii-leak-guard.integration.test.ts +1 -1
- package/src/api/__tests__/redis-sse-broker.integration.test.ts +133 -0
- package/src/api/__tests__/request-locale.integration.test.ts +1 -1
- package/src/api/__tests__/server-boot-guards.test.ts +1 -1
- package/src/api/__tests__/server-error-logging.test.ts +1 -1
- package/src/api/__tests__/sse-broker.test.ts +144 -1
- package/src/api/__tests__/stream-escape-hatch.integration.test.ts +1 -1
- package/src/api/auth-routes.ts +1 -1
- package/src/api/redis-sse-broker.ts +57 -3
- package/src/api/request-context.ts +24 -0
- package/src/api/server.ts +11 -0
- package/src/api/sse-broker.ts +69 -12
- package/src/changes.json +63 -0
- package/src/compliance/override-schema.ts +1 -1
- package/src/crypto/__tests__/event-pii.test.ts +1 -1
- package/src/crypto/kms-adapter.ts +1 -1
- package/src/db/__tests__/tenant-db-where-merge.integration.test.ts +1 -1
- package/src/db/queries/event-consumer.ts +155 -9
- package/src/db/queries/event-store.ts +69 -0
- package/src/db/tenant-db.ts +43 -5
- package/src/derivatives/__tests__/derivatives-context.integration.test.ts +1 -1
- package/src/engine/__tests__/boot-validator-action-wiring.test.ts +1 -1
- package/src/engine/__tests__/boot-validator-dashboard.test.ts +1 -1
- package/src/engine/__tests__/boot-validator-nav-area.test.ts +1 -1
- package/src/engine/__tests__/boot-validator-pii-retention.test.ts +1 -1
- package/src/engine/__tests__/boot-validator-projection-list.test.ts +1 -1
- package/src/engine/__tests__/boot-validator-query-output-schema.test.ts +1 -1
- package/src/engine/__tests__/boot-validator-query-refs.test.ts +1 -1
- package/src/engine/__tests__/boot-validator-ref-entity.test.ts +1 -1
- package/src/engine/__tests__/boot-validator-s0-integration.test.ts +1 -1
- package/src/engine/__tests__/boot-validator.test.ts +35 -1
- package/src/engine/__tests__/build-app-schema.test.ts +1 -1
- package/src/engine/__tests__/build-config-feature-schema-secrets.test.ts +1 -1
- package/src/engine/__tests__/define-feature-entity-mapping.test.ts +1 -1
- package/src/engine/__tests__/engine.test.ts +1 -1
- package/src/engine/__tests__/event-helpers.test.ts +1 -1
- package/src/engine/__tests__/event-migration-declarative.test.ts +1 -1
- package/src/engine/__tests__/extend-entity-projection.test.ts +1 -1
- package/src/engine/__tests__/extends-registrar.test.ts +1 -1
- package/src/engine/__tests__/hook-phases.test.ts +1 -1
- package/src/engine/__tests__/lifecycle-hooks.test.ts +1 -1
- package/src/engine/__tests__/pipeline-engine.test.ts +1 -1
- package/src/engine/__tests__/pipeline-handler.integration.test.ts +1 -1
- package/src/engine/__tests__/pipeline-observability.integration.test.ts +1 -1
- package/src/engine/__tests__/pipeline-performance.integration.test.ts +1 -1
- package/src/engine/__tests__/pipeline-sub-pipelines.test.ts +1 -1
- package/src/engine/__tests__/post-query-hook.test.ts +1 -1
- package/src/engine/__tests__/projection-detail-actions.test.ts +1 -1
- package/src/engine/__tests__/projection-detail-singleton.test.ts +1 -1
- package/src/engine/__tests__/projection-detail-tabs.test.ts +1 -1
- package/src/engine/__tests__/read-steps-tenant-filter.integration.test.ts +1 -1
- package/src/engine/__tests__/registry.test.ts +1 -1
- package/src/engine/__tests__/screen.test.ts +1 -1
- package/src/engine/__tests__/validate-projection-allowlist.test.ts +1 -1
- package/src/engine/boot-validator/__tests__/access-declarations.test.ts +1 -1
- package/src/engine/boot-validator/__tests__/action-icon.test.ts +81 -0
- package/src/engine/boot-validator/__tests__/anonymous-rate-limit-required.test.ts +1 -1
- package/src/engine/boot-validator/__tests__/no-all-role-in-handler-access.test.ts +1 -1
- package/src/engine/boot-validator/__tests__/parent-ref.test.ts +1 -1
- package/src/engine/boot-validator/__tests__/rate-limit-disabled-reason.test.ts +1 -1
- package/src/engine/boot-validator/screens.ts +83 -0
- package/src/engine/define-handler.ts +2 -1
- package/src/engine/entity-handlers.ts +2 -1
- package/src/engine/feature-ast/__tests__/pattern-change-schema.test.ts +1 -1
- package/src/engine/feature-ast/pattern-change-schema.ts +1 -1
- package/src/engine/feature-builder-state.ts +2 -1
- package/src/engine/feature-config-events-jobs.ts +2 -1
- package/src/engine/feature-entity-handlers.ts +2 -1
- package/src/engine/schema-builder.ts +1 -1
- package/src/entrypoint/__tests__/entrypoint-attach-dispatcher.integration.test.ts +1 -1
- package/src/entrypoint/__tests__/entrypoint-job-wiring.integration.test.ts +1 -1
- package/src/entrypoint/__tests__/split-deploy.integration.test.ts +1 -1
- package/src/env/__tests__/compose-env-schema.test.ts +1 -1
- package/src/env/__tests__/dry-run.test.ts +1 -1
- package/src/env/__tests__/parse-env-dry-run.test.ts +1 -1
- package/src/env/_zod-introspect.ts +1 -1
- package/src/env/dry-run.ts +1 -1
- package/src/env/index.ts +1 -1
- package/src/errors/__tests__/classes.test.ts +1 -1
- package/src/es-ops/__tests__/runner.integration.test.ts +1 -1
- package/src/event-store/__tests__/backfill-pii.integration.test.ts +1 -1
- package/src/event-store/__tests__/event-attribution.integration.test.ts +54 -4
- package/src/event-store/__tests__/upcaster.integration.test.ts +1 -1
- package/src/event-store/admin-api.ts +5 -0
- package/src/event-store/event-store.ts +16 -7
- package/src/jobs/__tests__/job-backoff.integration.test.ts +1 -1
- package/src/jobs/__tests__/job-event-trigger.integration.test.ts +1 -1
- package/src/jobs/__tests__/job-multi-trigger.integration.test.ts +1 -1
- package/src/jobs/__tests__/job-public-intake-origin.integration.test.ts +536 -0
- package/src/jobs/__tests__/job-retention.integration.test.ts +1 -1
- package/src/jobs/__tests__/job-write-as.integration.test.ts +1 -1
- package/src/jobs/__tests__/jobs.integration.test.ts +1 -1
- package/src/jobs/job-runner.ts +61 -6
- package/src/observability/__tests__/metrics-wiring.test.ts +1 -1
- package/src/observability/__tests__/observability.integration.test.ts +1 -1
- package/src/observability/metrics-wiring.ts +1 -1
- package/src/pipeline/__tests__/archive-stream.integration.test.ts +1 -1
- package/src/pipeline/__tests__/causation-chain.integration.test.ts +1 -1
- package/src/pipeline/__tests__/ctx-bridge.integration.test.ts +1 -1
- package/src/pipeline/__tests__/ctx-systemdb.integration.test.ts +1 -1
- package/src/pipeline/__tests__/dispatcher-utils.test.ts +8 -0
- package/src/pipeline/__tests__/dispatcher.test.ts +1 -1
- package/src/pipeline/__tests__/domain-events-projections.integration.test.ts +1 -1
- package/src/pipeline/__tests__/escape-hatch-audit.integration.test.ts +1 -1
- package/src/pipeline/__tests__/escape-hatch.integration.test.ts +1 -1
- package/src/pipeline/__tests__/event-dedup.integration.test.ts +1 -1
- package/src/pipeline/__tests__/event-define-event-strict.integration.test.ts +1 -1
- package/src/pipeline/__tests__/event-dispatcher-batch-delivery.test.ts +178 -0
- package/src/pipeline/__tests__/event-dispatcher-batch-handler.integration.test.ts +143 -0
- package/src/pipeline/__tests__/event-dispatcher-commit-order.integration.test.ts +278 -0
- package/src/pipeline/__tests__/event-dispatcher-delivery-max-attempts.test.ts +1 -0
- package/src/pipeline/__tests__/event-dispatcher-idle.integration.test.ts +94 -0
- package/src/pipeline/__tests__/event-dispatcher-lifecycle.integration.test.ts +6 -6
- package/src/pipeline/__tests__/event-dispatcher-per-consumer-turns.integration.test.ts +126 -0
- package/src/pipeline/__tests__/event-dispatcher-start-from.integration.test.ts +103 -3
- package/src/pipeline/__tests__/fetch-for-writing.integration.test.ts +1 -1
- package/src/pipeline/__tests__/idempotency-transient-failure.integration.test.ts +1 -1
- package/src/pipeline/__tests__/job-trigger-consumer.integration.test.ts +1 -1
- package/src/pipeline/__tests__/lifecycle-pipeline.test.ts +1 -1
- package/src/pipeline/__tests__/load-aggregate-query.integration.test.ts +1 -1
- package/src/pipeline/__tests__/msp-multi-hop.integration.test.ts +1 -1
- package/src/pipeline/__tests__/msp-rebuild.integration.test.ts +116 -1
- package/src/pipeline/__tests__/multi-stream-projection.integration.test.ts +1 -1
- package/src/pipeline/__tests__/pending-gap-ranges.test.ts +94 -0
- package/src/pipeline/__tests__/post-query-hook.integration.test.ts +1 -1
- package/src/pipeline/__tests__/public-intake-runtime-gate.integration.test.ts +262 -3
- package/src/pipeline/__tests__/query-projection.integration.test.ts +1 -1
- package/src/pipeline/__tests__/rate-limit-default.integration.test.ts +1 -1
- package/src/pipeline/__tests__/rebuild-poison-quarantine.integration.test.ts +1 -1
- package/src/pipeline/__tests__/search-consumer-named-events.integration.test.ts +1 -1
- package/src/pipeline/__tests__/system-identity-switch.integration.test.ts +1 -1
- package/src/pipeline/__tests__/try-append-event.integration.test.ts +1 -1
- package/src/pipeline/__tests__/unsafe-raw-escape-hatch.integration.test.ts +1 -1
- package/src/pipeline/dispatch-batch.ts +49 -19
- package/src/pipeline/dispatch-stream.ts +12 -3
- package/src/pipeline/dispatcher-utils.ts +21 -2
- package/src/pipeline/dispatcher.ts +71 -6
- package/src/pipeline/event-consumer-state.ts +26 -0
- package/src/pipeline/event-dispatcher-admin.ts +32 -5
- package/src/pipeline/event-dispatcher-delivery.ts +212 -65
- package/src/pipeline/event-dispatcher.ts +253 -49
- package/src/pipeline/index.ts +1 -0
- package/src/pipeline/msp-rebuild.ts +39 -1
- package/src/pipeline/pending-gap-ranges.ts +120 -0
- package/src/pipeline/system-hooks.ts +226 -64
- package/src/pipeline/write-origin.ts +31 -10
- package/src/rate-limit/__tests__/dispatcher-l3.integration.test.ts +1 -1
- package/src/search/__tests__/derive-search-adapter-config.test.ts +65 -0
- package/src/search/__tests__/meilisearch-adapter.integration.test.ts +91 -0
- package/src/search/__tests__/search-consumer-batch.integration.test.ts +306 -0
- package/src/search/derive-search-adapter-config.ts +27 -0
- package/src/search/index.ts +1 -0
- package/src/search/meilisearch-adapter.ts +96 -14
- package/src/secrets/__tests__/contains-secret.test.ts +1 -1
- package/src/stack/__tests__/request-helper.integration.test.ts +1 -1
- package/src/stack/__tests__/setup-test-stack-jobs.integration.test.ts +1 -1
- package/src/stack/__tests__/setup-test-stack-metrics.integration.test.ts +1 -1
- package/src/stack/test-stack.ts +1 -1
- package/src/testing/__tests__/e2e-generator.test.ts +1 -1
- package/src/testing/e2e-generator.ts +1 -1
package/src/db/tenant-db.ts
CHANGED
|
@@ -51,6 +51,41 @@ const declaredUnsafeRawRunners = new WeakMap<
|
|
|
51
51
|
// (withUnsafeRawGrant, acknowledgeConventionCrossTenant) carry it too.
|
|
52
52
|
const personalDataGates = new WeakMap<TenantDb, PersonalDataGate>();
|
|
53
53
|
|
|
54
|
+
// Lets createTenantDb(ctx.db.unsafeRaw(reason), ...) inherit the gate. Keyed by a
|
|
55
|
+
// per-grant proxy, never the shared pool/tx: tagging that would gate every sibling TenantDb.
|
|
56
|
+
const runnerPersonalDataGates = new WeakMap<DbRunner, PersonalDataGate>();
|
|
57
|
+
|
|
58
|
+
function gatedRunner(runner: DbRunner, gate: PersonalDataGate): DbRunner {
|
|
59
|
+
const proxy = new Proxy(runner as object, {
|
|
60
|
+
// Tagged-template calls need the real driver object as `this`.
|
|
61
|
+
apply(target, _thisArg, args) {
|
|
62
|
+
return Reflect.apply(target as (...callArgs: unknown[]) => unknown, target, args);
|
|
63
|
+
},
|
|
64
|
+
get(target, prop, _receiver) {
|
|
65
|
+
const value = Reflect.get(target, prop, target);
|
|
66
|
+
if (typeof value !== "function") return value;
|
|
67
|
+
if (prop === "begin" || prop === "savepoint") {
|
|
68
|
+
// createTenantDb(tx, ...) inside the callback must inherit the gate too.
|
|
69
|
+
return (...args: unknown[]) => {
|
|
70
|
+
const callback = args[args.length - 1];
|
|
71
|
+
if (typeof callback !== "function") {
|
|
72
|
+
return Reflect.apply(value, target, args);
|
|
73
|
+
}
|
|
74
|
+
const gatedArgs = [
|
|
75
|
+
...args.slice(0, -1),
|
|
76
|
+
(tx: unknown) => callback(gatedRunner(tx as DbRunner, gate)),
|
|
77
|
+
];
|
|
78
|
+
return Reflect.apply(value, target, gatedArgs);
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
return value.bind(target);
|
|
82
|
+
},
|
|
83
|
+
// @cast-boundary proxy-erasure — Proxy<object> re-tags as the wrapped DbRunner shape.
|
|
84
|
+
}) as DbRunner;
|
|
85
|
+
runnerPersonalDataGates.set(proxy, gate);
|
|
86
|
+
return proxy;
|
|
87
|
+
}
|
|
88
|
+
|
|
54
89
|
// The executor passes its entity so the check does not depend on the table-name lookup.
|
|
55
90
|
export function assertPersonalDataWrite(
|
|
56
91
|
db: TenantDb,
|
|
@@ -151,7 +186,9 @@ function buildUncheckedSystemDb(
|
|
|
151
186
|
});
|
|
152
187
|
}
|
|
153
188
|
report("unsafe-raw", reason);
|
|
154
|
-
|
|
189
|
+
const runner = tenantDbRunner(db);
|
|
190
|
+
const personalDataGate = personalDataGates.get(db);
|
|
191
|
+
return personalDataGate ? gatedRunner(runner, personalDataGate) : runner;
|
|
155
192
|
}
|
|
156
193
|
|
|
157
194
|
const uncheckedSystemDb: UncheckedSystemDb = {
|
|
@@ -352,6 +389,7 @@ export function createTenantDb(
|
|
|
352
389
|
): TenantDb {
|
|
353
390
|
if (meter) registerStandardMetrics(meter);
|
|
354
391
|
const report = grants?.report ?? fallbackEscapeHatchReporter(tenantId);
|
|
392
|
+
const personalDataGate = grants?.personalDataGate ?? runnerPersonalDataGates.get(db);
|
|
355
393
|
|
|
356
394
|
function withDbSpan<T>(
|
|
357
395
|
operation: "select" | "insert" | "update" | "delete",
|
|
@@ -452,9 +490,9 @@ export function createTenantDb(
|
|
|
452
490
|
table: Table | EntityTableMeta,
|
|
453
491
|
keys: readonly string[],
|
|
454
492
|
): AccessDeniedError | undefined {
|
|
455
|
-
if (!
|
|
493
|
+
if (!personalDataGate) return undefined;
|
|
456
494
|
try {
|
|
457
|
-
|
|
495
|
+
personalDataGate(tableNameOf(table), keys);
|
|
458
496
|
return undefined;
|
|
459
497
|
} catch (e) {
|
|
460
498
|
if (e instanceof AccessDeniedError) return e;
|
|
@@ -553,7 +591,7 @@ export function createTenantDb(
|
|
|
553
591
|
});
|
|
554
592
|
}
|
|
555
593
|
report("unsafe-raw", reason);
|
|
556
|
-
return db;
|
|
594
|
+
return personalDataGate ? gatedRunner(db, personalDataGate) : db;
|
|
557
595
|
}
|
|
558
596
|
|
|
559
597
|
const tenantDb: TenantDb = {
|
|
@@ -648,7 +686,7 @@ export function createTenantDb(
|
|
|
648
686
|
return createTenantDb(db, tenantId, "system", tracer, meter, signal, grants);
|
|
649
687
|
});
|
|
650
688
|
bindTenantDbRunner(tenantDb, db);
|
|
651
|
-
if (
|
|
689
|
+
if (personalDataGate) personalDataGates.set(tenantDb, personalDataGate);
|
|
652
690
|
return tenantDb;
|
|
653
691
|
}
|
|
654
692
|
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
|
8
8
|
import type { DerivativeRendererPlugin } from "@cosmicdrift/kumiko-types/derivatives-types";
|
|
9
|
-
import
|
|
9
|
+
import * as z from "zod";
|
|
10
10
|
import { defineFeature, EXT_DERIVATIVE_RENDERER } from "../../engine";
|
|
11
11
|
import { InternalError, NotFoundError, writeFailure } from "../../errors";
|
|
12
12
|
import { createFilesFeature } from "../../files/feature";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import
|
|
2
|
+
import * as z from "zod";
|
|
3
3
|
import { validateBoot } from "../boot-validator";
|
|
4
4
|
import { defineFeature } from "../define-feature";
|
|
5
5
|
import { createEntity, createTextField } from "../factories";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import
|
|
2
|
+
import * as z from "zod";
|
|
3
3
|
import { requiredKeysFromScreen } from "../../i18n/required-surface-keys";
|
|
4
4
|
import { validateBoot } from "../boot-validator";
|
|
5
5
|
import { defineFeature } from "../define-feature";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import
|
|
2
|
+
import * as z from "zod";
|
|
3
3
|
import { withBootValidatorFixture } from "../../testing/boot-validator-fixture";
|
|
4
4
|
import { validateBoot as validateBootRaw } from "../boot-validator";
|
|
5
5
|
import { defineFeature } from "../define-feature";
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
// allowPlaintext-Marker unterdrückt Heuristik-Warnings.
|
|
16
16
|
|
|
17
17
|
import { afterEach, beforeEach, describe, expect, spyOn, test } from "bun:test";
|
|
18
|
-
import
|
|
18
|
+
import * as z from "zod";
|
|
19
19
|
import { validateBoot } from "../boot-validator";
|
|
20
20
|
import { defineFeature } from "../define-feature";
|
|
21
21
|
import {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import
|
|
2
|
+
import * as z from "zod";
|
|
3
3
|
import { validateBoot } from "../boot-validator";
|
|
4
4
|
import { defineFeature } from "../define-feature";
|
|
5
5
|
import { createEntity, createTextField } from "../factories";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import
|
|
2
|
+
import * as z from "zod";
|
|
3
3
|
import { withBootValidatorFixture } from "../../testing/boot-validator-fixture";
|
|
4
4
|
import { validateBoot as validateBootRaw } from "../boot-validator";
|
|
5
5
|
import { defineFeature } from "../define-feature";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import
|
|
2
|
+
import * as z from "zod";
|
|
3
3
|
import { withBootValidatorFixture } from "../../testing/boot-validator-fixture";
|
|
4
4
|
import { validateBoot as validateBootRaw } from "../boot-validator";
|
|
5
5
|
import { defineFeature } from "../define-feature";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import
|
|
2
|
+
import * as z from "zod";
|
|
3
3
|
import { withBootValidatorFixture } from "../../testing/boot-validator-fixture";
|
|
4
4
|
import { validateBoot as validateBootRaw } from "../boot-validator";
|
|
5
5
|
import { defineFeature } from "../define-feature";
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
// + handler-access mit ROLES.TenantAdmin
|
|
15
15
|
|
|
16
16
|
import { afterEach, beforeEach, describe, expect, spyOn, test } from "bun:test";
|
|
17
|
-
import
|
|
17
|
+
import * as z from "zod";
|
|
18
18
|
import { ROLES } from "../../auth";
|
|
19
19
|
import { validateBoot } from "../boot-validator";
|
|
20
20
|
import { defineFeature } from "../define-feature";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, spyOn, test } from "bun:test";
|
|
2
|
-
import
|
|
2
|
+
import * as z from "zod";
|
|
3
3
|
import type { SchemaTable } from "../../db/dialect";
|
|
4
4
|
import { table, text } from "../../db/dialect";
|
|
5
5
|
import { rowMetaFieldNames } from "../../db/table-builder";
|
|
@@ -4673,6 +4673,40 @@ describe("boot-validator", () => {
|
|
|
4673
4673
|
);
|
|
4674
4674
|
});
|
|
4675
4675
|
|
|
4676
|
+
test("projectionDetail tabs-mode fields section with both groups and actions → throw", () => {
|
|
4677
|
+
const feature = defineFeature("shop", (r) => {
|
|
4678
|
+
r.screen({
|
|
4679
|
+
id: "order-detail",
|
|
4680
|
+
type: "projectionDetail",
|
|
4681
|
+
query: "shop:query:order-detail",
|
|
4682
|
+
layout: {
|
|
4683
|
+
mode: "tabs",
|
|
4684
|
+
sections: [
|
|
4685
|
+
{
|
|
4686
|
+
id: "billing",
|
|
4687
|
+
title: "Billing",
|
|
4688
|
+
fields: [],
|
|
4689
|
+
groups: [{ title: "Amounts", fields: ["name"] }],
|
|
4690
|
+
actions: [
|
|
4691
|
+
{ kind: "navigate", id: "edit", label: "actions.edit", screen: "order-detail" },
|
|
4692
|
+
],
|
|
4693
|
+
},
|
|
4694
|
+
{
|
|
4695
|
+
id: "positions",
|
|
4696
|
+
kind: "relatedList",
|
|
4697
|
+
title: "Positions",
|
|
4698
|
+
query: "shop:query:order-positions",
|
|
4699
|
+
columns: [{ field: "name" }],
|
|
4700
|
+
},
|
|
4701
|
+
],
|
|
4702
|
+
},
|
|
4703
|
+
});
|
|
4704
|
+
});
|
|
4705
|
+
expect(() => validateBoot([feature])).toThrow(
|
|
4706
|
+
/section "billing" declares both groups and actions in tabs mode/,
|
|
4707
|
+
);
|
|
4708
|
+
});
|
|
4709
|
+
|
|
4676
4710
|
test("projectionDetail relatedList defaultSort.field not a listed column → throw", () => {
|
|
4677
4711
|
const feature = defineFeature("shop", (r) => {
|
|
4678
4712
|
r.screen({
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
// im Browser-Bundle.
|
|
9
9
|
|
|
10
10
|
import { describe, expect, test } from "bun:test";
|
|
11
|
-
import
|
|
11
|
+
import * as z from "zod";
|
|
12
12
|
import { buildAppSchema, findNonJsonSafePath } from "../build-app-schema";
|
|
13
13
|
import { defineFeature } from "../define-feature";
|
|
14
14
|
import { createRegistry } from "../registry";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import
|
|
2
|
+
import * as z from "zod";
|
|
3
3
|
import { buildConfigFeatureSchema } from "../build-config-feature-schema";
|
|
4
4
|
import { createTenantConfig } from "../config-helpers";
|
|
5
5
|
import { defineFeature } from "../define-feature";
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// custom-fields/__tests__/custom-fields.integration.test.ts.
|
|
4
4
|
|
|
5
5
|
import { describe, expect, test } from "bun:test";
|
|
6
|
-
import
|
|
6
|
+
import * as z from "zod";
|
|
7
7
|
import { defineFeature } from "../define-feature";
|
|
8
8
|
import { createEntity, createTextField } from "../factories";
|
|
9
9
|
import { createRegistry } from "../registry";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, mock, test } from "bun:test";
|
|
2
|
-
import
|
|
2
|
+
import * as z from "zod";
|
|
3
3
|
import { AccessDeniedError } from "../../errors";
|
|
4
4
|
import { createGatedIdentitySwitch, type QueryAsFn } from "../../pipeline/system-identity-switch";
|
|
5
5
|
import {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import
|
|
2
|
+
import * as z from "zod";
|
|
3
3
|
import { createEntity, createRegistry, defineFeature } from "../index";
|
|
4
4
|
import type { PostSaveHookFn, PreDeleteHookFn, PreSaveHookFn, SaveContext } from "../types";
|
|
5
5
|
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { describe, expect, it } from "bun:test";
|
|
7
7
|
import { randomUUID } from "node:crypto";
|
|
8
|
-
import
|
|
8
|
+
import * as z from "zod";
|
|
9
9
|
import { TestUsers } from "../../stack";
|
|
10
10
|
import { defineWriteHandler } from "../define-handler";
|
|
11
11
|
import { defineStep } from "../define-step";
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
// surface against an empty ctx mock; this file is the real-stack gate.
|
|
45
45
|
|
|
46
46
|
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
47
|
-
import
|
|
47
|
+
import * as z from "zod";
|
|
48
48
|
import { table, text, timestamp, uuid } from "../../db/dialect";
|
|
49
49
|
import { createEventStoreExecutor } from "../../db/event-store-executor";
|
|
50
50
|
import { asRawClient, selectMany } from "../../db/query";
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
// and we wouldn't notice until a prod incident lacks the breadcrumbs.
|
|
15
15
|
|
|
16
16
|
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
17
|
-
import
|
|
17
|
+
import * as z from "zod";
|
|
18
18
|
import { setupTestStack, type TestStack, TestUsers, unsafeCreateEntityTable } from "../../stack";
|
|
19
19
|
import { createRecordingProvider, type RecordingProvider } from "../../testing";
|
|
20
20
|
import { defineFeature } from "../define-feature";
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
// here with a longer-pipeline handler variant.
|
|
24
24
|
|
|
25
25
|
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
26
|
-
import
|
|
26
|
+
import * as z from "zod";
|
|
27
27
|
import { setupTestStack, type TestStack, TestUsers, unsafeCreateEntityTable } from "../../stack";
|
|
28
28
|
import { defineFeature } from "../define-feature";
|
|
29
29
|
import { defineWriteHandler } from "../define-handler";
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// hygiene, error-propagation and build-time guards.
|
|
7
7
|
|
|
8
8
|
import { describe, expect, it } from "bun:test";
|
|
9
|
-
import
|
|
9
|
+
import * as z from "zod";
|
|
10
10
|
import { TestUsers } from "../../stack";
|
|
11
11
|
import { defineWriteHandler } from "../define-handler";
|
|
12
12
|
import { stepsPipeline } from "../pipeline";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import
|
|
2
|
+
import * as z from "zod";
|
|
3
3
|
import { withBootValidatorFixture } from "../../testing/boot-validator-fixture";
|
|
4
4
|
import { validateBoot as validateBootRaw } from "../boot-validator";
|
|
5
5
|
import { defineFeature } from "../define-feature";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import
|
|
2
|
+
import * as z from "zod";
|
|
3
3
|
import { withBootValidatorFixture } from "../../testing/boot-validator-fixture";
|
|
4
4
|
import { validateBoot as validateBootRaw } from "../boot-validator";
|
|
5
5
|
import { defineFeature } from "../define-feature";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import
|
|
2
|
+
import * as z from "zod";
|
|
3
3
|
import { withBootValidatorFixture } from "../../testing/boot-validator-fixture";
|
|
4
4
|
import { validateBoot as validateBootRaw } from "../boot-validator";
|
|
5
5
|
import { defineFeature } from "../define-feature";
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
// escape-hatch-audit.integration.test.ts.
|
|
9
9
|
|
|
10
10
|
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
|
11
|
-
import
|
|
11
|
+
import * as z from "zod";
|
|
12
12
|
import { table, text, uuid } from "../../db/dialect";
|
|
13
13
|
import { insertOne } from "../../db/query";
|
|
14
14
|
import { defineFeature, defineWriteHandler } from "../../engine";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import
|
|
2
|
+
import * as z from "zod";
|
|
3
3
|
import { createTenantConfig } from "../config-helpers";
|
|
4
4
|
import { defineFeature } from "../define-feature";
|
|
5
5
|
import { createEntity, createTextField } from "../factories";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import
|
|
2
|
+
import * as z from "zod";
|
|
3
3
|
import { withBootValidatorFixture } from "../../testing/boot-validator-fixture";
|
|
4
4
|
import { validateBoot as validateBootRaw } from "../boot-validator";
|
|
5
5
|
import { createTenantConfig } from "../config-helpers";
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { describe, expect, it } from "bun:test";
|
|
7
7
|
import { randomUUID } from "node:crypto";
|
|
8
|
-
import
|
|
8
|
+
import * as z from "zod";
|
|
9
9
|
import { table as pgTable, text, uuid } from "../../db/dialect";
|
|
10
10
|
import { defineFeature } from "../define-feature";
|
|
11
11
|
import { defineWriteHandler } from "../define-handler";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import
|
|
2
|
+
import * as z from "zod";
|
|
3
3
|
import { defineFeature } from "../../define-feature";
|
|
4
4
|
import { createEntity, createTextField } from "../../factories";
|
|
5
5
|
import { from } from "../../ownership";
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// validateActionHasIcon is the single icon-resolvability check shared by
|
|
2
|
+
// every projectionDetail/entityEdit action rendered in a Card title row:
|
|
3
|
+
// screen-level `actions`, section-level `actions`, and relatedList
|
|
4
|
+
// `emptyState.action` (see screens.ts call sites). Tested directly here
|
|
5
|
+
// rather than only via a full feature boot, since all three call sites
|
|
6
|
+
// share this one function.
|
|
7
|
+
|
|
8
|
+
import { describe, expect, test } from "bun:test";
|
|
9
|
+
import { validateActionHasIcon } from "../screens";
|
|
10
|
+
|
|
11
|
+
describe("validateActionHasIcon", () => {
|
|
12
|
+
test("rejects a screen-level action with no declared icon and an id that resolves none", () => {
|
|
13
|
+
expect(() =>
|
|
14
|
+
validateActionHasIcon("orders", "order-detail", "projectionDetail", "action", {
|
|
15
|
+
id: "custom-thing",
|
|
16
|
+
}),
|
|
17
|
+
).toThrow(/no resolvable icon/);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test("rejects a section action, naming the section in the error", () => {
|
|
21
|
+
expect(() =>
|
|
22
|
+
validateActionHasIcon(
|
|
23
|
+
"orders",
|
|
24
|
+
"order-detail",
|
|
25
|
+
"projectionDetail",
|
|
26
|
+
'section "Payments" action',
|
|
27
|
+
{ id: "custom-thing" },
|
|
28
|
+
),
|
|
29
|
+
).toThrow(/section "Payments" action "custom-thing" has no resolvable icon/);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test("rejects a relatedList emptyState action, naming it as such in the error", () => {
|
|
33
|
+
expect(() =>
|
|
34
|
+
validateActionHasIcon(
|
|
35
|
+
"orders",
|
|
36
|
+
"order-detail",
|
|
37
|
+
"projectionDetail",
|
|
38
|
+
'section "Payments" emptyState action',
|
|
39
|
+
{ id: "custom-thing" },
|
|
40
|
+
),
|
|
41
|
+
).toThrow(/emptyState action "custom-thing" has no resolvable icon/);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test("passes when the action declares an explicit icon", () => {
|
|
45
|
+
expect(() =>
|
|
46
|
+
validateActionHasIcon("orders", "order-detail", "entityEdit", "action", {
|
|
47
|
+
id: "custom-thing",
|
|
48
|
+
icon: "lock",
|
|
49
|
+
}),
|
|
50
|
+
).not.toThrow();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("passes when the id resolves a default icon via a kebab segment (no explicit icon needed)", () => {
|
|
54
|
+
// "deletion" -> "trash" in the shared ACTION_ICON_BY_ID map.
|
|
55
|
+
expect(() =>
|
|
56
|
+
validateActionHasIcon("user-data-rights", "privacy-center", "projectionDetail", "action", {
|
|
57
|
+
id: "request-deletion",
|
|
58
|
+
}),
|
|
59
|
+
).not.toThrow();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// action.icon is typed IconKey (a closed union) — but that compile-time
|
|
63
|
+
// guarantee only covers call sites inside this repo's `tsc --build`
|
|
64
|
+
// project graph. Samples and external consumers aren't in it, so an
|
|
65
|
+
// unregistered literal (a typo, or a value copied from a different icon
|
|
66
|
+
// set) can reach here as plain runtime data. The renderer's actionIconFor
|
|
67
|
+
// (renderer-web/src/primitives/index.tsx) already falls back to "no icon"
|
|
68
|
+
// for such a value instead of crashing — this must throw here instead of
|
|
69
|
+
// silently accepting it, or the renderer/validator resolvers diverge.
|
|
70
|
+
test("rejects a declared icon that is not a registered NAV_ICON key, even though it resolves to a value", () => {
|
|
71
|
+
expect(() =>
|
|
72
|
+
validateActionHasIcon("orders", "order-detail", "projectionDetail", "action", {
|
|
73
|
+
id: "refresh-order",
|
|
74
|
+
// @ts-expect-error — exercising a value outside the closed IconKey
|
|
75
|
+
// union, exactly like an untypechecked caller (a sample, or a JS
|
|
76
|
+
// consumer) would produce at runtime.
|
|
77
|
+
icon: "refresh-cw",
|
|
78
|
+
}),
|
|
79
|
+
).toThrow(/no resolvable icon/);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// handler on a parentRef entity must fail boot rather than leak silently.
|
|
6
6
|
|
|
7
7
|
import { describe, expect, test } from "bun:test";
|
|
8
|
-
import
|
|
8
|
+
import * as z from "zod";
|
|
9
9
|
import { defineFeature } from "../../define-feature";
|
|
10
10
|
import { defineEntityDetailHandler, defineEntityListHandler } from "../../entity-handlers";
|
|
11
11
|
import { createEntity, createTextField } from "../../factories";
|