@cosmicdrift/kumiko-framework 0.2.2 → 0.3.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 (191) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/package.json +124 -38
  3. package/src/__tests__/full-stack.integration.ts +2 -2
  4. package/src/api/auth-routes.ts +5 -5
  5. package/src/api/jwt.ts +2 -2
  6. package/src/api/route-registrars.ts +1 -1
  7. package/src/api/routes.ts +3 -3
  8. package/src/api/server.ts +6 -7
  9. package/src/auth/__tests__/roles.test.ts +24 -0
  10. package/src/auth/index.ts +7 -0
  11. package/src/auth/roles.ts +42 -0
  12. package/src/compliance/__tests__/duration-spec.test.ts +72 -0
  13. package/src/compliance/__tests__/profiles.test.ts +308 -0
  14. package/src/compliance/__tests__/sub-processors.test.ts +139 -0
  15. package/src/compliance/duration-spec.ts +44 -0
  16. package/src/compliance/index.ts +31 -0
  17. package/src/compliance/override-schema.ts +136 -0
  18. package/src/compliance/profiles.ts +427 -0
  19. package/src/compliance/sub-processors.ts +152 -0
  20. package/src/db/__tests__/big-int-field.test.ts +131 -0
  21. package/src/db/assert-exists-in.ts +2 -2
  22. package/src/db/cursor.ts +3 -3
  23. package/src/db/event-store-executor.ts +19 -13
  24. package/src/db/located-timestamp.ts +1 -1
  25. package/src/db/money.ts +12 -2
  26. package/src/db/pg-error.ts +1 -1
  27. package/src/db/row-helpers.ts +1 -1
  28. package/src/db/table-builder.ts +20 -5
  29. package/src/db/tenant-db.ts +9 -9
  30. package/src/engine/__tests__/_pipeline-test-utils.ts +23 -0
  31. package/src/engine/__tests__/boot-validator-api-exposure.test.ts +142 -0
  32. package/src/engine/__tests__/boot-validator-pii-retention.test.ts +570 -0
  33. package/src/engine/__tests__/boot-validator-s0-integration.test.ts +160 -0
  34. package/src/engine/__tests__/build-target.test.ts +135 -0
  35. package/src/engine/__tests__/codemod-pipeline.test.ts +551 -0
  36. package/src/engine/__tests__/entity-handlers.test.ts +3 -3
  37. package/src/engine/__tests__/event-helpers.test.ts +4 -4
  38. package/src/engine/__tests__/pipeline-engine.test.ts +215 -0
  39. package/src/engine/__tests__/pipeline-handler.integration.ts +894 -0
  40. package/src/engine/__tests__/pipeline-observability.integration.ts +142 -0
  41. package/src/engine/__tests__/pipeline-performance.integration.ts +152 -0
  42. package/src/engine/__tests__/pipeline-sub-pipelines.test.ts +288 -0
  43. package/src/engine/__tests__/raw-table.test.ts +2 -2
  44. package/src/engine/__tests__/steps-aggregate-append-event.test.ts +115 -0
  45. package/src/engine/__tests__/steps-aggregate-create.test.ts +92 -0
  46. package/src/engine/__tests__/steps-aggregate-update.test.ts +127 -0
  47. package/src/engine/__tests__/steps-call-feature.test.ts +123 -0
  48. package/src/engine/__tests__/steps-mail-send.test.ts +136 -0
  49. package/src/engine/__tests__/steps-read.test.ts +142 -0
  50. package/src/engine/__tests__/steps-resolver-utils.test.ts +50 -0
  51. package/src/engine/__tests__/steps-unsafe-projection-delete.test.ts +69 -0
  52. package/src/engine/__tests__/steps-unsafe-projection-upsert.test.ts +117 -0
  53. package/src/engine/__tests__/steps-webhook-send.test.ts +135 -0
  54. package/src/engine/__tests__/steps-workflow.test.ts +198 -0
  55. package/src/engine/__tests__/validate-projection-allowlist.test.ts +491 -0
  56. package/src/engine/__tests__/visual-tree-patterns.test.ts +251 -0
  57. package/src/engine/boot-validator/api-ext.ts +77 -0
  58. package/src/engine/boot-validator/config-deps.ts +163 -0
  59. package/src/engine/boot-validator/entity-handler.ts +466 -0
  60. package/src/engine/boot-validator/index.ts +159 -0
  61. package/src/engine/boot-validator/ownership.ts +198 -0
  62. package/src/engine/boot-validator/pii-retention.ts +155 -0
  63. package/src/engine/boot-validator/screens-nav.ts +624 -0
  64. package/src/engine/boot-validator.ts +1 -1528
  65. package/src/engine/build-app-schema.ts +1 -1
  66. package/src/engine/build-target.ts +99 -0
  67. package/src/engine/codemod/index.ts +15 -0
  68. package/src/engine/codemod/pipeline-codemod.ts +641 -0
  69. package/src/engine/config-helpers.ts +9 -19
  70. package/src/engine/constants.ts +1 -1
  71. package/src/engine/define-feature.ts +127 -9
  72. package/src/engine/define-handler.ts +89 -3
  73. package/src/engine/define-roles.ts +2 -2
  74. package/src/engine/define-step.ts +28 -0
  75. package/src/engine/define-workflow.ts +110 -0
  76. package/src/engine/entity-handlers.ts +10 -9
  77. package/src/engine/event-helpers.ts +4 -4
  78. package/src/engine/extension-names.ts +105 -0
  79. package/src/engine/extensions/user-data.ts +106 -0
  80. package/src/engine/factories.ts +26 -16
  81. package/src/engine/feature-ast/__tests__/visual-tree-parse.test.ts +184 -0
  82. package/src/engine/feature-ast/extractors/index.ts +74 -0
  83. package/src/engine/feature-ast/extractors/round1.ts +110 -0
  84. package/src/engine/feature-ast/extractors/round2.ts +253 -0
  85. package/src/engine/feature-ast/extractors/round3.ts +471 -0
  86. package/src/engine/feature-ast/extractors/round4.ts +1365 -0
  87. package/src/engine/feature-ast/extractors/round5.ts +72 -0
  88. package/src/engine/feature-ast/extractors/round6.ts +66 -0
  89. package/src/engine/feature-ast/extractors/shared.ts +177 -0
  90. package/src/engine/feature-ast/parse.ts +13 -0
  91. package/src/engine/feature-ast/patch.ts +9 -1
  92. package/src/engine/feature-ast/patcher.ts +10 -3
  93. package/src/engine/feature-ast/patterns.ts +71 -1
  94. package/src/engine/feature-ast/render.ts +31 -1
  95. package/src/engine/index.ts +66 -2
  96. package/src/engine/pattern-library/__tests__/library.test.ts +11 -0
  97. package/src/engine/pattern-library/library.ts +78 -2
  98. package/src/engine/pipeline.ts +88 -0
  99. package/src/engine/projection-helpers.ts +1 -1
  100. package/src/engine/read-claim.ts +1 -1
  101. package/src/engine/registry.ts +30 -2
  102. package/src/engine/resolve-config-or-param.ts +4 -0
  103. package/src/engine/run-pipeline.ts +162 -0
  104. package/src/engine/schema-builder.ts +10 -4
  105. package/src/engine/state-machine.ts +1 -1
  106. package/src/engine/steps/_drizzle-boundary.ts +19 -0
  107. package/src/engine/steps/_duration-utils.ts +33 -0
  108. package/src/engine/steps/_no-return-guard.ts +21 -0
  109. package/src/engine/steps/_resolver-utils.ts +42 -0
  110. package/src/engine/steps/_step-dispatch-constants.ts +38 -0
  111. package/src/engine/steps/aggregate-append-event.ts +56 -0
  112. package/src/engine/steps/aggregate-create.ts +56 -0
  113. package/src/engine/steps/aggregate-update.ts +68 -0
  114. package/src/engine/steps/branch.ts +84 -0
  115. package/src/engine/steps/call-feature.ts +49 -0
  116. package/src/engine/steps/compute.ts +41 -0
  117. package/src/engine/steps/for-each.ts +111 -0
  118. package/src/engine/steps/mail-send.ts +44 -0
  119. package/src/engine/steps/read-find-many.ts +51 -0
  120. package/src/engine/steps/read-find-one.ts +58 -0
  121. package/src/engine/steps/retry.ts +87 -0
  122. package/src/engine/steps/return.ts +34 -0
  123. package/src/engine/steps/unsafe-projection-delete.ts +46 -0
  124. package/src/engine/steps/unsafe-projection-upsert.ts +69 -0
  125. package/src/engine/steps/wait-for-event.ts +71 -0
  126. package/src/engine/steps/wait.ts +69 -0
  127. package/src/engine/steps/webhook-send.ts +71 -0
  128. package/src/engine/system-user.ts +1 -1
  129. package/src/engine/types/feature.ts +143 -1
  130. package/src/engine/types/fields.ts +134 -10
  131. package/src/engine/types/handlers.ts +18 -10
  132. package/src/engine/types/identifiers.ts +1 -0
  133. package/src/engine/types/index.ts +15 -1
  134. package/src/engine/types/step.ts +334 -0
  135. package/src/engine/types/target-ref.ts +21 -0
  136. package/src/engine/types/tree-node.ts +130 -0
  137. package/src/engine/types/workspace.ts +7 -0
  138. package/src/engine/validate-projection-allowlist.ts +161 -0
  139. package/src/event-store/snapshot.ts +1 -1
  140. package/src/event-store/upcaster-dead-letter.ts +1 -1
  141. package/src/event-store/upcaster.ts +1 -1
  142. package/src/files/__tests__/read-stream.test.ts +105 -0
  143. package/src/files/__tests__/write-stream.test.ts +233 -0
  144. package/src/files/__tests__/zip-stream.test.ts +357 -0
  145. package/src/files/file-routes.ts +1 -1
  146. package/src/files/in-memory-provider.ts +38 -0
  147. package/src/files/index.ts +3 -0
  148. package/src/files/local-provider.ts +58 -1
  149. package/src/files/types.ts +36 -8
  150. package/src/files/zip-stream.ts +251 -0
  151. package/src/jobs/job-runner.ts +10 -10
  152. package/src/lifecycle/lifecycle.ts +0 -3
  153. package/src/logging/index.ts +1 -0
  154. package/src/logging/pino-logger.ts +11 -7
  155. package/src/logging/utils.ts +24 -0
  156. package/src/observability/prometheus-meter.ts +7 -5
  157. package/src/pipeline/__tests__/archive-stream.integration.ts +1 -1
  158. package/src/pipeline/__tests__/causation-chain.integration.ts +1 -1
  159. package/src/pipeline/__tests__/domain-events-projections.integration.ts +3 -3
  160. package/src/pipeline/__tests__/event-define-event-strict.integration.ts +4 -4
  161. package/src/pipeline/__tests__/load-aggregate-query.integration.ts +1 -1
  162. package/src/pipeline/__tests__/msp-multi-hop.integration.ts +3 -3
  163. package/src/pipeline/__tests__/msp-rebuild.integration.ts +3 -3
  164. package/src/pipeline/__tests__/multi-stream-projection.integration.ts +2 -2
  165. package/src/pipeline/__tests__/query-projection.integration.ts +5 -5
  166. package/src/pipeline/append-event-core.ts +22 -6
  167. package/src/pipeline/dispatcher-utils.ts +188 -0
  168. package/src/pipeline/dispatcher.ts +63 -283
  169. package/src/pipeline/distributed-lock.ts +1 -1
  170. package/src/pipeline/entity-cache.ts +2 -2
  171. package/src/pipeline/event-consumer-state.ts +0 -13
  172. package/src/pipeline/event-dispatcher.ts +4 -4
  173. package/src/pipeline/index.ts +0 -2
  174. package/src/pipeline/lifecycle-pipeline.ts +6 -12
  175. package/src/pipeline/msp-rebuild.ts +5 -5
  176. package/src/pipeline/multi-stream-apply-context.ts +6 -7
  177. package/src/pipeline/projection-rebuild.ts +2 -2
  178. package/src/pipeline/projection-state.ts +0 -12
  179. package/src/rate-limit/__tests__/resolver.integration.ts +8 -4
  180. package/src/rate-limit/resolver.ts +1 -1
  181. package/src/search/in-memory-adapter.ts +1 -1
  182. package/src/search/meilisearch-adapter.ts +3 -3
  183. package/src/search/types.ts +1 -1
  184. package/src/secrets/leak-guard.ts +2 -2
  185. package/src/stack/request-helper.ts +9 -5
  186. package/src/stack/test-stack.ts +1 -1
  187. package/src/testing/handler-context.ts +4 -4
  188. package/src/testing/http-cookies.ts +1 -1
  189. package/src/time/tz-context.ts +1 -2
  190. package/src/ui-types/index.ts +4 -0
  191. package/src/engine/feature-ast/extractors.ts +0 -2562
