@cosmicdrift/kumiko-framework 0.2.3 → 0.4.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 (166) hide show
  1. package/CHANGELOG.md +93 -0
  2. package/package.json +124 -39
  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/compliance/profiles.ts +8 -8
  10. package/src/db/assert-exists-in.ts +2 -2
  11. package/src/db/cursor.ts +3 -3
  12. package/src/db/event-store-executor.ts +19 -13
  13. package/src/db/located-timestamp.ts +1 -1
  14. package/src/db/money.ts +12 -2
  15. package/src/db/pg-error.ts +1 -1
  16. package/src/db/row-helpers.ts +1 -1
  17. package/src/db/table-builder.ts +3 -5
  18. package/src/db/tenant-db.ts +9 -9
  19. package/src/engine/__tests__/_pipeline-test-utils.ts +23 -0
  20. package/src/engine/__tests__/build-target.test.ts +135 -0
  21. package/src/engine/__tests__/codemod-pipeline.test.ts +551 -0
  22. package/src/engine/__tests__/entity-handlers.test.ts +3 -3
  23. package/src/engine/__tests__/event-helpers.test.ts +4 -4
  24. package/src/engine/__tests__/pipeline-engine.test.ts +215 -0
  25. package/src/engine/__tests__/pipeline-handler.integration.ts +894 -0
  26. package/src/engine/__tests__/pipeline-observability.integration.ts +142 -0
  27. package/src/engine/__tests__/pipeline-performance.integration.ts +152 -0
  28. package/src/engine/__tests__/pipeline-sub-pipelines.test.ts +288 -0
  29. package/src/engine/__tests__/raw-table.test.ts +2 -2
  30. package/src/engine/__tests__/steps-aggregate-append-event.test.ts +115 -0
  31. package/src/engine/__tests__/steps-aggregate-create.test.ts +92 -0
  32. package/src/engine/__tests__/steps-aggregate-update.test.ts +127 -0
  33. package/src/engine/__tests__/steps-call-feature.test.ts +123 -0
  34. package/src/engine/__tests__/steps-mail-send.test.ts +136 -0
  35. package/src/engine/__tests__/steps-read.test.ts +142 -0
  36. package/src/engine/__tests__/steps-resolver-utils.test.ts +50 -0
  37. package/src/engine/__tests__/steps-unsafe-projection-delete.test.ts +69 -0
  38. package/src/engine/__tests__/steps-unsafe-projection-upsert.test.ts +117 -0
  39. package/src/engine/__tests__/steps-webhook-send.test.ts +135 -0
  40. package/src/engine/__tests__/steps-workflow.test.ts +198 -0
  41. package/src/engine/__tests__/validate-projection-allowlist.test.ts +491 -0
  42. package/src/engine/__tests__/visual-tree-patterns.test.ts +251 -0
  43. package/src/engine/boot-validator/api-ext.ts +77 -0
  44. package/src/engine/boot-validator/config-deps.ts +163 -0
  45. package/src/engine/boot-validator/entity-handler.ts +466 -0
  46. package/src/engine/boot-validator/index.ts +159 -0
  47. package/src/engine/boot-validator/ownership.ts +198 -0
  48. package/src/engine/boot-validator/pii-retention.ts +155 -0
  49. package/src/engine/boot-validator/screens-nav.ts +624 -0
  50. package/src/engine/boot-validator.ts +1 -1804
  51. package/src/engine/build-app-schema.ts +1 -1
  52. package/src/engine/build-target.ts +99 -0
  53. package/src/engine/codemod/index.ts +15 -0
  54. package/src/engine/codemod/pipeline-codemod.ts +641 -0
  55. package/src/engine/config-helpers.ts +9 -19
  56. package/src/engine/constants.ts +1 -1
  57. package/src/engine/define-feature.ts +88 -9
  58. package/src/engine/define-handler.ts +89 -3
  59. package/src/engine/define-roles.ts +2 -2
  60. package/src/engine/define-step.ts +28 -0
  61. package/src/engine/define-workflow.ts +110 -0
  62. package/src/engine/entity-handlers.ts +10 -9
  63. package/src/engine/event-helpers.ts +4 -4
  64. package/src/engine/factories.ts +12 -12
  65. package/src/engine/feature-ast/__tests__/visual-tree-parse.test.ts +184 -0
  66. package/src/engine/feature-ast/extractors/index.ts +74 -0
  67. package/src/engine/feature-ast/extractors/round1.ts +110 -0
  68. package/src/engine/feature-ast/extractors/round2.ts +253 -0
  69. package/src/engine/feature-ast/extractors/round3.ts +471 -0
  70. package/src/engine/feature-ast/extractors/round4.ts +1365 -0
  71. package/src/engine/feature-ast/extractors/round5.ts +72 -0
  72. package/src/engine/feature-ast/extractors/round6.ts +66 -0
  73. package/src/engine/feature-ast/extractors/shared.ts +177 -0
  74. package/src/engine/feature-ast/parse.ts +7 -0
  75. package/src/engine/feature-ast/patch.ts +9 -1
  76. package/src/engine/feature-ast/patcher.ts +10 -3
  77. package/src/engine/feature-ast/patterns.ts +49 -1
  78. package/src/engine/feature-ast/render.ts +17 -1
  79. package/src/engine/index.ts +44 -2
  80. package/src/engine/pattern-library/__tests__/library.test.ts +6 -0
  81. package/src/engine/pattern-library/library.ts +42 -2
  82. package/src/engine/pipeline.ts +88 -0
  83. package/src/engine/projection-helpers.ts +1 -1
  84. package/src/engine/read-claim.ts +1 -1
  85. package/src/engine/registry.ts +30 -2
  86. package/src/engine/resolve-config-or-param.ts +4 -0
  87. package/src/engine/run-pipeline.ts +162 -0
  88. package/src/engine/schema-builder.ts +2 -4
  89. package/src/engine/state-machine.ts +1 -1
  90. package/src/engine/steps/_drizzle-boundary.ts +19 -0
  91. package/src/engine/steps/_duration-utils.ts +33 -0
  92. package/src/engine/steps/_no-return-guard.ts +21 -0
  93. package/src/engine/steps/_resolver-utils.ts +42 -0
  94. package/src/engine/steps/_step-dispatch-constants.ts +38 -0
  95. package/src/engine/steps/aggregate-append-event.ts +56 -0
  96. package/src/engine/steps/aggregate-create.ts +56 -0
  97. package/src/engine/steps/aggregate-update.ts +68 -0
  98. package/src/engine/steps/branch.ts +84 -0
  99. package/src/engine/steps/call-feature.ts +49 -0
  100. package/src/engine/steps/compute.ts +41 -0
  101. package/src/engine/steps/for-each.ts +111 -0
  102. package/src/engine/steps/mail-send.ts +44 -0
  103. package/src/engine/steps/read-find-many.ts +51 -0
  104. package/src/engine/steps/read-find-one.ts +58 -0
  105. package/src/engine/steps/retry.ts +87 -0
  106. package/src/engine/steps/return.ts +34 -0
  107. package/src/engine/steps/unsafe-projection-delete.ts +46 -0
  108. package/src/engine/steps/unsafe-projection-upsert.ts +69 -0
  109. package/src/engine/steps/wait-for-event.ts +71 -0
  110. package/src/engine/steps/wait.ts +69 -0
  111. package/src/engine/steps/webhook-send.ts +71 -0
  112. package/src/engine/system-user.ts +1 -1
  113. package/src/engine/types/feature.ts +93 -1
  114. package/src/engine/types/handlers.ts +18 -10
  115. package/src/engine/types/index.ts +11 -1
  116. package/src/engine/types/step.ts +334 -0
  117. package/src/engine/types/target-ref.ts +21 -0
  118. package/src/engine/types/tree-node.ts +132 -0
  119. package/src/engine/types/workspace.ts +7 -0
  120. package/src/engine/validate-projection-allowlist.ts +161 -0
  121. package/src/event-store/snapshot.ts +1 -1
  122. package/src/event-store/upcaster-dead-letter.ts +1 -1
  123. package/src/event-store/upcaster.ts +1 -1
  124. package/src/files/file-routes.ts +1 -1
  125. package/src/files/types.ts +2 -2
  126. package/src/jobs/job-runner.ts +10 -10
  127. package/src/lifecycle/lifecycle.ts +0 -3
  128. package/src/logging/index.ts +1 -0
  129. package/src/logging/pino-logger.ts +11 -7
  130. package/src/logging/utils.ts +24 -0
  131. package/src/observability/prometheus-meter.ts +7 -5
  132. package/src/pipeline/__tests__/archive-stream.integration.ts +1 -1
  133. package/src/pipeline/__tests__/causation-chain.integration.ts +1 -1
  134. package/src/pipeline/__tests__/domain-events-projections.integration.ts +3 -3
  135. package/src/pipeline/__tests__/event-define-event-strict.integration.ts +4 -4
  136. package/src/pipeline/__tests__/load-aggregate-query.integration.ts +1 -1
  137. package/src/pipeline/__tests__/msp-multi-hop.integration.ts +3 -3
  138. package/src/pipeline/__tests__/msp-rebuild.integration.ts +3 -3
  139. package/src/pipeline/__tests__/multi-stream-projection.integration.ts +2 -2
  140. package/src/pipeline/__tests__/query-projection.integration.ts +5 -5
  141. package/src/pipeline/append-event-core.ts +22 -6
  142. package/src/pipeline/dispatcher-utils.ts +188 -0
  143. package/src/pipeline/dispatcher.ts +63 -283
  144. package/src/pipeline/distributed-lock.ts +1 -1
  145. package/src/pipeline/entity-cache.ts +2 -2
  146. package/src/pipeline/event-consumer-state.ts +0 -13
  147. package/src/pipeline/event-dispatcher.ts +4 -4
  148. package/src/pipeline/index.ts +0 -2
  149. package/src/pipeline/lifecycle-pipeline.ts +6 -12
  150. package/src/pipeline/msp-rebuild.ts +5 -5
  151. package/src/pipeline/multi-stream-apply-context.ts +6 -7
  152. package/src/pipeline/projection-rebuild.ts +2 -2
  153. package/src/pipeline/projection-state.ts +0 -12
  154. package/src/rate-limit/__tests__/resolver.integration.ts +8 -4
  155. package/src/rate-limit/resolver.ts +1 -1
  156. package/src/search/in-memory-adapter.ts +1 -1
  157. package/src/search/meilisearch-adapter.ts +3 -3
  158. package/src/search/types.ts +1 -1
  159. package/src/secrets/leak-guard.ts +2 -2
  160. package/src/stack/request-helper.ts +9 -5
  161. package/src/stack/test-stack.ts +1 -1
  162. package/src/testing/handler-context.ts +4 -4
  163. package/src/testing/http-cookies.ts +1 -1
  164. package/src/time/tz-context.ts +1 -2
  165. package/src/ui-types/index.ts +4 -0
  166. package/src/engine/feature-ast/extractors.ts +0 -2602
