@cosmicdrift/kumiko-framework 0.165.0 → 0.165.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/package.json +5 -3
- package/src/__tests__/consumer-cli.integration.test.ts +32 -0
- package/src/__tests__/schema-cli.integration.test.ts +1 -1
- package/src/api/__tests__/api.test.ts +267 -19
- package/src/api/__tests__/auth-middleware-anonymous-access-boot.test.ts +40 -0
- package/src/api/__tests__/auth-routes-invalid-body-invite.test.ts +16 -0
- package/src/api/__tests__/auth-routes-mfa-preauth-confirm.test.ts +2 -1
- package/src/api/__tests__/auth-routes-mfa-preauth-enable-start.test.ts +64 -1
- package/src/api/__tests__/auth-routes-trusted-proxy.test.ts +150 -0
- package/src/api/__tests__/batch.integration.test.ts +21 -2
- package/src/api/__tests__/jwt.test.ts +52 -2
- package/src/api/__tests__/redis-login-rate-limiter.integration.test.ts +72 -0
- package/src/api/__tests__/sse-broker.test.ts +57 -0
- package/src/api/__tests__/sse-route.test.ts +4 -0
- package/src/api/auth-routes.ts +178 -33
- package/src/api/index.ts +1 -0
- package/src/api/jwt.ts +22 -1
- package/src/api/routes.ts +103 -35
- package/src/api/server.ts +26 -2
- package/src/api/sse-broker.ts +38 -0
- package/src/bun-db/index.ts +1 -0
- package/src/bun-db/query.ts +14 -5
- package/src/consumer-cli.ts +60 -13
- package/src/db/__tests__/decimal-field.test.ts +3 -3
- package/src/db/__tests__/entity-table-meta-source.test.ts +43 -8
- package/src/db/__tests__/event-store-executor-write-verbs.integration.test.ts +14 -1
- package/src/db/__tests__/located-timestamp.test.ts +19 -0
- package/src/db/__tests__/migrate-runner.test.ts +75 -0
- package/src/db/__tests__/replay-migration-sql.test.ts +131 -2
- package/src/db/__tests__/table-builder-meta-lockstep.test.ts +6 -6
- package/src/db/__tests__/tenant-db-where-merge.test.ts +10 -6
- package/src/db/api.ts +2 -2
- package/src/db/bun-provider.ts +2 -2
- package/src/db/collect-table-metas.ts +3 -3
- package/src/db/connection.ts +6 -3
- package/src/db/dialect.ts +1 -6
- package/src/db/entity-table-meta-types.ts +1 -1
- package/src/db/entity-table-meta.ts +49 -32
- package/src/db/event-store-executor-context.ts +2 -3
- package/src/db/event-store-executor-read.ts +2 -3
- package/src/db/event-store-executor-write.ts +8 -0
- package/src/db/index.ts +13 -2
- package/src/db/located-timestamp.ts +4 -0
- package/src/db/migrate-runner.ts +102 -11
- package/src/db/pg-error.ts +8 -0
- package/src/db/postgres-provider.ts +2 -2
- package/src/db/queries/__tests__/event-store-idempotency-index.integration.test.ts +80 -0
- package/src/db/queries/ddl.ts +45 -0
- package/src/db/queries/event-store.ts +97 -5
- package/src/db/queries/test-stack.ts +4 -30
- package/src/db/reference-data.ts +2 -3
- package/src/db/replay-migration-sql.ts +114 -12
- package/src/db/table-builder.ts +2 -2
- package/src/db/tenant-db.ts +3 -5
- package/src/engine/__tests__/engine.test.ts +30 -0
- package/src/engine/__tests__/schema-builder.test.ts +18 -0
- package/src/engine/__tests__/store-table.test.ts +10 -13
- package/src/engine/boot-validator/nav.ts +5 -0
- package/src/engine/constants.ts +28 -6
- package/src/engine/create-app.ts +11 -0
- package/src/engine/effective-features.ts +12 -2
- package/src/engine/extensions/user-data.ts +12 -4
- package/src/engine/feature-ast/extractors/round5.ts +1 -1
- package/src/engine/feature-ui-extensions.ts +3 -3
- package/src/engine/hook-helpers.ts +3 -1
- package/src/engine/index.ts +1 -1
- package/src/engine/ownership.ts +4 -3
- package/src/engine/registry-ingest.ts +14 -14
- package/src/engine/registry-state.ts +6 -3
- package/src/engine/schema-builder.ts +1 -0
- package/src/engine/steps/__tests__/duration-utils.test.ts +20 -0
- package/src/engine/steps/_duration-utils.ts +2 -0
- package/src/engine/steps/unsafe-projection-upsert.ts +1 -4
- package/src/engine/types/config.ts +1 -1
- package/src/engine/types/define-handler.ts +1 -1
- package/src/engine/types/entity-handlers.ts +1 -1
- package/src/engine/types/event-type-map.ts +1 -1
- package/src/engine/types/feature.ts +1 -1
- package/src/engine/types/fields.ts +1 -1
- package/src/engine/types/handlers.ts +1 -1
- package/src/engine/types/hooks.ts +1 -1
- package/src/engine/types/http-route.ts +1 -1
- package/src/engine/types/nav.ts +1 -1
- package/src/engine/types/ownership.ts +1 -1
- package/src/engine/types/projection.ts +1 -1
- package/src/engine/types/relations.ts +1 -1
- package/src/engine/types/screen.ts +1 -1
- package/src/engine/types/step.ts +1 -1
- package/src/engine/types/target-ref.ts +1 -1
- package/src/engine/types/tree-node.ts +1 -1
- package/src/engine/types/workspace.ts +1 -1
- package/src/engine/validate-projection-allowlist.ts +6 -6
- package/src/errors/classes.ts +21 -0
- package/src/errors/index.ts +1 -0
- package/src/errors/write-error-info.ts +6 -2
- package/src/event-store/__tests__/admin-api.integration.test.ts +27 -1
- package/src/event-store/__tests__/event-store.integration.test.ts +32 -0
- package/src/event-store/__tests__/unscoped-stream-primitives.guard.test.ts +22 -4
- package/src/event-store/admin-api.ts +11 -4
- package/src/event-store/event-store.ts +19 -4
- package/src/event-store/types.ts +1 -1
- package/src/files/__tests__/build-storage-key.test.ts +28 -0
- package/src/files/__tests__/local-provider.test.ts +31 -0
- package/src/files/__tests__/write-stream.test.ts +3 -3
- package/src/files/index.ts +1 -1
- package/src/files/local-provider.ts +6 -1
- package/src/files/types.ts +8 -1
- package/src/jobs/__tests__/jobs.integration.test.ts +167 -7
- package/src/jobs/job-runner.ts +41 -11
- package/src/logging/types.ts +1 -1
- package/src/migrations/__tests__/kumiko-drift.integration.test.ts +2 -2
- package/src/migrations/projection-table-index.ts +1 -1
- package/src/observability/index.ts +1 -0
- package/src/observability/standard-metrics.ts +35 -2
- package/src/observability/types/index.ts +1 -1
- package/src/observability/types/metric.ts +1 -1
- package/src/observability/types/provider.ts +1 -1
- package/src/observability/types/span.ts +1 -1
- package/src/pipeline/__tests__/dispatcher.test.ts +212 -0
- package/src/pipeline/__tests__/event-consumer-state.integration.test.ts +31 -0
- package/src/pipeline/__tests__/event-dispatcher-rearm.integration.test.ts +83 -0
- package/src/pipeline/__tests__/lifecycle-pipeline.test.ts +107 -97
- package/src/pipeline/dispatch-shared.ts +59 -6
- package/src/pipeline/dispatch-stream.ts +50 -11
- package/src/pipeline/dispatcher.ts +7 -1
- package/src/pipeline/event-consumer-state.ts +16 -13
- package/src/pipeline/event-dispatcher-delivery.ts +20 -6
- package/src/pipeline/event-dispatcher.ts +22 -0
- package/src/pipeline/index.ts +2 -0
- package/src/pipeline/system-hooks.ts +87 -0
- package/src/rate-limit/__tests__/resolver.integration.test.ts +18 -0
- package/src/rate-limit/resolver.ts +6 -2
- package/src/schema-cli.ts +24 -12
- package/src/search/__tests__/reindex-entity.integration.test.ts +24 -1
- package/src/search/reindex-entity.ts +31 -2
- package/src/search/types.ts +1 -1
- package/src/stack/__tests__/setup-test-stack-jobs.integration.test.ts +6 -2
- package/src/stack/db.ts +2 -1
- package/src/stack/push-entity-projection-tables.ts +2 -1
- package/src/stack/request-helper.ts +20 -1
- package/src/stack/table-helpers.ts +6 -4
- package/src/stack/test-stack.ts +18 -15
- package/src/testing/__tests__/late-bound.test.ts +7 -0
- package/src/testing/__tests__/wait-for.test.ts +6 -0
- package/src/testing/file-provider-contract.ts +26 -6
- package/src/testing/index.ts +1 -0
- package/src/testing/late-bound.ts +5 -3
- package/src/testing/wait-for.ts +3 -0
- package/src/testing/without-ambient-temporal.ts +14 -0
- package/src/time/__tests__/polyfill-reinstall.test.ts +17 -0
- package/src/time/geo-tz.ts +1 -1
- package/src/time/polyfill.ts +28 -39
- package/src/time/tz-context.ts +30 -24
- package/src/utils/__tests__/safe-json-temporal.test.ts +14 -0
- package/src/utils/safe-json.ts +3 -2
- package/src/engine/__tests__/registry-facade-sweep.test.ts +0 -80
package/src/testing/wait-for.ts
CHANGED
|
@@ -14,6 +14,9 @@ export async function waitFor(
|
|
|
14
14
|
options?: { delays?: number[] },
|
|
15
15
|
): Promise<void> {
|
|
16
16
|
const delays = options?.delays ?? [250, 1000, 3000];
|
|
17
|
+
if (delays.length === 0) {
|
|
18
|
+
throw new Error("waitFor: empty delay schedule");
|
|
19
|
+
}
|
|
17
20
|
let lastError: unknown;
|
|
18
21
|
|
|
19
22
|
for (let i = 0; i < delays.length; i++) {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Run `fn` with `globalThis.Temporal` deleted, then restore.
|
|
3
|
+
* Used by ambient-independence regression tests (kumiko-framework#1525/#1550).
|
|
4
|
+
*/
|
|
5
|
+
export async function withoutAmbientTemporal<T>(fn: () => T | Promise<T>): Promise<T> {
|
|
6
|
+
const savedGlobal = (globalThis as { Temporal?: unknown }).Temporal;
|
|
7
|
+
delete (globalThis as { Temporal?: unknown }).Temporal;
|
|
8
|
+
try {
|
|
9
|
+
return await fn();
|
|
10
|
+
} finally {
|
|
11
|
+
if (savedGlobal === undefined) delete (globalThis as { Temporal?: unknown }).Temporal;
|
|
12
|
+
else (globalThis as { Temporal?: unknown }).Temporal = savedGlobal;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { withoutAmbientTemporal } from "../../testing/without-ambient-temporal";
|
|
3
|
+
import { ensureTemporalPolyfill, getTemporal } from "../polyfill";
|
|
4
|
+
|
|
5
|
+
describe("ensureTemporalPolyfill — re-install after teardown", () => {
|
|
6
|
+
test("re-assigns globalThis.Temporal after delete", async () => {
|
|
7
|
+
await ensureTemporalPolyfill();
|
|
8
|
+
expect("Temporal" in globalThis).toBe(true);
|
|
9
|
+
|
|
10
|
+
await withoutAmbientTemporal(async () => {
|
|
11
|
+
expect("Temporal" in globalThis).toBe(false);
|
|
12
|
+
await ensureTemporalPolyfill();
|
|
13
|
+
expect("Temporal" in globalThis).toBe(true);
|
|
14
|
+
expect(getTemporal().Now.instant().toString()).toMatch(/Z$/);
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
});
|
package/src/time/geo-tz.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from "@cosmicdrift/kumiko-types/geo-tz";
|
|
1
|
+
export type * from "@cosmicdrift/kumiko-types/geo-tz";
|
package/src/time/polyfill.ts
CHANGED
|
@@ -1,66 +1,55 @@
|
|
|
1
|
-
// Temporal
|
|
1
|
+
// Temporal polyfill bootstrap.
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
3
|
+
// Temporal is native in Chromium 144+ / Firefox 139+, but missing in Safari,
|
|
4
|
+
// iOS, and Hermes. Bun/Node coverage is incomplete. Boot installs
|
|
5
|
+
// `temporal-polyfill` once so server/web/mobile share one API.
|
|
6
6
|
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
// `globalThis.Temporal` schon existiert). Wir cachen das Ergebnis trotzdem
|
|
14
|
-
// in einem Modul-Singleton, damit Boot-Performance nicht jedes Mal das
|
|
15
|
-
// Polyfill-Modul-Loading triggert.
|
|
7
|
+
// Idempotent: if `globalThis.Temporal` already exists the call is a no-op.
|
|
8
|
+
// The cache is the live global check — not a sticky module flag — so tests
|
|
9
|
+
// that delete the ambient global (fw#1550) still re-install on the next call.
|
|
10
|
+
// Re-install uses the value export + Object.assign (not `temporal-polyfill/global`
|
|
11
|
+
// side-effect import): ESM caches the side-effect module and would not re-run
|
|
12
|
+
// after a teardown.
|
|
16
13
|
|
|
17
|
-
let polyfillInstalled = false;
|
|
18
14
|
let polyfillPromise: Promise<void> | null = null;
|
|
19
15
|
|
|
20
16
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* - Wenn Native Temporal existiert (moderne Browser, neueres Bun): No-Op.
|
|
24
|
-
* - Sonst: lädt `temporal-polyfill/global` (installiert globalThis.Temporal).
|
|
25
|
-
*
|
|
26
|
-
* Wird einmal beim Framework-Boot aufgerufen. Feature-Code muss das nicht
|
|
27
|
-
* selbst tun — `Temporal` ist nach dem Boot überall verfügbar.
|
|
17
|
+
* Ensure `globalThis.Temporal` is available. Idempotent.
|
|
28
18
|
*/
|
|
29
19
|
export async function ensureTemporalPolyfill(): Promise<void> {
|
|
30
|
-
|
|
31
|
-
|
|
20
|
+
if ("Temporal" in globalThis) {
|
|
21
|
+
// skip: Temporal already on globalThis (native or prior polyfill)
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
32
24
|
if (polyfillPromise) {
|
|
33
25
|
await polyfillPromise;
|
|
34
|
-
|
|
35
|
-
|
|
26
|
+
if ("Temporal" in globalThis) {
|
|
27
|
+
// skip: Concurrent boot — peer call finished install
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
// Peer resolved but global still missing (torn down mid-flight).
|
|
31
|
+
polyfillPromise = null;
|
|
36
32
|
}
|
|
37
33
|
|
|
38
34
|
polyfillPromise = (async () => {
|
|
39
|
-
// Runtime-Check entgegen Type-System-Annahme: temporal-spec deklariert
|
|
40
|
-
// Temporal ambient, aber ohne Polyfill fehlt es zur Laufzeit. `in`-Check
|
|
41
|
-
// ist die sauberste Prüfung, ohne Cast + ohne TS-Truthy-Warnings.
|
|
42
35
|
if ("Temporal" in globalThis) {
|
|
43
|
-
|
|
44
|
-
// skip: Native Temporal vorhanden — Polyfill nicht nötig.
|
|
36
|
+
// skip: raced native/peer install
|
|
45
37
|
return;
|
|
46
38
|
}
|
|
47
|
-
//
|
|
48
|
-
//
|
|
49
|
-
await import("temporal-polyfill
|
|
50
|
-
|
|
39
|
+
// Value export — assign ourselves so teardown + re-call still works
|
|
40
|
+
// (side-effect `temporal-polyfill/global` is ESM-cached and silent on re-import).
|
|
41
|
+
const { Temporal } = await import("temporal-polyfill");
|
|
42
|
+
Object.assign(globalThis, { Temporal });
|
|
51
43
|
})();
|
|
52
44
|
|
|
53
45
|
await polyfillPromise;
|
|
54
46
|
}
|
|
55
47
|
|
|
56
48
|
/**
|
|
57
|
-
* Type-safe
|
|
58
|
-
*
|
|
59
|
-
* `ensureTemporalPolyfill()` einmal awaiten und danach `Temporal` global
|
|
60
|
-
* nutzen, oder über diesen Helper die Sicherheit haben.
|
|
49
|
+
* Type-safe access to globalThis.Temporal. Throws if the polyfill has not
|
|
50
|
+
* been installed yet (boot-order bug).
|
|
61
51
|
*/
|
|
62
52
|
export function getTemporal(): typeof Temporal {
|
|
63
|
-
// Runtime-Check entgegen Type-System-Annahme — siehe ensureTemporalPolyfill.
|
|
64
53
|
if (!("Temporal" in globalThis)) {
|
|
65
54
|
throw new Error(
|
|
66
55
|
"Temporal not available. Call ensureTemporalPolyfill() during framework boot before any time-related code runs.",
|
package/src/time/tz-context.ts
CHANGED
|
@@ -1,25 +1,29 @@
|
|
|
1
|
-
// ctx.tz —
|
|
1
|
+
// ctx.tz — the feature-code API for TZ operations.
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
// - "
|
|
5
|
-
// - "
|
|
6
|
-
// - "
|
|
7
|
-
// - "ZonedDateTime → JSON
|
|
8
|
-
// - "JSON
|
|
3
|
+
// A consistent shape for every date/time need in handler code:
|
|
4
|
+
// - "now as a UTC instant" → ctx.tz.now()
|
|
5
|
+
// - "today in the tenant's TZ" → ctx.tz.today(ctx.tz.tenant)
|
|
6
|
+
// - "parse a wall-clock string" → ctx.tz.parse("2026-04-03T10:00", "Europe/Lisbon")
|
|
7
|
+
// - "ZonedDateTime → JSON pair" → ctx.tz.toLocatedJson(zdt)
|
|
8
|
+
// - "JSON pair → ZonedDateTime" → ctx.tz.fromLocatedJson({ at, tz })
|
|
9
9
|
//
|
|
10
|
-
// Feature
|
|
11
|
-
//
|
|
10
|
+
// Feature code should no longer call `new Date()` — the lint rule for that
|
|
11
|
+
// lands in a later iteration, once all existing usages are migrated.
|
|
12
12
|
//
|
|
13
|
-
// `tenant` + `user`
|
|
14
|
-
//
|
|
15
|
-
//
|
|
13
|
+
// `tenant` + `user` are the TZ defaults for the current request. Currently
|
|
14
|
+
// both default to "UTC" — once tenant.timezone + user.timezone fields
|
|
15
|
+
// exist, they get read from the request context here.
|
|
16
16
|
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
17
|
+
// The pure type contracts (TzContext, TzContextOptions, LocatedTimestampJson)
|
|
18
|
+
// live in @cosmicdrift/kumiko-types/tz-context — only the factories are here.
|
|
19
19
|
|
|
20
20
|
import type { TzContext, TzContextOptions } from "@cosmicdrift/kumiko-types/tz-context";
|
|
21
|
-
import {
|
|
21
|
+
import { Temporal as TemporalPolyfill } from "temporal-polyfill";
|
|
22
|
+
import { ensureTemporalPolyfill } from "./polyfill";
|
|
22
23
|
|
|
24
|
+
// Back-compat shim: re-exported so existing `from "@cosmicdrift/kumiko-framework/time/tz-context"`
|
|
25
|
+
// imports keep working. Prefer importing from @cosmicdrift/kumiko-types/tz-context directly in
|
|
26
|
+
// new code — drop this re-export once no framework-path imports remain.
|
|
23
27
|
export type {
|
|
24
28
|
LocatedTimestampJson,
|
|
25
29
|
TzContext,
|
|
@@ -27,15 +31,17 @@ export type {
|
|
|
27
31
|
} from "@cosmicdrift/kumiko-types/tz-context";
|
|
28
32
|
|
|
29
33
|
/**
|
|
30
|
-
* Factory:
|
|
31
|
-
*
|
|
32
|
-
*
|
|
34
|
+
* Factory: creates a TzContext for the current request.
|
|
35
|
+
* Uses temporal-polyfill's module export (not globalThis.Temporal) so
|
|
36
|
+
* buildHandlerContext stays ambient-free (fw#1525/#1550).
|
|
33
37
|
*/
|
|
34
38
|
export function createTzContext(options: TzContextOptions = {}): TzContext {
|
|
35
|
-
const T = getTemporal();
|
|
36
39
|
const tenant = options.tenant ?? "UTC";
|
|
37
40
|
const user = options.user ?? tenant;
|
|
38
41
|
const geoTz = options.geoTz;
|
|
42
|
+
// @cast-boundary temporal-polyfill-vs-ambient: same TC39 Temporal values at
|
|
43
|
+
// runtime; TzContext is typed against ambient Temporal from temporal-spec.
|
|
44
|
+
const T = TemporalPolyfill as unknown as typeof Temporal;
|
|
39
45
|
|
|
40
46
|
return {
|
|
41
47
|
tenant,
|
|
@@ -52,9 +58,9 @@ export function createTzContext(options: TzContextOptions = {}): TzContext {
|
|
|
52
58
|
parse: (wallClock: string, tz: string) => T.PlainDateTime.from(wallClock).toZonedDateTime(tz),
|
|
53
59
|
toInstant: (zdt) => zdt.toInstant(),
|
|
54
60
|
toLocatedJson: (zdt) => ({
|
|
55
|
-
// Wall-
|
|
56
|
-
// .toPlainDateTime().toString()
|
|
57
|
-
//
|
|
61
|
+
// Wall-clock WITHOUT offset (no "Z", no "+01:00") plus the IANA name.
|
|
62
|
+
// .toPlainDateTime().toString() returns "YYYY-MM-DDTHH:MM:SS[.fff]"
|
|
63
|
+
// without an offset — exactly our contract.
|
|
58
64
|
at: zdt.toPlainDateTime().toString(),
|
|
59
65
|
tz: zdt.timeZoneId,
|
|
60
66
|
}),
|
|
@@ -84,8 +90,8 @@ export function createTzContext(options: TzContextOptions = {}): TzContext {
|
|
|
84
90
|
}
|
|
85
91
|
|
|
86
92
|
/**
|
|
87
|
-
* Convenience:
|
|
88
|
-
*
|
|
93
|
+
* Convenience: ensures the polyfill is loaded AND creates the TzContext in
|
|
94
|
+
* one await. Prefer this in boot code.
|
|
89
95
|
*/
|
|
90
96
|
export async function createTzContextAsync(options?: TzContextOptions): Promise<TzContext> {
|
|
91
97
|
await ensureTemporalPolyfill();
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { Temporal as PolyfillTemporal } from "temporal-polyfill";
|
|
3
|
+
import { withoutAmbientTemporal } from "../../testing/without-ambient-temporal";
|
|
4
|
+
import { stringifyJson } from "../safe-json";
|
|
5
|
+
|
|
6
|
+
describe("stringifyJson — Temporal.Instant without ambient Temporal", () => {
|
|
7
|
+
test("serializes polyfill Instant when globalThis.Temporal is missing", async () => {
|
|
8
|
+
const instant = PolyfillTemporal.Instant.from("2026-01-01T00:00:00Z");
|
|
9
|
+
await withoutAmbientTemporal(() => {
|
|
10
|
+
// Must not ReferenceError on bare Temporal (old instanceof branch).
|
|
11
|
+
expect(stringifyJson({ at: instant })).toBe('{"at":"2026-01-01T00:00:00Z"}');
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
});
|
package/src/utils/safe-json.ts
CHANGED
|
@@ -29,7 +29,9 @@ export function parseJsonOrThrow<T>(raw: string, context: string): T {
|
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
/** JSON.stringify that survives BigInt
|
|
32
|
+
/** JSON.stringify that survives BigInt values from DB rows.
|
|
33
|
+
* Temporal.Instant serializes via its own toJSON() — no Instant branch needed
|
|
34
|
+
* (and `v instanceof Temporal.Instant` would ReferenceError without ambient). */
|
|
33
35
|
export function stringifyJson(value: unknown): string {
|
|
34
36
|
return JSON.stringify(value, (_key, v) => {
|
|
35
37
|
if (typeof v === "bigint") {
|
|
@@ -43,7 +45,6 @@ export function stringifyJson(value: unknown): string {
|
|
|
43
45
|
}
|
|
44
46
|
return v.toString();
|
|
45
47
|
}
|
|
46
|
-
if (v instanceof Temporal.Instant) return v.toString();
|
|
47
48
|
return v;
|
|
48
49
|
});
|
|
49
50
|
}
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
// Dilution reclaim: integration lcov registers every facade getter as DA:N,0
|
|
2
|
-
// when the file loads, but unit only instruments getters that a test touches.
|
|
3
|
-
// Calling every getter once in unit raises LF+LH so the badge merge stops
|
|
4
|
-
// counting those one-liners as uncovered.
|
|
5
|
-
|
|
6
|
-
import { describe, expect, test } from "bun:test";
|
|
7
|
-
import { createRegistry } from "../registry";
|
|
8
|
-
|
|
9
|
-
describe("buildRegistryFacade — getter sweep", () => {
|
|
10
|
-
test("empty registry: every getter is callable without throw", () => {
|
|
11
|
-
const r = createRegistry([]);
|
|
12
|
-
|
|
13
|
-
expect(r.features.size).toBe(0);
|
|
14
|
-
expect(r.getFeature("x")).toBeUndefined();
|
|
15
|
-
expect(r.hasRateLimitedHandler()).toBe(false);
|
|
16
|
-
expect(r.getEntity("x")).toBeUndefined();
|
|
17
|
-
expect(r.getAllEntities().size).toBe(0);
|
|
18
|
-
expect(r.getWriteHandler("x")).toBeUndefined();
|
|
19
|
-
expect(r.getQueryHandler("x")).toBeUndefined();
|
|
20
|
-
expect(r.getAllQueryHandlers().size).toBe(0);
|
|
21
|
-
expect(r.getSearchableFields("x")).toEqual([]);
|
|
22
|
-
expect(r.getSortableFields("x")).toEqual([]);
|
|
23
|
-
expect(r.getRelations("x")).toEqual({});
|
|
24
|
-
expect(r.getSearchIncludes("x").size).toBe(0);
|
|
25
|
-
expect(r.getIncomingRelations("x")).toEqual([]);
|
|
26
|
-
expect(r.getPreSaveHooks("x")).toEqual([]);
|
|
27
|
-
expect(r.getPostSaveHooks("x")).toEqual([]);
|
|
28
|
-
expect(r.getPreDeleteHooks("x")).toEqual([]);
|
|
29
|
-
expect(r.getPostDeleteHooks("x")).toEqual([]);
|
|
30
|
-
expect(r.getPreQueryHooks("x")).toEqual([]);
|
|
31
|
-
expect(r.getPostQueryHooks("x")).toEqual([]);
|
|
32
|
-
expect(r.getEntityPostSaveHooks("x")).toEqual([]);
|
|
33
|
-
expect(r.getEntityPreDeleteHooks("x")).toEqual([]);
|
|
34
|
-
expect(r.getEntityPostDeleteHooks("x")).toEqual([]);
|
|
35
|
-
expect(r.getEntityPostQueryHooks("x")).toEqual([]);
|
|
36
|
-
expect(r.getSearchPayloadExtensions("x")).toEqual([]);
|
|
37
|
-
expect(r.getAllTranslations()).toBeDefined();
|
|
38
|
-
expect(r.getHandlerEntity("x")).toBeUndefined();
|
|
39
|
-
expect(r.isHandlerSystemScoped("x")).toBe(false);
|
|
40
|
-
expect(r.getHandlerFeature("x")).toBeUndefined();
|
|
41
|
-
expect(r.getAllMetrics().size).toBe(0);
|
|
42
|
-
expect(r.getAllSecretKeys().size).toBe(0);
|
|
43
|
-
expect(r.getSecretKey("x")).toBeUndefined();
|
|
44
|
-
expect(r.getConfigKey("x")).toBeUndefined();
|
|
45
|
-
expect(r.getAllConfigKeys().size).toBe(0);
|
|
46
|
-
expect(r.getJob("x")).toBeUndefined();
|
|
47
|
-
expect(r.getAllJobs().size).toBe(0);
|
|
48
|
-
expect(r.getEvent("x")).toBeUndefined();
|
|
49
|
-
expect(r.getEventUpcasters().size).toBe(0);
|
|
50
|
-
expect(r.getExtension("x")).toBeUndefined();
|
|
51
|
-
expect(r.getExtensionUsages("x")).toEqual([]);
|
|
52
|
-
expect(r.getAllExtensionSelectors().size).toBe(0);
|
|
53
|
-
expect(r.getAllNotifications().size).toBe(0);
|
|
54
|
-
expect(r.getAllReferenceData()).toEqual([]);
|
|
55
|
-
expect(r.getAllConfigSeeds()).toEqual([]);
|
|
56
|
-
expect(r.getProjectionsForSource("x")).toEqual([]);
|
|
57
|
-
expect(r.getAllProjections().size).toBe(0);
|
|
58
|
-
expect(r.getAllStoreTables().size).toBe(0);
|
|
59
|
-
expect(r.getAllMultiStreamProjections().size).toBe(0);
|
|
60
|
-
expect(r.getMultiStreamProjectionFeature("x")).toBeUndefined();
|
|
61
|
-
expect(r.getAuthClaimsHooks()).toEqual([]);
|
|
62
|
-
expect(r.getAllClaimKeys().size).toBe(0);
|
|
63
|
-
expect(r.getClaimKey("x")).toBeUndefined();
|
|
64
|
-
expect(r.getAllScreens().size).toBe(0);
|
|
65
|
-
expect(r.getScreen("x")).toBeUndefined();
|
|
66
|
-
expect(r.getScreenFeature("x")).toBeUndefined();
|
|
67
|
-
expect(r.getScreensByEntity("x")).toEqual([]);
|
|
68
|
-
expect(r.getAllNavs().size).toBe(0);
|
|
69
|
-
expect(r.getNav("x")).toBeUndefined();
|
|
70
|
-
expect(r.getNavFeature("x")).toBeUndefined();
|
|
71
|
-
expect(r.getNavsByParent("x")).toEqual([]);
|
|
72
|
-
expect(r.getTopLevelNavs()).toEqual([]);
|
|
73
|
-
expect(r.getAllWorkspaces().size).toBe(0);
|
|
74
|
-
expect(r.getWorkspace("x")).toBeUndefined();
|
|
75
|
-
expect(r.getWorkspaceFeature("x")).toBeUndefined();
|
|
76
|
-
expect(r.getWorkspaceNavs("x")).toEqual([]);
|
|
77
|
-
expect(r.getDefaultWorkspace()).toBeUndefined();
|
|
78
|
-
expect(r.getTreeActions("x")).toBeUndefined();
|
|
79
|
-
});
|
|
80
|
-
});
|