@cosmicdrift/kumiko-framework 0.158.2 → 0.160.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.
Files changed (168) hide show
  1. package/package.json +7 -2
  2. package/src/__tests__/consumer-cli.integration.test.ts +110 -0
  3. package/src/api/__tests__/api.test.ts +65 -0
  4. package/src/api/__tests__/auth-routes-cookie.test.ts +17 -1
  5. package/src/api/__tests__/auth-routes-invalid-body-invite.test.ts +237 -0
  6. package/src/api/__tests__/auth-routes-mfa-verify.test.ts +1 -0
  7. package/src/api/__tests__/csrf-constants-sync.test.ts +20 -0
  8. package/src/api/__tests__/dispatcher-live.integration.test.ts +74 -0
  9. package/src/api/__tests__/jwt.test.ts +150 -1
  10. package/src/api/__tests__/login-rate-limiter-sweep.test.ts +41 -0
  11. package/src/api/__tests__/server-boot-guards.test.ts +71 -0
  12. package/src/api/__tests__/server-jwt-ttl.test.ts +58 -0
  13. package/src/api/api-constants.ts +5 -0
  14. package/src/api/auth-middleware.ts +48 -59
  15. package/src/api/auth-routes.ts +51 -17
  16. package/src/api/index.ts +3 -3
  17. package/src/api/jwt.ts +148 -7
  18. package/src/api/pii-leak-guard.ts +5 -2
  19. package/src/api/routes.ts +57 -0
  20. package/src/api/server.ts +19 -5
  21. package/src/bun-db/__tests__/select-many-retry.test.ts +79 -0
  22. package/src/bun-db/query.ts +46 -27
  23. package/src/consumer-cli.ts +87 -0
  24. package/src/crypto/__tests__/pii-field-encryption.test.ts +69 -13
  25. package/src/crypto/blind-index.ts +8 -4
  26. package/src/crypto/event-pii.ts +1 -0
  27. package/src/crypto/kms-adapter.ts +2 -118
  28. package/src/crypto/pii-field-encryption.ts +49 -15
  29. package/src/db/__tests__/build-filter-where.test.ts +34 -0
  30. package/src/db/__tests__/event-store-executor-context.pii-roundtrip.test.ts +67 -0
  31. package/src/db/__tests__/event-store-executor-write-verbs.integration.test.ts +396 -0
  32. package/src/db/__tests__/event-store-executor.integration.test.ts +5 -5
  33. package/src/db/blind-index-cleanup.ts +3 -1
  34. package/src/db/connection.ts +3 -11
  35. package/src/db/cursor.ts +1 -18
  36. package/src/db/dialect.ts +8 -19
  37. package/src/db/encryption.ts +2 -3
  38. package/src/db/entity-table-meta-types.ts +2 -0
  39. package/src/db/entity-table-meta.ts +16 -90
  40. package/src/db/event-store-executor.ts +4 -96
  41. package/src/db/queries/backfill-pii.ts +1 -0
  42. package/src/db/queries/event-consumer.ts +35 -2
  43. package/src/db/table-builder.ts +2 -19
  44. package/src/db/tenant-db.ts +6 -55
  45. package/src/engine/__tests__/boot-validator-boot-check.test.ts +99 -0
  46. package/src/engine/__tests__/boot-validator-gdpr-storage.test.ts +7 -233
  47. package/src/engine/__tests__/boot-validator.test.ts +46 -0
  48. package/src/engine/__tests__/codemod-pipeline.test.ts +139 -10
  49. package/src/engine/__tests__/define-roles.test.ts +21 -0
  50. package/src/engine/__tests__/engine.test.ts +28 -0
  51. package/src/engine/__tests__/event-type-map-augmentation.test.ts +24 -0
  52. package/src/engine/__tests__/registry-facade-sweep.test.ts +80 -0
  53. package/src/engine/__tests__/registry.test.ts +40 -0
  54. package/src/engine/__tests__/store-table.test.ts +12 -0
  55. package/src/engine/__tests__/tier-resolver-extension.test.ts +19 -1
  56. package/src/engine/boot-validator/action-wiring.ts +1 -1
  57. package/src/engine/boot-validator/boot-check.ts +21 -0
  58. package/src/engine/boot-validator/entity-handler.ts +10 -1
  59. package/src/engine/boot-validator/entity-list-screens.ts +1 -1
  60. package/src/engine/boot-validator/gdpr-storage.ts +0 -112
  61. package/src/engine/boot-validator/index.ts +3 -9
  62. package/src/engine/boot-validator/screens.ts +1 -1
  63. package/src/engine/define-feature.ts +2 -0
  64. package/src/engine/define-handler.ts +11 -91
  65. package/src/engine/entity-handlers.ts +15 -27
  66. package/src/engine/feature-ast/__tests__/canonical-form.test.ts +11 -1
  67. package/src/engine/feature-ast/__tests__/parse.test.ts +983 -3
  68. package/src/engine/feature-ast/__tests__/patch.test.ts +168 -0
  69. package/src/engine/feature-ast/__tests__/patcher.test.ts +7 -0
  70. package/src/engine/feature-ast/__tests__/render-roundtrip.test.ts +9 -0
  71. package/src/engine/feature-ast/extractors/handlers.ts +19 -2
  72. package/src/engine/feature-ast/extractors/index.ts +1 -0
  73. package/src/engine/feature-ast/index.ts +2 -0
  74. package/src/engine/feature-ast/parse.ts +3 -0
  75. package/src/engine/feature-ast/patch.ts +2 -0
  76. package/src/engine/feature-ast/patcher.ts +21 -0
  77. package/src/engine/feature-ast/patterns.ts +16 -0
  78. package/src/engine/feature-ast/render.ts +15 -0
  79. package/src/engine/feature-builder-state.ts +6 -0
  80. package/src/engine/feature-config-events-jobs.ts +1 -1
  81. package/src/engine/feature-entity-handlers.ts +36 -2
  82. package/src/engine/feature-ui-extensions.ts +5 -1
  83. package/src/engine/field-helpers.ts +31 -0
  84. package/src/engine/handler-helpers.ts +26 -0
  85. package/src/engine/hook-helpers.ts +14 -0
  86. package/src/engine/index.ts +5 -2
  87. package/src/engine/ownership.ts +22 -76
  88. package/src/engine/pattern-library/__tests__/library.test.ts +9 -0
  89. package/src/engine/pattern-library/library.ts +2 -0
  90. package/src/engine/pattern-library/mixed-schemas.ts +37 -0
  91. package/src/engine/registry-facade.ts +9 -0
  92. package/src/engine/registry-ingest.ts +10 -0
  93. package/src/engine/registry-state.ts +3 -0
  94. package/src/engine/registry-validate.ts +1 -1
  95. package/src/engine/screen-helpers.ts +54 -0
  96. package/src/engine/tier-resolver-extension.ts +3 -2
  97. package/src/engine/types/config.ts +2 -497
  98. package/src/engine/types/define-handler.ts +2 -0
  99. package/src/engine/types/entity-handlers.ts +2 -0
  100. package/src/engine/types/event-type-map.ts +1 -37
  101. package/src/engine/types/feature.ts +2 -976
  102. package/src/engine/types/fields.ts +2 -697
  103. package/src/engine/types/handlers.ts +2 -839
  104. package/src/engine/types/hooks.ts +2 -184
  105. package/src/engine/types/http-route.ts +1 -72
  106. package/src/engine/types/identifiers.ts +1 -47
  107. package/src/engine/types/index.ts +66 -33
  108. package/src/engine/types/nav.ts +2 -67
  109. package/src/engine/types/ownership.ts +2 -0
  110. package/src/engine/types/projection.ts +2 -165
  111. package/src/engine/types/relations.ts +1 -51
  112. package/src/engine/types/screen.ts +2 -793
  113. package/src/engine/types/step.ts +2 -334
  114. package/src/engine/types/target-ref.ts +1 -21
  115. package/src/engine/types/tree-node.ts +1 -129
  116. package/src/engine/types/workspace.ts +2 -42
  117. package/src/entrypoint/index.ts +2 -2
  118. package/src/errors/write-error-info.ts +6 -22
  119. package/src/event-store/__tests__/event-store.integration.test.ts +31 -0
  120. package/src/event-store/__tests__/unscoped-stream-primitives.guard.test.ts +43 -0
  121. package/src/event-store/errors.ts +2 -35
  122. package/src/event-store/event-store.ts +28 -51
  123. package/src/event-store/events-schema.ts +1 -10
  124. package/src/event-store/index.ts +3 -2
  125. package/src/event-store/snapshot.ts +11 -35
  126. package/src/event-store/types.ts +2 -0
  127. package/src/files/__tests__/in-memory-provider.contract.test.ts +4 -0
  128. package/src/files/file-handle.ts +2 -19
  129. package/src/files/provider-resolver.ts +3 -5
  130. package/src/files/types.ts +5 -54
  131. package/src/i18n/required-surface-keys.ts +1 -1
  132. package/src/jobs/__tests__/jobs.integration.test.ts +102 -1
  133. package/src/logging/types.ts +1 -7
  134. package/src/observability/types/index.ts +1 -29
  135. package/src/observability/types/metric.ts +1 -56
  136. package/src/observability/types/provider.ts +1 -32
  137. package/src/observability/types/span.ts +1 -58
  138. package/src/pipeline/__tests__/dispatcher.test.ts +134 -1
  139. package/src/pipeline/__tests__/event-dispatcher-delivery-max-attempts.test.ts +126 -0
  140. package/src/pipeline/__tests__/event-dispatcher-rearm.integration.test.ts +180 -0
  141. package/src/pipeline/__tests__/lifecycle-pipeline.test.ts +208 -0
  142. package/src/pipeline/dispatch-shared.ts +51 -3
  143. package/src/pipeline/dispatch-stream.ts +74 -0
  144. package/src/pipeline/dispatcher-utils.ts +1 -1
  145. package/src/pipeline/dispatcher.ts +7 -0
  146. package/src/pipeline/entity-cache.ts +2 -33
  147. package/src/pipeline/event-consumer-state.ts +28 -3
  148. package/src/pipeline/event-dispatcher-admin.ts +4 -0
  149. package/src/pipeline/event-dispatcher-delivery.ts +29 -3
  150. package/src/pipeline/event-dispatcher.ts +27 -1
  151. package/src/pipeline/multi-stream-apply-context.ts +4 -42
  152. package/src/pipeline/system-hooks.ts +7 -0
  153. package/src/rate-limit/resolver.ts +10 -30
  154. package/src/search/types.ts +1 -39
  155. package/src/secrets/__tests__/envelope-cipher.test.ts +2 -30
  156. package/src/secrets/__tests__/envelope.test.ts +1 -1
  157. package/src/secrets/envelope-cipher.ts +17 -45
  158. package/src/secrets/types.ts +2 -177
  159. package/src/stack/__tests__/event-collector.test.ts +42 -0
  160. package/src/testing/__tests__/late-bound.test.ts +25 -0
  161. package/src/testing/__tests__/wait-for.test.ts +53 -0
  162. package/src/testing/boot-validator-fixture.ts +1 -1
  163. package/src/testing/file-provider-contract.ts +84 -0
  164. package/src/testing/handler-context.ts +1 -1
  165. package/src/testing/index.ts +1 -0
  166. package/src/time/geo-tz.ts +1 -32
  167. package/src/time/tz-context.ts +9 -56
  168. package/src/ui-types/index.ts +7 -7