package/CHANGELOG.md CHANGED
@@ -1,5 +1,98 @@
1
1
  # @cosmicdrift/kumiko-framework
2
2
 
3
+ ## 0.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 825e7d2: Visual-Tree V.1.4 → V.1.6 — Feature-complete Editor + Folder-Hierarchy + Roving-tabindex.
8
+
9
+ **V.1.4** — explicit `folder?: string` Schema-Field auf text-block-entity. Slug bleibt
10
+ kebab-only validiert, Folder explizit gesetzt. Tree gruppiert via `groupBlocksByFolder`
11
+ (ersetzt `groupBlocksBySlugPrefix`). `Subscribe<T>` Signature um optional `emitError`
12
+ erweitert für explicit async-error-Pfade. ProviderBranch zeigt Error-Banner mit
13
+ Retry-Button. Drift-Test pinnt seedTextBlock-vs-set.write Slug-Validation.
14
+
15
+ **V.1.4b** — URL-State-Routing für Editor-Target via `nav.searchParams`. F5 + Back-Button
16
+ stellen den Editor-State wieder her. Format: `?t=text-content:edit&a_slug=...&a_lang=...`.
17
+ Plus `useDispatchTarget` hook ersetzt globalen `dispatchTarget` als empfohlenen Production-
18
+ Pfad (legacy bleibt für Test-Hooks).
19
+
20
+ **V.1.5** — Arrow-Key-Navigation (`<aside role="tree">`, ARIA-tree-Pattern) + SSE-driven
21
+ Tree-Refresh. `ClientFeatureDefinition.treeEntities?: string[]` listet Entity-Namen pro
22
+ Provider; live-events triggern provider-re-mount → Stale-Tree-state="stub"→"filled"
23
+ flippt nach save automatisch.
24
+
25
+ **V.1.5c+d** — Active-Node-Highlight (explicit blue + 2px border-l + scrollIntoView),
26
+ VS-Code-Polish (compact spacing, focus-visible, folder-icon-color text-amber, indent-
27
+ guides per ancestor-depth), Folder-Wrapper für legal-pages ("📁 Legal" + slug-first
28
+ Verschachtelung) und text-content ("📁 Content").
29
+
30
+ **V.1.6** — Multi-level Folder-Splitting (`folder="page/marketing"` → nested folders,
31
+ walk-or-create-pattern, folder/leaf-collision-tolerant). Roving-tabindex (nur focused-
32
+ treeitem hat tabIndex=0, Tab cyclt aus dem Tree raus).
33
+
34
+ 35/35 kumiko check PASS, 13/13 group-blocks + 22/22 text-content integration tests grün.
35
+ Browser + Keyboard lokal validated.
36
+
37
+ **Breaking**: `TreeContext` Type entfernt (V.1.2 SR2-Rip — war nie genutzt). Provider sind
38
+ session-bound: `TreeChildrenSubscribe = () => Subscribe<T>` statt `(ctx) => Subscribe<T>`.
39
+
40
+ **V.1.7-Followups**: useEffect-deps in VisualTree-focus-init (Performance), Cancellation-
41
+ Token in TreeProvider's fetch (emit-after-unmount-warning), inline-rename, drag-drop,
42
+ file-icons per slug-extension, parent-jump bei ArrowLeft auf collapsed-item.
43
+
44
+ ## 0.3.0
45
+
46
+ ### Minor Changes
47
+
48
+ - 0.3.0 bringt zwei neue Subsysteme (Step-Engine Tier-3 + Visual-Tree) plus
49
+ eine AST-Codemod-Pipeline als Vorarbeit für den L2-AI-Layer.
50
+
51
+ ### Breaking Changes
52
+
53
+ - `skipTransitionGuard` → `unsafeSkipTransitionGuard` (Rename in
54
+ feature-ast + engine). Der `unsafe`-Prefix macht die Tragweite des
55
+ Casts sichtbar und ist konsistent zur `unsafeProjectionUpsert`- und
56
+ `r.rawTable`-Konvention. Migration: 1:1-Ersetzung, keine Verhaltens-Änderung.
57
+
58
+ ### Features
59
+
60
+ - **Step-Engine M.4 — Tier-3 Workflow-Engine.** Neue Step-Vocabulary
61
+ `wait`, `waitForEvent`, `retry` ermöglicht persistierte Long-Running-Flows
62
+ über Job-Boundaries hinweg. Q7 Snapshot-at-Start hängt jedem Step-Run
63
+ einen SHA-256-Fingerprint des Aggregat-Zustands an, sodass Replays
64
+ deterministisch gegen den ursprünglichen Eingangszustand laufen.
65
+ - **Visual-Tree V.1.x — Tree-API + Editor-Panel.** Neue `VisualTree`-
66
+ Component plus TreeProvider-Pattern; erste TreeProviders für
67
+ `text-content` und `legal-pages` (CMS-light + Impressum/Privacy).
68
+ Fundament für den späteren No-Code-Designer (~3000 LOC, 98 Tests).
69
+ - **Codemod-Pipeline.** AST-basierte Patcher-Module für strukturelle
70
+ Feature-Edits — wird vom kommenden L2-AI-Layer als Tool-Surface
71
+ verwendet, ist aber eigenständig nutzbar für ts-morph-style Migrationen.
72
+ - **user-data-rights Sample-Recipe.** DSGVO Art. 15/17/18/20 vollständig
73
+ als Sample-Recipe (`samples/recipes/`) inklusive README — zeigt die
74
+ Export- und Forget-Pipeline gegen den `compliance-profiles`-Default
75
+ (`eu-dsgvo`).
76
+
77
+ ### Fixes
78
+
79
+ - `tier-engine`: auto-default-tier-Hook benutzt jetzt `ctx.db.raw` für
80
+ Event-Store-Operationen (#37, vorher: stiller Bug, 22 Tage live).
81
+ - `engine`: unsafe-projection-upsert nutzt `as never` statt `as any` —
82
+ schmaler Cast-Surface, weniger Compiler-Knebel.
83
+ - `visual-tree`: runtime-isolation marker für client-konsumierte Files,
84
+ damit der Multi-Entry-Build den richtigen Bundle-Split bekommt.
85
+ - `feature-ast`: vollständiger `unsafeSkipTransitionGuard`-Rename (war
86
+ in zwei Modulen noch der alte Name).
87
+ - `framework`: Error-Reasons + `noConsole`-Lint + No-Date-API-Guard
88
+ wieder push-ready.
89
+
90
+ ### Library-Updates
91
+
92
+ hono 4.12, jose 6.2, stripe 22.1, meilisearch 0.58, marked 18,
93
+ bun-types 1.3.13, lucide-react 1.14, bullmq 5.76, ioredis 5.10,
94
+ i18next 26.0, react + radix-ui-primitives auf aktuelle Minors.
95
+
3
96
  ## 0.2.3
4
97
 
5
98
  ## 0.2.2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosmicdrift/kumiko-framework",
3
- "version": "0.2.3",
3
+ "version": "0.4.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,56 +28,141 @@
28
28
  "runtime": "runtime"
29
29
  },