package/CHANGELOG.md CHANGED
@@ -1,5 +1,59 @@
1
1
  # @cosmicdrift/kumiko-framework
2
2
 
3
+ ## 0.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 0.3.0 bringt zwei neue Subsysteme (Step-Engine Tier-3 + Visual-Tree) plus
8
+ eine AST-Codemod-Pipeline als Vorarbeit für den L2-AI-Layer.
9
+
10
+ ### Breaking Changes
11
+
12
+ - `skipTransitionGuard` → `unsafeSkipTransitionGuard` (Rename in
13
+ feature-ast + engine). Der `unsafe`-Prefix macht die Tragweite des
14
+ Casts sichtbar und ist konsistent zur `unsafeProjectionUpsert`- und
15
+ `r.rawTable`-Konvention. Migration: 1:1-Ersetzung, keine Verhaltens-Änderung.
16
+
17
+ ### Features
18
+
19
+ - **Step-Engine M.4 — Tier-3 Workflow-Engine.** Neue Step-Vocabulary
20
+ `wait`, `waitForEvent`, `retry` ermöglicht persistierte Long-Running-Flows
21
+ über Job-Boundaries hinweg. Q7 Snapshot-at-Start hängt jedem Step-Run
22
+ einen SHA-256-Fingerprint des Aggregat-Zustands an, sodass Replays
23
+ deterministisch gegen den ursprünglichen Eingangszustand laufen.
24
+ - **Visual-Tree V.1.x — Tree-API + Editor-Panel.** Neue `VisualTree`-
25
+ Component plus TreeProvider-Pattern; erste TreeProviders für
26
+ `text-content` und `legal-pages` (CMS-light + Impressum/Privacy).
27
+ Fundament für den späteren No-Code-Designer (~3000 LOC, 98 Tests).
28
+ - **Codemod-Pipeline.** AST-basierte Patcher-Module für strukturelle
29
+ Feature-Edits — wird vom kommenden L2-AI-Layer als Tool-Surface
30
+ verwendet, ist aber eigenständig nutzbar für ts-morph-style Migrationen.
31
+ - **user-data-rights Sample-Recipe.** DSGVO Art. 15/17/18/20 vollständig
32
+ als Sample-Recipe (`samples/recipes/`) inklusive README — zeigt die
33
+ Export- und Forget-Pipeline gegen den `compliance-profiles`-Default
34
+ (`eu-dsgvo`).
35
+
36
+ ### Fixes
37
+
38
+ - `tier-engine`: auto-default-tier-Hook benutzt jetzt `ctx.db.raw` für
39
+ Event-Store-Operationen (#37, vorher: stiller Bug, 22 Tage live).
40
+ - `engine`: unsafe-projection-upsert nutzt `as never` statt `as any` —
41
+ schmaler Cast-Surface, weniger Compiler-Knebel.
42
+ - `visual-tree`: runtime-isolation marker für client-konsumierte Files,
43
+ damit der Multi-Entry-Build den richtigen Bundle-Split bekommt.
44
+ - `feature-ast`: vollständiger `unsafeSkipTransitionGuard`-Rename (war
45
+ in zwei Modulen noch der alte Name).
46
+ - `framework`: Error-Reasons + `noConsole`-Lint + No-Date-API-Guard
47
+ wieder push-ready.
48
+
49
+ ### Library-Updates
50
+
51
+ hono 4.12, jose 6.2, stripe 22.1, meilisearch 0.58, marked 18,
52
+ bun-types 1.3.13, lucide-react 1.14, bullmq 5.76, ioredis 5.10,
53
+ i18next 26.0, react + radix-ui-primitives auf aktuelle Minors.
54
+
55
+ ## 0.2.3
56
+
3
57
  ## 0.2.2
4
58
 
5
59
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosmicdrift/kumiko-framework",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
4
4
  "description": "Framework core — engine, pipeline, API, DB, and every other bit that makes Kumiko go.",
5
5
  "license": "BUSL-1.1",
6
6
  "author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
@@ -28,55 +28,141 @@
28
28
  "runtime": "runtime"
29
29
  },