@@ -1,184 +1,2 @@
1
- import type { StoredEvent } from "../../event-store/event-store";
2
- import type { AppContext } from "./handlers";
3
- import type { EntityId } from "./identifiers";
4
-
5
- // --- Validation ---
6
-
7
- export type ValidationError = {
8
- readonly field: string;
9
- readonly error: string;
10
- };
11
-
12
- export type ValidationHookFn = (
13
- data: Readonly<Record<string, unknown>>,
14
- ) => readonly ValidationError[] | null;
15
-
16
- // --- Save/Delete Context (what hooks receive) ---
17
-
18
- export type SaveContext = {
19
- readonly kind: "save";
20
- readonly id: EntityId;
21
- readonly data: Readonly<Record<string, unknown>>;
22
- readonly changes: Readonly<Record<string, unknown>>;
23
- readonly previous: Readonly<Record<string, unknown>>;
24
- readonly isNew: boolean;
25
- readonly entityName?: string | undefined;
26
- // The event that produced this save. Populated by the event-store-executor;
27
- // the pipeline uses it to drive projections inside the same transaction.
28
- // Optional because hand-crafted SaveContexts (tests, custom executors) may
29
- // not have an event — projections just skip in that case.
30
- readonly event?: StoredEvent | undefined;
31
- };
32
-
33
- export type DeleteContext = {
34
- readonly kind: "delete";
35
- readonly id: EntityId;
36
- readonly data: Readonly<Record<string, unknown>>;
37
- readonly entityName?: string | undefined;
38
- // See SaveContext.event — same semantics.
39
- readonly event?: StoredEvent | undefined;
40
- };
41
-
42
- export type LifecycleResult = SaveContext | DeleteContext;
43
-
44
- // --- Lifecycle Hooks ---
45
-
46
- export type PreSaveHookFn = (
47
- changes: Record<string, unknown>,
48
- context: AppContext & {
49
- readonly previous: Readonly<Record<string, unknown>>;
50
- readonly isNew: boolean;
51
- },
52
- ) => Promise<Record<string, unknown>>;
53
-
54
- export type PostSaveHookFn = (result: SaveContext, context: AppContext) => Promise<void>;
55
-
56
- // Batch-variant: called once at the end of a dispatcher batch with every
57
- // successful SaveContext. The per-save PostSaveHookFn still fires for
58
- // side-effects that need per-entity semantics (SSE); PostSaveBatch exists
59
- // for adapters that can amortise work across the whole batch (e.g. search
60
- // index batch-writes, bulk webhook fanout).
61
- export type PostSaveBatchHookFn = (
62
- results: readonly SaveContext[],
63
- context: AppContext,
64
- ) => Promise<void>;
65
-
66
- export type PreDeleteHookFn = (payload: DeleteContext, context: AppContext) => Promise<void>;
67
-
68
- export type PostDeleteHookFn = (payload: DeleteContext, context: AppContext) => Promise<void>;
69
-
70
- export type PostDeleteBatchHookFn = (
71
- payloads: readonly DeleteContext[],
72
- context: AppContext,
73
- ) => Promise<void>;
74
-
75
- export type PreQueryHookFn = (
76
- payload: Record<string, unknown>,
77
- context: AppContext,
78
- ) => Promise<Record<string, unknown>>;
79
-
80
- // postQuery — fires after query-handler-execute, before field-access-read-filter.
81
- // Hook receives normalized rows + entityName + can mutate rows (e.g., merge
82
- // custom-fields, add computed-counts, attach related-data). Mutation result
83
- // replaces original rows. Hook is responsible for its own field-access-logic
84
- // on added fields (field-access-filter only knows entity's stammfields).
85
- export type PostQueryHookFn = (
86
- result: {
87
- // undefined for standalone queries (no-colon handler names like
88
- // "ns:dashboard") — those have no backing entity, but handler-keyed
89
- // postQuery hooks still fire on them.
90
- readonly entityName: string | undefined;
91
- readonly rows: ReadonlyArray<Record<string, unknown>>;
92
- },
93
- context: AppContext,
94
- ) => Promise<{ rows: ReadonlyArray<Record<string, unknown>> }>;
95
-
96
- export type LifecycleHookFn =
97
- | PreSaveHookFn
98
- | PostSaveHookFn
99
- | PreDeleteHookFn
100
- | PostDeleteHookFn
101
- | PreQueryHookFn
102
- | PostQueryHookFn;
103
-
104
- // --- Hook Phases ---
105
- //
106
- // inTransaction: Hook runs inside the DB transaction. Failures roll back
107
- // the entire write. Use for: DB-based side-effects (counter updates,
108
- // dependent entity writes).
109
- //
110
- // afterCommit (default): Hook runs after the transaction commits. Failures
111
- // are logged but don't affect the write. Use for: external systems
112
- // (SSE broadcast, search index, email, webhooks).
113
-
114
- export const HookPhases = {
115
- inTransaction: "inTransaction",
116
- afterCommit: "afterCommit",
117
- } as const;
118
-
119
- export type HookPhase = (typeof HookPhases)[keyof typeof HookPhases];
120
-
121
- // Owner-tag shared across every hook structure. The lifecycle pipeline uses
122
- // it to skip hooks whose owning feature is globally disabled:
123
- // - A concrete feature name like "orders" → subject to the feature-toggle
124
- // filter (skipped when "orders" is disabled).
125
- // - "*" (star) → invariant plumbing, never filtered. Reserved for
126
- // extension-provided hooks and framework-internal hooks that belong to
127
- // the pipeline itself, not a feature.
128
- // - Omitted (undefined) → treated as "*". Supports tests that hand-build
129
- // HookMap objects without caring about ownership.
130
- export type HookOwner = { readonly featureName?: string };
131
-
132
- export type PhasedHook<TFn> = {
133
- readonly fn: TFn;
134
- readonly phase: HookPhase;
135
- } & HookOwner;
136
-
137
- // Flat (non-phased) hook — preSave, preQuery. Same owner contract, no
138
- // phase semantics because these hooks run exactly once per handler pass
139
- // before/around the DB transaction.
140
- export type OwnedFn<TFn> = {
141
- readonly fn: TFn;
142
- } & HookOwner;
143
-
144
- // --- Hook Maps ---
145
-
146
- // Slots are optional: defineFeature materializes every slot, but hand-built
147
- // FeatureDefinitions at system boundaries (test fixtures, partial boots —
148
- // see registry.test.ts "slot robustness") legitimately omit them, and the
149
- // registry merge paths tolerate undefined. The type mirrors that contract.
150
- export type HookMap = {
151
- readonly validation?: Readonly<Record<string, ValidationHookFn>>;
152
- readonly preSave?: Readonly<Record<string, readonly OwnedFn<PreSaveHookFn>[]>>;
153
- readonly postSave?: Readonly<Record<string, readonly PhasedHook<PostSaveHookFn>[]>>;
154
- readonly preDelete?: Readonly<Record<string, readonly PhasedHook<PreDeleteHookFn>[]>>;
155
- readonly postDelete?: Readonly<Record<string, readonly PhasedHook<PostDeleteHookFn>[]>>;
156
- readonly preQuery?: Readonly<Record<string, readonly OwnedFn<PreQueryHookFn>[]>>;
157
- readonly postQuery?: Readonly<Record<string, readonly OwnedFn<PostQueryHookFn>[]>>;
158
- };
159
-
160
- export type EntityHookMap = {
161
- readonly postSave?: Readonly<Record<string, readonly PhasedHook<PostSaveHookFn>[]>>;
162
- readonly preDelete?: Readonly<Record<string, readonly PhasedHook<PreDeleteHookFn>[]>>;
163
- readonly postDelete?: Readonly<Record<string, readonly PhasedHook<PostDeleteHookFn>[]>>;
164
- readonly postQuery?: Readonly<Record<string, readonly OwnedFn<PostQueryHookFn>[]>>;
165
- };
166
-
167
- // Search-Payload-Extension (F3) — contributor function that adds flat
168
- // fields to an entity's search-document. Fires synchronously during
169
- // buildSearchDocument (in `system-hooks.ts`), receives current entity
170
- // state, returns extra fields to merge into the search-index payload.
171
- //
172
- // Use-cases: custom-fields-bundle (merge customFields-jsonb-keys flat
173
- // into index), tags-bundle (project tags-array as searchable), computed-
174
- // fields (denormalize related-counts).
175
- //
176
- // IMPORTANT: contributor must be deterministic per (entityName, entityId,
177
- // state). Async-allowed for future-proofing but discouraged — the
178
- // indexing path runs once per entity-write, sync extension is
179
- // near-zero-cost.
180
- export type SearchPayloadContributorFn = (args: {
181
- readonly entityName: string;
182
- readonly entityId: EntityId;
183
- readonly state: Record<string, unknown>;
184
- }) => Record<string, unknown> | Promise<Record<string, unknown>>;
1
+ // Legacy path re-exported for callers still importing this module directly.
2
+ export * from "@cosmicdrift/kumiko-types/hooks";
@@ -1,72 +1 @@
1
- // HTTP-Route-Definition feature-deklarierte HTTP-Endpoints außerhalb
2
- // der /api/write|query|batch-Pipeline. Use-Case: RSS/Atom-Feeds, OpenAPI-
3
- // Specs, OG-Image-Generators, Webhook-Receiver — alles wo der Feature-
4
- // Author das Wire-Format selbst kontrolliert.
5
- //
6
- // Pattern symmetrisch zu r.queryHandler / r.writeHandler: Definition als
7
- // Teil des Features (nicht des App-Bootstrapping). Phase-3 Multi-Tenant
8
- // wird trivial weil tenant-context via host-resolution greift.
9
- //
10
- // Escape-hatch bleibt: runProdApp.extraRoutes für hand-rolled Routes die
11
- // nichts mit einem Feature zu tun haben (z.B. plattform-spezifische
12
- // Static-Serving-Logic).
13
-
14
- import type { Context } from "hono";
15
-
16
- /** Subset von HTTP-Methoden den wir aktiv unterstützen. Hono spricht
17
- * alle, aber das hier sind die einzigen die ein Feature-Author
18
- * realistisch deklariert. */
19
- export type HttpRouteMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | "OPTIONS";
20
-
21
- /** Dependencies die der Handler vom Framework bekommt. App-Author kann
22
- * die App selbst aufrufen (`deps.app.fetch(...)` für intern-call) oder
23
- * direkt per dispatcher Daten ziehen. Db/Redis sind die rohen Connections
24
- * — wer Tenant-Scope braucht muss durch dispatcher.query gehen.
25
- *
26
- * Hono-typing: `Context<any, any>` weil das Hono-Type-Param-Setup nur
27
- * intern relevant ist. Concrete Hono-app wird im Boot-Path zugewiesen. */
28
- export type HttpRouteHandlerDeps = {
29
- /** Die Hono-app — Handler kann via app.fetch(...) interne Routes
30
- * ansprechen (z.B. /api/query mit der vollen Auth-/Anonymous-Chain). */
31
- // biome-ignore lint/suspicious/noExplicitAny: Hono's generic-Param ist im Framework-Boundary unsichtbar
32
- readonly app: import("hono").Hono<any, any>;
33
- /** Run a query handler in-process, forcing a SPECIFIC tenant — WITHOUT
34
- * going through the public /api/query HTTP layer (no header parsing, no
35
- * anonymousAccess tenant resolution). The synthesized caller carries
36
- * anonymous-level access ONLY (same role a real anonymous request would
37
- * have, no more) — the primitive forces the tenant, not the privilege
38
- * level, so it stays safe to call from any `anonymous: true` route
39
- * without risking a field-level disclosure a real anonymous caller
40
- * couldn't already get. Use this whenever the route needs a tenant
41
- * other than the one the request resolves to (e.g. always
42
- * SYSTEM_TENANT_ID regardless of the visited host) — spoofing that via
43
- * an internal X-Tenant header on `app.fetch(...)` is indistinguishable
44
- * from an external client and gets rejected by resolverTrust:
45
- * "authoritative" anonymousAccess configs (see auth-middleware.ts). */
46
- readonly systemQuery: (
47
- type: string,
48
- payload: unknown,
49
- tenantId: import("./identifiers").TenantId,
50
- ) => Promise<unknown>;
51
- };
52
-
53
- export type HttpRouteHandler = (
54
- // biome-ignore lint/suspicious/noExplicitAny: Hono Context-Generics sind im Framework-Boundary unsichtbar
55
- c: Context<any, any>,
56
- deps: HttpRouteHandlerDeps,
57
- ) => Response | Promise<Response>;
58
-
59
- export type HttpRouteDefinition = {
60
- /** HTTP-Methode — bei Hono-Mount via app.{get,post,...}(path). */
61
- readonly method: HttpRouteMethod;
62
- /** URL-Pfad (Hono-Pattern, z.B. "/feed.xml" oder "/og/:tenantId.png"). */
63
- readonly path: string;
64
- /** Wenn true, bypasses die /api/*-Auth-Middleware. Default false —
65
- * Routes liegen außerhalb /api/* und sehen die Auth-Middleware
66
- * ohnehin nicht; das Flag ist semantisch (= "diese Route ist
67
- * bewusst öffentlich") für Boot-Validator + Doku. */
68
- readonly anonymous?: boolean;
69
- /** Hono-Handler. Bekommt Hono-Context + Framework-Deps; returnt
70
- * Response (sync oder async). */
71
- readonly handler: HttpRouteHandler;
72
- };
1
+ export * from "@cosmicdrift/kumiko-types/http-route";
@@ -1,47 +1 @@
1
- // Domain-identifier type aliases. Used everywhere a tenantId/userId/aggregateId
2
- // travels through the framework. One declaration per concept so future
3
- // representation changes (branded types, UUID validation, opaque wrappers)
4
- // land in a single place.
5
-
6
- // Tenant identifier — UUID string today. May become branded/opaque later
7
- // without touching call sites.
8
- export type TenantId = string;
9
-
10
- // Lowercase UUID (any RFC-4122 variant). Strict enough to keep client-
11
- // supplied junk (e.g. SQL fragments, path-traversal probes) out of the
12
- // pipeline; loose enough that v4 / v7 / nil all match. Any caller that
13
- // already holds a TenantId from a trusted source (JWT payload, server
14
- // config) skips this — the helper is for **untrusted input** crossing
15
- // the system boundary.
16
- const TENANT_ID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
17
-
18
- // Validates a candidate string against the tenantId format and returns it
19
- // as a TenantId, or `null` when it doesn't match. Use at every system
20
- // boundary that admits untrusted input (HTTP headers, cookies, query
21
- // params). Returning null instead of throwing keeps the caller in charge
22
- // of the rejection shape — middleware returns 400, batch jobs may filter
23
- // + log, and unit tests don't need a try/catch.
24
- export function parseTenantId(value: unknown): TenantId | null {
25
- if (typeof value !== "string") return null;
26
- if (!TENANT_ID_REGEX.test(value)) return null;
27
- return value;
28
- }
29
-
30
- // "System-scope" tenant marker: handlers carry this tenantId when the event
31
- // doesn't belong to any particular tenant (reference data, cross-tenant
32
- // jobs, global config). The concrete UUID is a valid v4 (not all-zeroes —
33
- // Postgres' UUID type rejects invalid variants), chosen to be easy to
34
- // eyeball in logs. Central constant so call sites don't re-type the string
35
- // and the isSystemTenant() check stays in sync.
36
- export const SYSTEM_TENANT_ID: TenantId = "00000000-0000-4000-8000-000000000000";
37
-
38
- export function isSystemTenant(tenantId: TenantId | null | undefined): boolean {
39
- return !tenantId || tenantId === SYSTEM_TENANT_ID;
40
- }
41
-
42
- // Primary-key identifier for any entity row. Two shapes coexist because of
43
- // the entity-def `idType` switch: classic CRUD entities keep `serial` (number),
44
- // while tenant + ES aggregates run on `uuid` (string). Call sites that pass
45
- // the id through to the DB layer stay agnostic; only code that formats ids
46
- // for URLs, logs, or cache keys needs `String(id)` — JS coerces both safely.
47
- export type EntityId = number | string;
1
+ export * from "@cosmicdrift/kumiko-types/identifiers";
@@ -1,14 +1,8 @@
1
- // Barrel: re-exports all types from logical modules
1
+ // Barrel: re-exports all types from @cosmicdrift/kumiko-types, plus the
2
+ // runtime helpers below that stay framework-side.
2
3
  // Duplicate types (OnDeleteStrategy, ConfigScope, ConcurrencyMode, LifecycleHookType)