30
30
  "exports": {
31
- "./compliance": "./src/compliance/index.ts",
32
- "./engine": "./src/engine/index.ts",
33
- "./engine/types": "./src/engine/types/index.ts",
34
- "./errors": "./src/errors/index.ts",
35
- "./db": "./src/db/index.ts",
36
- "./event-store": "./src/event-store/index.ts",
37
- "./event-store/admin-api": "./src/event-store/admin-api.ts",
38
- "./pipeline": "./src/pipeline/index.ts",
39
- "./api": "./src/api/index.ts",
40
- "./i18n": "./src/i18n/index.ts",
41
- "./auth": "./src/auth/index.ts",
42
- "./files": "./src/files/index.ts",
43
- "./jobs": "./src/jobs/index.ts",
44
- "./migrations": "./src/migrations/index.ts",
45
- "./entrypoint": "./src/entrypoint/index.ts",
46
- "./random": "./src/random/index.ts",
47
- "./redis": "./src/redis/index.ts",
48
- "./search": "./src/search/index.ts",
49
- "./search/meilisearch": "./src/search/meilisearch-adapter.ts",
50
- "./secrets": "./src/secrets/index.ts",
51
- "./stack": "./src/stack/index.ts",
52
- "./testing": "./src/testing/index.ts",
53
- "./testing/handler-context": "./src/testing/handler-context.ts",
54
- "./testing/e2e-generator": "./src/testing/e2e-generator.ts",
55
- "./time": "./src/time/index.ts",
56
- "./ui-types": "./src/ui-types/index.ts",
57
- "./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
+ }
58
143
  },