30
30
  "exports": {
31
- "./engine": "./src/engine/index.ts",
32
- "./engine/types": "./src/engine/types/index.ts",
33
- "./errors": "./src/errors/index.ts",
34
- "./db": "./src/db/index.ts",
35
- "./event-store": "./src/event-store/index.ts",
36
- "./event-store/admin-api": "./src/event-store/admin-api.ts",
37
- "./pipeline": "./src/pipeline/index.ts",
38
- "./api": "./src/api/index.ts",
39
- "./i18n": "./src/i18n/index.ts",
40
- "./auth": "./src/auth/index.ts",
41
- "./files": "./src/files/index.ts",
42
- "./jobs": "./src/jobs/index.ts",
43
- "./migrations": "./src/migrations/index.ts",
44
- "./entrypoint": "./src/entrypoint/index.ts",
45
- "./random": "./src/random/index.ts",
46
- "./redis": "./src/redis/index.ts",
47
- "./search": "./src/search/index.ts",
48
- "./search/meilisearch": "./src/search/meilisearch-adapter.ts",
49
- "./secrets": "./src/secrets/index.ts",
50
- "./stack": "./src/stack/index.ts",
51
- "./testing": "./src/testing/index.ts",
52
- "./testing/handler-context": "./src/testing/handler-context.ts",
53
- "./testing/e2e-generator": "./src/testing/e2e-generator.ts",
54
- "./time": "./src/time/index.ts",
55
- "./ui-types": "./src/ui-types/index.ts",
56
- "./utils": "./src/utils/index.ts"
31
+ "./compliance": {
32
+ "types": "./src/compliance/index.ts",
33
+ "default": "./src/compliance/index.ts"
34
+ },
35
+ "./engine": {
36
+ "types": "./src/engine/index.ts",
37
+ "default": "./src/engine/index.ts"
38
+ },
39
+ "./engine/types": {
40
+ "types": "./src/engine/types/index.ts",
41
+ "default": "./src/engine/types/index.ts"
42
+ },
43
+ "./engine/codemod": {
44
+ "types": "./src/engine/codemod/index.ts",
45
+ "default": "./src/engine/codemod/index.ts"
46
+ },
47
+ "./errors": {
48
+ "types": "./src/errors/index.ts",
49
+ "default": "./src/errors/index.ts"
50
+ },
51
+ "./db": {
52
+ "types": "./src/db/index.ts",
53
+ "default": "./src/db/index.ts"
54
+ },
55
+ "./event-store": {
56
+ "types": "./src/event-store/index.ts",
57
+ "default": "./src/event-store/index.ts"
58
+ },
59
+ "./event-store/admin-api": {
60
+ "types": "./src/event-store/admin-api.ts",
61
+ "default": "./src/event-store/admin-api.ts"
62
+ },
63
+ "./pipeline": {
64
+ "types": "./src/pipeline/index.ts",
65
+ "default": "./src/pipeline/index.ts"
66
+ },
67
+ "./api": {
68
+ "types": "./src/api/index.ts",
69
+ "default": "./src/api/index.ts"
70
+ },
71
+ "./i18n": {
72
+ "types": "./src/i18n/index.ts",
73
+ "default": "./src/i18n/index.ts"
74
+ },
75
+ "./auth": {
76
+ "types": "./src/auth/index.ts",
77
+ "default": "./src/auth/index.ts"
78
+ },
79
+ "./files": {
80
+ "types": "./src/files/index.ts",
81
+ "default": "./src/files/index.ts"
82
+ },
83
+ "./jobs": {
84
+ "types": "./src/jobs/index.ts",
85
+ "default": "./src/jobs/index.ts"
86
+ },
87
+ "./migrations": {
88
+ "types": "./src/migrations/index.ts",
89
+ "default": "./src/migrations/index.ts"
90
+ },
91
+ "./entrypoint": {
92
+ "types": "./src/entrypoint/index.ts",
93
+ "default": "./src/entrypoint/index.ts"
94
+ },
95
+ "./random": {
96
+ "types": "./src/random/index.ts",
97
+ "default": "./src/random/index.ts"
98
+ },
99
+ "./redis": {
100
+ "types": "./src/redis/index.ts",
101
+ "default": "./src/redis/index.ts"
102
+ },
103
+ "./search": {
104
+ "types": "./src/search/index.ts",
105
+ "default": "./src/search/index.ts"
106
+ },
107
+ "./search/meilisearch": {
108
+ "types": "./src/search/meilisearch-adapter.ts",
109
+ "default": "./src/search/meilisearch-adapter.ts"
110
+ },
111
+ "./secrets": {
112
+ "types": "./src/secrets/index.ts",
113
+ "default": "./src/secrets/index.ts"
114
+ },
115
+ "./stack": {
116
+ "types": "./src/stack/index.ts",
117
+ "default": "./src/stack/index.ts"
118
+ },
119
+ "./testing": {
120
+ "types": "./src/testing/index.ts",
121
+ "default": "./src/testing/index.ts"
122
+ },
123
+ "./testing/handler-context": {
124
+ "types": "./src/testing/handler-context.ts",
125
+ "default": "./src/testing/handler-context.ts"
126
+ },
127
+ "./testing/e2e-generator": {
128
+ "types": "./src/testing/e2e-generator.ts",
129
+ "default": "./src/testing/e2e-generator.ts"
130
+ },
131
+ "./time": {
132
+ "types": "./src/time/index.ts",
133
+ "default": "./src/time/index.ts"
134
+ },
135
+ "./ui-types": {
136
+ "types": "./src/ui-types/index.ts",
137
+ "default": "./src/ui-types/index.ts"
138
+ },
139
+ "./utils": {
140
+ "types": "./src/utils/index.ts",
141
+ "default": "./src/utils/index.ts"
142
+ }
57
143
  },