3
4
  // are defined ONLY in constants.ts — re-exported here for backwards compatibility.
4
5
 
5
- // Re-export types that were duplicated in types.ts but are canonical in constants.ts
6
- export type {
7
- ConcurrencyMode,
8
- ConfigScope,
9
- LifecycleHookType,
10
- OnDeleteStrategy,
11
- } from "../constants";
12
6
  export type {
13
7
  ConfigAccessor,
14
8
  ConfigAccessorFactory,
@@ -53,15 +47,30 @@ export type {
53
47
  TranslationKeys,
54
48
  TranslationsDef,
55
49
  UiExtensionDef,
56
- } from "./config";
50
+ } from "@cosmicdrift/kumiko-types/config";
51
+ export type {
52
+ QueryHandlerDefinition,
53
+ StreamHandlerDefinition,
54
+ WriteHandlerDefinition,
55
+ WriteHandlerInput,
56
+ } from "@cosmicdrift/kumiko-types/define-handler";
57
+ export type {
58
+ EntityCrudRegistrar,
59
+ EntityCrudVerb,
60
+ EntityHandlerOptions,
61
+ EntityQueryHandlerOptions,
62
+ RegisterEntityCrudOptions,
63
+ } from "@cosmicdrift/kumiko-types/entity-handlers";
57
64
  // Cross-Feature Compile-Time-Type-Map — features extend per declare-module.