59
144
  "dependencies": {
60
- "bullmq": "^5.73.5",
61
- "bun-types": "^1.3.12",
145
+ "bullmq": "^5.76.7",
146
+ "bun-types": "^1.3.13",
62
147
  "drizzle-kit": "^0.31.10",
63
148
  "drizzle-orm": "^0.45.2",
64
- "hono": "^4.12.12",
65
- "i18next": "^26.0.4",
66
- "ioredis": "^5.6.0",
67
- "jose": "^6.0.11",
68
- "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",
69
154
  "pino": "^10.3.1",
70
155
  "postgres": "^3.4.9",
71
156
  "temporal-polyfill": "^0.3.2",
72
157
  "ts-morph": "^28.0.0",
73
158
  "uuid": "^14.0.0",
74
- "zod": "^4.3.6"
159
+ "zod": "^4.4.3"
75
160
  },
76
161
  "devDependencies": {
77
- "@cosmicdrift/kumiko-dispatcher-live": "0.2.3",
162
+ "@cosmicdrift/kumiko-dispatcher-live": "0.4.0",
78
163
  "@types/uuid": "^11.0.0",
79
- "bun-types": "^1.2.9",
80
- "drizzle-kit": "^0.31.0",
164
+ "bun-types": "^1.3.13",
165
+ "drizzle-kit": "^0.31.10",
81
166
  "pino-pretty": "^13.1.3"
82
167
  },