58
144
  "dependencies": {
59
- "bullmq": "^5.73.5",
60
- "bun-types": "^1.3.12",
145
+ "bullmq": "^5.76.7",
146
+ "bun-types": "^1.3.13",
61
147
  "drizzle-kit": "^0.31.10",
62
148
  "drizzle-orm": "^0.45.2",
63
- "hono": "^4.12.12",
64
- "i18next": "^26.0.4",
65
- "ioredis": "^5.6.0",
66
- "jose": "^6.0.11",
67
- "meilisearch": "^0.57.0",
149
+ "hono": "^4.12.18",
150
+ "i18next": "^26.1.0",
151
+ "ioredis": "^5.10.1",
152
+ "jose": "^6.2.3",
153
+ "meilisearch": "^0.58.0",
68
154
  "pino": "^10.3.1",
69
155
  "postgres": "^3.4.9",
70
156
  "temporal-polyfill": "^0.3.2",
71
157
  "ts-morph": "^28.0.0",
72
158
  "uuid": "^14.0.0",
73
- "zod": "^4.3.6"
159
+ "zod": "^4.4.3"
74
160
  },
75
161
  "devDependencies": {
76
- "@cosmicdrift/kumiko-dispatcher-live": "0.2.2",
162
+ "@cosmicdrift/kumiko-dispatcher-live": "0.3.0",
77
163
  "@types/uuid": "^11.0.0",
78
- "bun-types": "^1.2.9",
79
- "drizzle-kit": "^0.31.0",
164
+ "bun-types": "^1.3.13",
165
+ "drizzle-kit": "^0.31.10",
80
166
  "pino-pretty": "^13.1.3"
81
167
  },