58
65
  export type {
59
66
  KumikoEntityTypeMap,
60
67
  KumikoEventTypeMap,
61
68
  KumikoHandlerPayloadMap,
62
69
  KumikoHandlerResultMap,
63
- } from "./event-type-map";
70
+ } from "@cosmicdrift/kumiko-types/event-type-map";
64
71
  export type {
72
+ BootCheckContext,
73
+ BootCheckFn,
65
74
  FeatureDefinition,
66
75
  FeatureMetricDef,
67
76
  FeatureMetricType,
@@ -76,7 +85,7 @@ export type {
76
85
  StoreTableOptions,
77
86
  UiHintOption,
78
87
  UiHints,
79
- } from "./feature";
88
+ } from "@cosmicdrift/kumiko-types/feature";
80
89
  export type {
81
90
  AnyFileFieldDef,
82
91
  BigIntFieldDef,
@@ -114,8 +123,7 @@ export type {
114
123
  TimestampFieldDef,
115
124
  TransitionMap,
116
125
  TzFieldDef,
117
- } from "./fields";
118
- export { DEFAULT_CURRENCIES, isFileField } from "./fields";
126
+ } from "@cosmicdrift/kumiko-types/fields";
119
127
  export type {
120
128
  AccessRule,
121
129
  AggregateStreamHandle,
@@ -154,6 +162,8 @@ export type {
154
162
  RateLimitOption,
155
163
  RateLimitPer,
156
164
  SessionUser,
165
+ StreamHandlerDef,
166
+ StreamHandlerFn,
157
167
  TryAppendEventFn,
158
168
  TryAppendEventResult,
159
169
  UnsafeAppendEventFn,
@@ -161,8 +171,7 @@ export type {
161
171
  WriteHandlerDef,
162
172
  WriteHandlerFn,
163
173
  WriteResult,
164
- } from "./handlers";
165
- export { resolveName, withResponseData } from "./handlers";
174
+ } from "@cosmicdrift/kumiko-types/handlers";
166
175
  export type {
167
176
  DeleteContext,
168
177
  EntityHookMap,
@@ -185,18 +194,32 @@ export type {
185
194
  SearchPayloadContributorFn,
186
195
  ValidationError,
187
196
  ValidationHookFn,
188
- } from "./hooks";
189
- export { HookPhases } from "./hooks";
197
+ } from "@cosmicdrift/kumiko-types/hooks";
190
198
  export type {
191
199
  HttpRouteDefinition,
192
200
  HttpRouteHandler,
193
201
  HttpRouteHandlerDeps,
194
202
  HttpRouteMethod,
195
- } from "./http-route";
203
+ } from "@cosmicdrift/kumiko-types/http-route";
196
204
  // Domain-identifier type aliases — see identifiers.ts for rationale.