83
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
  }
@@ -243,7 +243,7 @@ const RAW_PROFILES: Readonly<Record<ComplianceProfileKey, ComplianceProfileRaw>>
243
243
  },
244
244
  tenantDestroyGracePeriod: { days: 30 },
245
245
  },
246
- };
246
+ } satisfies Readonly<Record<ComplianceProfileKey, ComplianceProfileRaw>>;
247
247
 
248
248
  // Raw-Profile (vor extends-Resolution) — `extends`-Profile dürfen
249
249
  // Required-Felder weglassen, sie kommen vom Base-Profile dazu.
@@ -262,11 +262,11 @@ type ComplianceProfileRaw = Partial<Omit<ComplianceProfile, "key" | "region" | "
262
262
  * Default-Fallback fuer "noch keine Wahl getroffen", mit sichtbarer
263
263
  * Warning. Production-Tenants sollen ein echtes Profile waehlen.
264
264
  */
265
- export const SELECTABLE_PROFILE_KEYS: readonly ComplianceProfileKey[] = [
265
+ export const SELECTABLE_PROFILE_KEYS = [
266
266
  "eu-dsgvo",
267
267
  "swiss-dsg",
268
268
  "de-hr-dsgvo-hgb",
269
- ];
269
+ ] as const satisfies readonly ComplianceProfileKey[];
270
270
 