82
168
  "publishConfig": {
@@ -46,11 +46,11 @@ let USER_CREATED_EVENT: string;
46
46
  const domainEventSubscriberCalls: Array<{ type: string; payload: unknown }> = [];
47
47
 
48
48
  async function emitUserCreated(
49
- ctx: Pick<HandlerContext, "appendEventUnsafe">,
49
+ ctx: Pick<HandlerContext, "unsafeAppendEvent">,
50
50
  id: EntityId,
51
51
  email: string,
52
52
  ): Promise<void> {
53
- await ctx.appendEventUnsafe({
53
+ await ctx.unsafeAppendEvent({
54
54
  aggregateId: String(id),
55
55
  aggregateType: "user",
56
56
  type: USER_CREATED_EVENT,
@@ -650,7 +650,7 @@ export function createAuthRoutes(
650
650
  }
651
651
  const result = await dispatcher.write(inv.acceptWithLoginHandler, parsed.data, GUEST_USER);
652
652
  if (!result.isSuccess) {
653
- const status = result.error.httpStatus as 400 | 401 | 403 | 422 | 500;
653
+ const status = result.error.httpStatus as 400 | 401 | 403 | 422 | 500; // @cast-boundary engine-payload
654
654
  return c.json({ isSuccess: false, error: result.error }, status);
655
655
  }
656
656
  const data = result.data as {
@@ -658,7 +658,7 @@ export function createAuthRoutes(
658
658
  session: SessionUser;
659
659
  tenantId: TenantId;
660
660
  role: string;
661
- };
661
+ }; // @cast-boundary engine-payload
662
662
  let sessionForJwt: SessionUser = data.session;
663
663
  if (config.sessionCreator) {
664
664
  const sid = await config.sessionCreator(data.session, requestMeta(c));
@@ -690,7 +690,7 @@ export function createAuthRoutes(
690
690
  }
691
691
  const result = await dispatcher.write(inv.signupCompleteHandler, parsed.data, GUEST_USER);
692
692
  if (!result.isSuccess) {
693
- const status = result.error.httpStatus as 400 | 401 | 403 | 422 | 500;
693
+ const status = result.error.httpStatus as 400 | 401 | 403 | 422 | 500; // @cast-boundary engine-payload
694
694
  return c.json({ isSuccess: false, error: result.error }, status);
695
695
  }
696
696
  const data = result.data as {
@@ -698,7 +698,7 @@ export function createAuthRoutes(
698
698
  session: SessionUser;
699
699
  tenantId: TenantId;
700
700
  role: string;
701
- };
701
+ }; // @cast-boundary engine-payload
702
702
  let sessionForJwt: SessionUser = data.session;
703
703
  if (config.sessionCreator) {
704
704
  const sid = await config.sessionCreator(data.session, requestMeta(c));
@@ -967,7 +967,7 @@ function registerTokenConfirmRoute(opts: {
967
967
  }
968
968
  const result = await opts.dispatcher.write(opts.confirmHandler, parsed.data, GUEST_USER);
969
969
  if (!result.isSuccess) {
970
- const status = result.error.httpStatus as 400 | 401 | 403 | 422 | 500;
970
+ const status = result.error.httpStatus as 400 | 401 | 403 | 422 | 500; // @cast-boundary engine-payload
971
971
  return c.json({ isSuccess: false, error: result.error }, status);
972
972
  }
973
973
  return c.json({ isSuccess: true });
package/src/api/jwt.ts CHANGED
@@ -50,8 +50,8 @@ export function createJwtHelper(secret: string, issuer = "kumiko"): JwtHelper {
50
50
  const { payload } = await jose.jwtVerify(token, encodedSecret, { issuer });
51
51
  const result: JwtPayload = {
52
52
  sub: String(payload.sub),
53
- tenantId: payload["tenantId"] as string,
54
- roles: payload["roles"] as string[],
53
+ tenantId: payload["tenantId"] as string, // @cast-boundary dynamic-key
54
+ roles: payload["roles"] as string[], // @cast-boundary dynamic-key
55
55
  };
56
56
  const claims = payload["claims"];
57
57
  if (claims && typeof claims === "object") {
@@ -68,7 +68,7 @@ function constantTimeEqual(a: string, b: string): boolean {
68
68
  // Prometheus-compatible meter (future OTLP bridge) works without an
69
69
  // explicit union — if it exposes `snapshot()` it's serialisable.
70
70
  function isPrometheusMeter(m: Meter): m is PrometheusMeter {
71
- return typeof (m as { snapshot?: unknown }).snapshot === "function";
71
+ return typeof (m as { snapshot?: unknown }).snapshot === "function"; // @cast-boundary schema-walk
72
72
  }
73
73
 
74
74
  // Mount `/metrics` (or the caller-supplied path). Takes just the Meter —
package/src/api/routes.ts CHANGED
@@ -73,7 +73,7 @@ export function createApiRoutes(dispatcher: Dispatcher) {
73
73
  failedIndex: result.failedIndex,
74
74
  results: result.results,
75
75
  },
76
- err.httpStatus as ContentfulStatusCode,
76
+ err.httpStatus as ContentfulStatusCode, // @cast-boundary engine-payload
77
77
  );
78
78
  }
79
79
  return c.json(result);
@@ -121,7 +121,7 @@ function toKumiko(e: unknown): KumikoError {
121
121
  function writeErrorResponse(c: Context, err: KumikoError, statusOverride?: number) {
122
122
  const requestId = requestContext.get()?.requestId;
123
123
  const { error } = serializeError(err, requestId);
124
- const status = (statusOverride ?? err.httpStatus) as ContentfulStatusCode;
124
+ const status = (statusOverride ?? err.httpStatus) as ContentfulStatusCode; // @cast-boundary engine-payload
125
125
  return c.json({ isSuccess: false, error }, status);
126
126
  }
127
127
 
@@ -130,6 +130,6 @@ function writeErrorResponse(c: Context, err: KumikoError, statusOverride?: numbe
130
130
  function queryErrorResponse(c: Context, err: KumikoError, statusOverride?: number) {
131
131
  const requestId = requestContext.get()?.requestId;
132
132
  const body = serializeError(err, requestId);
133
- const status = (statusOverride ?? err.httpStatus) as ContentfulStatusCode;
133
+ const status = (statusOverride ?? err.httpStatus) as ContentfulStatusCode; // @cast-boundary engine-payload
134
134
  return c.json(body, status);
135
135
  }
package/src/api/server.ts CHANGED
@@ -42,7 +42,7 @@ import {
42
42
  globalIpRateLimit,
43
43
  } from "../rate-limit";
44
44
  import type { SearchAdapter } from "../search/types";
45
- import { generateId } from "../utils";
45
+ import { assertUnreachable, generateId } from "../utils";
46
46
  import { PUBLIC_API_PATHS } from "./api-constants";
47
47
  import { type AnonymousAccessConfig, authMiddleware } from "./auth-middleware";
48
48
  import { type AuthRoutesConfig, createAuthRoutes } from "./auth-routes";
@@ -383,7 +383,7 @@ export function buildServer(options: ServerOptions): KumikoServer {
383
383
  // can pause this consumer when the feature is globally disabled. Events
384
384
  // queue up in the store and replay cleanly from the same cursor on resume.
385
385
  ...(options.registry.getMultiStreamProjectionFeature(msp.name) && {
386
- featureName: options.registry.getMultiStreamProjectionFeature(msp.name) as string,
386
+ featureName: options.registry.getMultiStreamProjectionFeature(msp.name) as string, // @cast-boundary engine-bridge
387
387
  }),
388
388
  // Copy the continuous-lifecycle error policy straight onto the consumer.
389
389
  // Rebuild uses its own policy (rebuildProjection reads msp.errorMode.rebuild
@@ -409,10 +409,7 @@ export function buildServer(options: ServerOptions): KumikoServer {
409
409
  // Hand the raw DbRunner to apply(): MSPs write to their projection
410
410
  // table directly, they don't go through the TenantDb wrapper.
411
411
  const rawRunner =
412
- event.tenantId === SYSTEM_TENANT_ID
413
- ? baseDb
414
- : // @cast-boundary engine-bridge — TenantDb exposes its raw DbRunner via .raw
415
- (scopedDb as { raw: typeof baseDb }).raw;
412
+ event.tenantId === SYSTEM_TENANT_ID ? baseDb : (scopedDb as { raw: typeof baseDb }).raw; // @cast-boundary engine-bridge
416
413
  // Saga/process-manager ctx: apply can call ctx.appendEvent to cascade
417
414
  // a follow-up event onto another aggregate. Uses the triggering event's
418
415
  // tenantId + userId so the causal chain stays tenant-consistent.
@@ -563,7 +560,7 @@ export function buildServer(options: ServerOptions): KumikoServer {
563
560
  app.route("/api", createSseRoute(sseBroker));
564
561
 
565
562
  if (options.files) {
566
- const fileDb = options.files.db ?? (options.context.db as FileRoutesOptions["db"]);
563
+ const fileDb = options.files.db ?? (options.context.db as FileRoutesOptions["db"]); // @cast-boundary engine-bridge
567
564
  if (!fileDb) throw new Error("files option requires db in context or files.db");
568
565
  app.route(
569
566
  "/api",
@@ -605,6 +602,8 @@ export function buildServer(options: ServerOptions): KumikoServer {
605
602
  // Hono-on() für die Methoden ohne Convenience-Method.
606
603
  app.on(route.method, route.path, honoHandler);
607
604
  break;
605
+ default:
606
+ assertUnreachable(route.method, "http method");
608
607
  }
609
608
  }
610
609
  }
@@ -0,0 +1,24 @@
1
+ // Snapshot-Tests fuer ROLES — faengt stille Drift ab.
2
+
3
+ import { describe, expect, test } from "vitest";
4
+ import { ROLES } from "../roles";
5
+
6
+ describe("ROLES constants", () => {
7
+ test("Snapshot — explizit zu updaten bei Aenderungen", () => {
8
+ expect(ROLES).toMatchInlineSnapshot(`
9
+ {
10
+ "DataProtectionOfficer": "DataProtectionOfficer",
11
+ "Member": "Member",
12
+ "SystemAdmin": "SystemAdmin",
13
+ "TenantAdmin": "TenantAdmin",
14
+ "TenantOwner": "TenantOwner",
15
+ }
16
+ `);
17
+ });
18
+
19
+ test("ROLES-Werte sind identisch zu den Keys (keine Drift im Mapping)", () => {
20
+ for (const [key, value] of Object.entries(ROLES)) {
21
+ expect(value).toBe(key);
22
+ }
23
+ });
24
+ });
@@ -0,0 +1,7 @@
1
+ // `@cosmicdrift/kumiko-framework/auth` — Auth-Foundation (Roles, Token-
2
+ // Helpers, Session-Types). Wird von Bundled-Features (auth-email-password,
3
+ // tenant, user) und Datenschutz-Sprints (S1+ user-data-rights, S5 tenant-
4
+ // lifecycle, S6 legal-hold) genutzt.
5
+
6
+ export type { Role } from "./roles";
7
+ export { ROLES } from "./roles";
@@ -0,0 +1,42 @@
1
+ // Zentrale Role-Konstanten der Plattform.
2
+ //
3
+ // Hintergrund: Bundled-Features driften zwischen "Admin" (text-content,
4
+ // secrets, ai-foundation, file-provider-s3) und "TenantAdmin" (tenant-
5
+ // handler, publicstatus, platform). App-Builder fallen in diese Falle.
6
+ // Diese Datei ist die Single Source of Truth — Bundled-Features
7
+ // migrieren schrittweise auf die ROLES-Constants in den Sprint-
8
+ // Touchpoints, an denen sie ohnehin angefasst werden.
9
+ //
10
+ // Canonical-Names:
11
+ // TenantOwner — Volle Tenant-Hoheit, einzige Rolle die
12
+ // Tenant-Destroy triggern darf.
13
+ // TenantAdmin — Tenant-Konfiguration + User-Management.
14
+ // Bestehender String "Admin" wird hierauf
15
+ // migriert.
16
+ // DataProtectionOfficer — DPO; setzt Legal-Holds, sieht Authority-
17
+ // Audit-Stream auch im silentMode (Sprint 6).
18
+ // SystemAdmin — Plattform-Operator (NICHT Tenant-scoped).
19
+ // Authority-Export, KMS-Recovery, Cross-
20
+ // Tenant-Setup. Matched den bestehenden
21
+ // "SystemAdmin"-String in text-content,
22
+ // secrets, auth-email-password, etc.
23
+ // Member — Standard-Mitglied eines Tenants ohne
24
+ // Admin-Rechte.
25
+
26
+ /**
27
+ * Plattform-weit standardisierte Role-Namen. Alle Datenschutz-Sprints
28
+ * (1+) nutzen ausschliesslich diese Constants statt String-Literale.
29
+ */
30
+ export const ROLES = {
31
+ TenantOwner: "TenantOwner",
32
+ TenantAdmin: "TenantAdmin",
33
+ DataProtectionOfficer: "DataProtectionOfficer",
34
+ SystemAdmin: "SystemAdmin",
35
+ Member: "Member",
36
+ } as const;
37
+
38
+ /**
39
+ * Type-Union aller bekannten Role-Namen (Compile-Time-Check fuer
40
+ * Handler-Access-Rules + Ownership-Maps).
41
+ */
42
+ export type Role = (typeof ROLES)[keyof typeof ROLES];
@@ -0,0 +1,72 @@
1
+ // Unit-Tests fuer DurationSpec-Helpers (S2.U5a.fix2).
2
+ //
3
+ // Pinst beide Discriminated-Union-Branches (`{days}` + `{hours}`) plus
4
+ // Edge-Cases die in den Integration-Tests nicht auftauchen (0-Werte,
5
+ // Singular/Plural). Der Bug aus dem U5a-Review (`{hours: 6}` fiel auf
6
+ // 30d-Default) wird hier zentral verhindert.
7
+
8
+ import { ensureTemporalPolyfill, getTemporal } from "@cosmicdrift/kumiko-framework/time";
9
+ import { beforeAll, describe, expect, test } from "vitest";
10
+ import { addDurationSpec, describeDurationSpec, durationSpecToMs } from "../duration-spec";
11
+
12
+ beforeAll(async () => {
13
+ await ensureTemporalPolyfill();
14
+ });
15
+
16
+ describe("durationSpecToMs", () => {
17
+ test("days → days * 86_400_000", () => {
18
+ expect(durationSpecToMs({ days: 30 })).toBe(30 * 24 * 60 * 60 * 1000);
19
+ expect(durationSpecToMs({ days: 1 })).toBe(24 * 60 * 60 * 1000);
20
+ });
21
+
22
+ test("hours → hours * 3_600_000", () => {
23
+ expect(durationSpecToMs({ hours: 6 })).toBe(6 * 60 * 60 * 1000);
24
+ expect(durationSpecToMs({ hours: 72 })).toBe(72 * 60 * 60 * 1000);
25
+ });
26
+
27
+ test("0-Werte ergeben 0", () => {
28
+ expect(durationSpecToMs({ days: 0 })).toBe(0);
29
+ expect(durationSpecToMs({ hours: 0 })).toBe(0);
30
+ });
31
+ });
32
+
33
+ describe("addDurationSpec", () => {
34
+ test("days addiert exakt zu Instant.epochMilliseconds", () => {
35
+ const T = getTemporal();
36
+ const t0 = T.Instant.fromEpochMilliseconds(1_700_000_000_000);
37
+ const t1 = addDurationSpec(t0, { days: 30 });
38
+ expect(t1.epochMilliseconds - t0.epochMilliseconds).toBe(30 * 24 * 60 * 60 * 1000);
39
+ });
40
+
41
+ test("hours addiert exakt zu Instant.epochMilliseconds", () => {
42
+ const T = getTemporal();
43
+ const t0 = T.Instant.fromEpochMilliseconds(1_700_000_000_000);
44
+ const t1 = addDurationSpec(t0, { hours: 6 });
45
+ expect(t1.epochMilliseconds - t0.epochMilliseconds).toBe(6 * 60 * 60 * 1000);
46
+ });
47
+
48
+ // Regression-Guard fuer den U5a-Bug: vorher fiel `{hours: 6}` auf
49
+ // `30 * 86_400_000`-Default zurueck. Wenn jemand den Branch wieder
50
+ // verliert, faellt dieser Test sofort um.
51
+ test("hours-Branch ist NICHT auf days-Default mappable (U5a-Regression)", () => {
52
+ const T = getTemporal();
53
+ const t0 = T.Instant.fromEpochMilliseconds(1_700_000_000_000);
54
+ const tHours = addDurationSpec(t0, { hours: 6 });
55
+ const tDaysDefault = addDurationSpec(t0, { days: 30 });
56
+ expect(tHours.epochMilliseconds).not.toBe(tDaysDefault.epochMilliseconds);
57
+ });
58
+ });
59
+
60
+ describe("describeDurationSpec", () => {
61
+ test("days mit Pluralisierung", () => {
62
+ expect(describeDurationSpec({ days: 30 })).toBe("30 days");
63
+ expect(describeDurationSpec({ days: 1 })).toBe("1 day");
64
+ expect(describeDurationSpec({ days: 0 })).toBe("0 days");
65
+ });
66
+
67
+ test("hours mit Pluralisierung", () => {
68
+ expect(describeDurationSpec({ hours: 72 })).toBe("72 hours");
69
+ expect(describeDurationSpec({ hours: 1 })).toBe("1 hour");
70
+ expect(describeDurationSpec({ hours: 0 })).toBe("0 hours");
71
+ });
72
+ });