197
- export type { EntityId, TenantId } from "./identifiers";
198
- export { isSystemTenant, parseTenantId, SYSTEM_TENANT_ID } from "./identifiers";
199
- export type { NavDefinition } from "./nav";
205
+ export type { EntityId, TenantId } from "@cosmicdrift/kumiko-types/identifiers";
206
+ export {
207
+ isSystemTenant,
208
+ parseTenantId,
209
+ SYSTEM_TENANT_ID,
210
+ } from "@cosmicdrift/kumiko-types/identifiers";
211
+ export type { NavDefinition } from "@cosmicdrift/kumiko-types/nav";
212
+ export type {
213
+ FromRule,
214
+ FromRuleKind,
215
+ OwnershipClause,
216
+ OwnershipMap,
217
+ OwnershipRef,
218
+ OwnershipRule,
219
+ SqlFragment,
220
+ WhereRule,
221
+ WhereRuleContext,
222
+ } from "@cosmicdrift/kumiko-types/ownership";
200
223
  export type {
201
224
  EntityProjectionExtension,
202
225
  MspErrorMode,
@@ -206,14 +229,14 @@ export type {
206
229
  ProjectionDefinition,
207
230
  ProjectionTable,
208
231
  SingleStreamApplyFn,
209
- } from "./projection";
232
+ } from "@cosmicdrift/kumiko-types/projection";
210
233
  export type {
211
234
  BelongsToRelation,
212
235
  EntityRelations,
213
236
  HasManyRelation,
214
237
  ManyToManyRelation,
215
238
  RelationDefinition,
216
- } from "./relations";
239
+ } from "@cosmicdrift/kumiko-types/relations";
217
240
  export type {
218
241
  ActionFormScreenDefinition,
219
242
  ConfigEditScreenDefinition,
@@ -252,14 +275,8 @@ export type {
252
275
  ScreenFilterOp,
253
276
  ScreenSlots,
254
277
  ToolbarAction,
255
- } from "./screen";
256
- export {
257
- isExtensionEditSection,
258
- isFormatSpec,
259
- normalizeEditField,
260
- normalizeListColumn,
261
- } from "./screen";
262
- export type { TargetRef } from "./target-ref";
278
+ } from "@cosmicdrift/kumiko-types/screen";
279
+ export type { TargetRef } from "@cosmicdrift/kumiko-types/target-ref";
263
280
  export type {
264
281
  Subscribe,
265
282
  TreeAction,
@@ -268,5 +285,21 @@ export type {
268
285
  TreeChildrenSubscribe,
269
286
  TreeNode,
270
287
  TreeNodeState,
271
- } from "./tree-node";
272
- export type { WorkspaceDefinition } from "./workspace";
288
+ } from "@cosmicdrift/kumiko-types/tree-node";
289
+ export type { WorkspaceDefinition } from "@cosmicdrift/kumiko-types/workspace";
290
+ // Re-export types that were duplicated in types.ts but are canonical in constants.ts
291
+ export type {
292
+ ConcurrencyMode,
293
+ ConfigScope,
294
+ LifecycleHookType,
295
+ OnDeleteStrategy,
296
+ } from "../constants";
297
+ export { DEFAULT_CURRENCIES, isFileField } from "../field-helpers";
298
+ export { resolveName, withResponseData } from "../handler-helpers";
299
+ export { HookPhases } from "../hook-helpers";
300
+ export {
301
+ isExtensionEditSection,
302
+ isFormatSpec,
303
+ normalizeEditField,
304
+ normalizeListColumn,
305
+ } from "../screen-helpers";
@@ -1,67 +1,2 @@
1
- import type { AccessRule } from "./handlers";
2
- import type { TargetRef } from "./target-ref";
3
- import type { TreeAction } from "./tree-node";
4
-
5
- // Nav entry declaration. Every feature that wants to appear in the app's
6
- // navigation tree registers one or more entries via r.nav(). The engine
7
- // keeps the list flat — ui-core's resolveNavigation assembles the parent/
8
- // child tree at render time, so changes (toggles, access-gating) don't
9
- // require re-indexing a tree shape server-side.
10
- //
11
- // Cross-feature references are allowed: `screen` may point at any
12
- // registered screen QN, `parent` at any registered nav QN. The boot
13
- // validator checks both references exist + rejects parent cycles.
14
- export type NavDefinition = {
15
- // Feature author writes the feature-local short id ("catalog"); the
16
- // registry overwrites `id` with the qualified name ("shop:nav:catalog")
17
- // in its stored copy. Callers of `registry.getNav(qn)` /
18
- // `getTopLevelNavs()` / `getNavsByParent(...)` always see the qualified
19
- // id — no parallel reverse index needed. `feature.navs[shortId]` on the
20
- // unregistered FeatureDefinition keeps the short form.
21
- readonly id: string;
22
- // i18n translation key. Resolved at render time by the renderer's
23
- // useTranslation hook; engine keeps it opaque.
24
- readonly label: string;
25
- // Icon key — whatever the icon registry of the active renderer understands.
26
- // Engine doesn't validate; unknown icons surface as a missing icon on screen,
27
- // not a boot failure.
28
- readonly icon?: string;
29
- // Qualified name of a parent nav entry ("<feature>:nav:<id>"). Omit for
30
- // top-level entries. Boot-validator rejects cycles + dangling refs.
31
- readonly parent?: string;
32
- // Sort weight within the parent's children (lower = earlier). Ties are
33
- // broken by registration order — features registered later appear lower.
34
- readonly order?: number;
35
- // Qualified name of the screen this entry navigates to
36
- // ("<feature>:screen:<id>"). Omit for pure grouping entries (a parent-only
37
- // nav node that renders a sub-tree but has no target screen itself).
38
- readonly screen?: string;
39
- // Polymorphes Klick-Ziel (öffnet die EditorPanel-Maske via Target-
40
- // Resolver) — Alternative zu `screen`. Ein Knoten trägt screen XOR
41
- // target; der Renderer dispatcht das target statt einen Route-Link zu
42
- // rendern. Gespiegelt aus dem alten Visual-Tree (TreeNode.target).
43
- readonly target?: TargetRef;
44
- // Hover-Actions rechts in der Zeile (VS-Code-Pattern) — erst bei Hover
45
- // sichtbar. Reihenfolge wie deklariert.
46
- readonly actions?: readonly TreeAction[];
47
- // „+"-Affordance am Knoten. Klick dispatcht createAction.target; der
48
- // Provider weiß was „leer befüllen" für ihn heißt (neuer Page-Slug etc.).
49
- readonly createAction?: TreeAction;
50
- // Children kommen zur Laufzeit aus einem registrierten nav-provider
51
- // (lazy beim Ausklappen, SSE-live via treeEntities), keyed auf diese
52
- // Nav-QN. Macht den Knoten expandable auch ohne statische children.
53
- readonly provider?: boolean;
54
- // Role / openToAll gate. The nav resolver hides entries the user can't
55
- // reach; leave unset to always show (engine stays un-opinionated about
56
- // who sees what — apps that need default-deny can set { roles: [] }).
57
- // If `screen` is set and `access` is left unset, the client-side nav
58
- // builder (buildNavRegistrySliceForApp) fills this in from the target
59
- // screen's own `access` — a nav entry never invites a role into a 403.
60
- // An explicit `access` here always wins over the screen's.
61
- readonly access?: AccessRule;
62
- // Workspace QNs this entry self-assigns to. Merged at boot with any
63
- // r.workspace({ nav: [...] }) explicit lists. Omit to leave workspace
64
- // membership decided solely by the workspace's nav list (or both empty
65
- // → entry belongs to no workspace).
66
- readonly workspaces?: readonly string[];
67
- };
1
+ // Legacy path re-exported for callers still importing this module directly.
2
+ export * from "@cosmicdrift/kumiko-types/nav";
@@ -0,0 +1,2 @@
1
+ // Legacy path — re-exported for callers still importing this module directly.
2
+ export * from "@cosmicdrift/kumiko-types/ownership";