271
271
  /**
272
272
  * Top-Level-Properties des `ComplianceProfile`-Type, die ein Tenant-
@@ -339,7 +339,7 @@ function deepMerge<T extends Record<string, unknown>>(
339
339
  out[k] = v;
340
340
  }
341
341
  }
342
- return out as T;
342
+ return out as T; // @cast-boundary generic-record
343
343
  }
344
344
 
345
345
  /**
@@ -355,7 +355,7 @@ function deepMerge<T extends Record<string, unknown>>(
355
355
  function resolveExtends(key: ComplianceProfileKey): ComplianceProfile {
356
356
  const raw = RAW_PROFILES[key];
357
357
  if (!raw.extends) {
358
- return raw as ComplianceProfile;
358
+ return raw as ComplianceProfile; // @cast-boundary schema-walk
359
359
  }
360
360
 
361
361
  const base = RAW_PROFILES[raw.extends];
@@ -366,7 +366,7 @@ function resolveExtends(key: ComplianceProfileKey): ComplianceProfile {
366
366
  }
367
367
 
368
368
  return deepMerge(
369
- base as Record<string, unknown>,
369
+ base as Record<string, unknown>, // @cast-boundary generic-record
370
370
  raw as unknown as Record<string, unknown>,
371
371
  ) as unknown as ComplianceProfile;
372
372
  }
@@ -379,7 +379,7 @@ function resolveExtends(key: ComplianceProfileKey): ComplianceProfile {
379
379
  export const COMPLIANCE_PROFILES: Readonly<Record<ComplianceProfileKey, ComplianceProfile>> =
380
380
  Object.fromEntries(
381
381
  (Object.keys(RAW_PROFILES) as ComplianceProfileKey[]).map((k) => [k, resolveExtends(k)]),
382
- ) as Readonly<Record<ComplianceProfileKey, ComplianceProfile>>;
382
+ ) as Readonly<Record<ComplianceProfileKey, ComplianceProfile>>; // @cast-boundary dynamic-key
383
383
 
384
384
  // --- Effective-Profile-Resolver ---
385
385
 
@@ -421,7 +421,7 @@ export function resolveComplianceProfile(args: {
421
421
 
422
422
  const merged = deepMerge(
423
423
  base as unknown as Record<string, unknown>,
424
- args.override as Record<string, unknown>,
424
+ args.override as Record<string, unknown>, // @cast-boundary engine-payload
425
425
  ) as unknown as ComplianceProfile;
426
426
  return { profile: merged };
427
427
  }
@@ -1,5 +1,5 @@
1
- import type { TenantId } from "@cosmicdrift/kumiko-framework/engine";
2
1
  import { and, eq, type SQL } from "drizzle-orm";
2
+ import type { TenantId } from "../engine/types/identifiers";
3
3
  import { NotFoundError } from "../errors";
4
4
  import type { DbConnection } from "./connection";
5
5
  import type { TenantDb } from "./tenant-db";
@@ -43,7 +43,7 @@ export async function assertExistsIn(
43
43
  const [row] = await db
44
44
  .select()
45
45
  .from(entity)
46
- .where(and(...conditions) as SQL);
46
+ .where(and(...conditions) as SQL); // @cast-boundary db-operator
47
47
 
48
48
  if (!row) {
49
49
  const entityName = options.entityName ?? String(options.field).replace(/Id$/, "");
package/src/db/cursor.ts CHANGED
@@ -1,5 +1,5 @@
1
- import type { EntityId, TenantId } from "@cosmicdrift/kumiko-framework/engine";
2
1
  import { and, asc, desc, eq, gt, inArray, type SQL, sql } from "drizzle-orm";
2
+ import type { EntityId, TenantId } from "../engine/types/identifiers";
3
3
  import type { SelectQuery as PgSelect } from "./dialect";
4
4
 
5
5
  export type CursorQueryOptions = {
@@ -61,7 +61,7 @@ export function applyCursorQuery<T extends PgSelect>(
61
61
  // werfen.
62
62
  conditions.push(sql`false`);
63
63
  } else {
64
- conditions.push(inArray(table.id, options.filterIds as readonly string[]));
64
+ conditions.push(inArray(table.id, options.filterIds as readonly string[])); // @cast-boundary db-operator
65
65
  }
66
66
  }
67
67
 
@@ -79,5 +79,5 @@ export function applyCursorQuery<T extends PgSelect>(
79
79
  options.sortDirection === "desc" ? result.orderBy(desc(column)) : result.orderBy(asc(column));
80
80
  }
81
81
 
82
- return result as T;
82
+ return result as T; // @cast-boundary engine-bridge
83
83
  }