@cosmicdrift/kumiko-framework 0.2.3 → 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.
- package/CHANGELOG.md +52 -0
- package/package.json +124 -39
- package/src/__tests__/full-stack.integration.ts +2 -2
- package/src/api/auth-routes.ts +5 -5
- package/src/api/jwt.ts +2 -2
- package/src/api/route-registrars.ts +1 -1
- package/src/api/routes.ts +3 -3
- package/src/api/server.ts +6 -7
- package/src/compliance/profiles.ts +8 -8
- package/src/db/assert-exists-in.ts +2 -2
- package/src/db/cursor.ts +3 -3
- package/src/db/event-store-executor.ts +19 -13
- package/src/db/located-timestamp.ts +1 -1
- package/src/db/money.ts +12 -2
- package/src/db/pg-error.ts +1 -1
- package/src/db/row-helpers.ts +1 -1
- package/src/db/table-builder.ts +3 -5
- package/src/db/tenant-db.ts +9 -9
- package/src/engine/__tests__/_pipeline-test-utils.ts +23 -0
- package/src/engine/__tests__/build-target.test.ts +135 -0
- package/src/engine/__tests__/codemod-pipeline.test.ts +551 -0
- package/src/engine/__tests__/entity-handlers.test.ts +3 -3
- package/src/engine/__tests__/event-helpers.test.ts +4 -4
- package/src/engine/__tests__/pipeline-engine.test.ts +215 -0
- package/src/engine/__tests__/pipeline-handler.integration.ts +894 -0
- package/src/engine/__tests__/pipeline-observability.integration.ts +142 -0
- package/src/engine/__tests__/pipeline-performance.integration.ts +152 -0
- package/src/engine/__tests__/pipeline-sub-pipelines.test.ts +288 -0
- package/src/engine/__tests__/raw-table.test.ts +2 -2
- package/src/engine/__tests__/steps-aggregate-append-event.test.ts +115 -0
- package/src/engine/__tests__/steps-aggregate-create.test.ts +92 -0
- package/src/engine/__tests__/steps-aggregate-update.test.ts +127 -0
- package/src/engine/__tests__/steps-call-feature.test.ts +123 -0
- package/src/engine/__tests__/steps-mail-send.test.ts +136 -0
- package/src/engine/__tests__/steps-read.test.ts +142 -0
- package/src/engine/__tests__/steps-resolver-utils.test.ts +50 -0
- package/src/engine/__tests__/steps-unsafe-projection-delete.test.ts +69 -0
- package/src/engine/__tests__/steps-unsafe-projection-upsert.test.ts +117 -0
- package/src/engine/__tests__/steps-webhook-send.test.ts +135 -0
- package/src/engine/__tests__/steps-workflow.test.ts +198 -0
- package/src/engine/__tests__/validate-projection-allowlist.test.ts +491 -0
- package/src/engine/__tests__/visual-tree-patterns.test.ts +251 -0
- package/src/engine/boot-validator/api-ext.ts +77 -0
- package/src/engine/boot-validator/config-deps.ts +163 -0
- package/src/engine/boot-validator/entity-handler.ts +466 -0
- package/src/engine/boot-validator/index.ts +159 -0
- package/src/engine/boot-validator/ownership.ts +198 -0
- package/src/engine/boot-validator/pii-retention.ts +155 -0
- package/src/engine/boot-validator/screens-nav.ts +624 -0
- package/src/engine/boot-validator.ts +1 -1804
- package/src/engine/build-app-schema.ts +1 -1
- package/src/engine/build-target.ts +99 -0
- package/src/engine/codemod/index.ts +15 -0
- package/src/engine/codemod/pipeline-codemod.ts +641 -0
- package/src/engine/config-helpers.ts +9 -19
- package/src/engine/constants.ts +1 -1
- package/src/engine/define-feature.ts +88 -9
- package/src/engine/define-handler.ts +89 -3
- package/src/engine/define-roles.ts +2 -2
- package/src/engine/define-step.ts +28 -0
- package/src/engine/define-workflow.ts +110 -0
- package/src/engine/entity-handlers.ts +10 -9
- package/src/engine/event-helpers.ts +4 -4
- package/src/engine/factories.ts +12 -12
- package/src/engine/feature-ast/__tests__/visual-tree-parse.test.ts +184 -0
- package/src/engine/feature-ast/extractors/index.ts +74 -0
- package/src/engine/feature-ast/extractors/round1.ts +110 -0
- package/src/engine/feature-ast/extractors/round2.ts +253 -0
- package/src/engine/feature-ast/extractors/round3.ts +471 -0
- package/src/engine/feature-ast/extractors/round4.ts +1365 -0
- package/src/engine/feature-ast/extractors/round5.ts +72 -0
- package/src/engine/feature-ast/extractors/round6.ts +66 -0
- package/src/engine/feature-ast/extractors/shared.ts +177 -0
- package/src/engine/feature-ast/parse.ts +7 -0
- package/src/engine/feature-ast/patch.ts +9 -1
- package/src/engine/feature-ast/patcher.ts +10 -3
- package/src/engine/feature-ast/patterns.ts +49 -1
- package/src/engine/feature-ast/render.ts +17 -1
- package/src/engine/index.ts +45 -2
- package/src/engine/pattern-library/__tests__/library.test.ts +6 -0
- package/src/engine/pattern-library/library.ts +42 -2
- package/src/engine/pipeline.ts +88 -0
- package/src/engine/projection-helpers.ts +1 -1
- package/src/engine/read-claim.ts +1 -1
- package/src/engine/registry.ts +30 -2
- package/src/engine/resolve-config-or-param.ts +4 -0
- package/src/engine/run-pipeline.ts +162 -0
- package/src/engine/schema-builder.ts +2 -4
- package/src/engine/state-machine.ts +1 -1
- package/src/engine/steps/_drizzle-boundary.ts +19 -0
- package/src/engine/steps/_duration-utils.ts +33 -0
- package/src/engine/steps/_no-return-guard.ts +21 -0
- package/src/engine/steps/_resolver-utils.ts +42 -0
- package/src/engine/steps/_step-dispatch-constants.ts +38 -0
- package/src/engine/steps/aggregate-append-event.ts +56 -0
- package/src/engine/steps/aggregate-create.ts +56 -0
- package/src/engine/steps/aggregate-update.ts +68 -0
- package/src/engine/steps/branch.ts +84 -0
- package/src/engine/steps/call-feature.ts +49 -0
- package/src/engine/steps/compute.ts +41 -0
- package/src/engine/steps/for-each.ts +111 -0
- package/src/engine/steps/mail-send.ts +44 -0
- package/src/engine/steps/read-find-many.ts +51 -0
- package/src/engine/steps/read-find-one.ts +58 -0
- package/src/engine/steps/retry.ts +87 -0
- package/src/engine/steps/return.ts +34 -0
- package/src/engine/steps/unsafe-projection-delete.ts +46 -0
- package/src/engine/steps/unsafe-projection-upsert.ts +69 -0
- package/src/engine/steps/wait-for-event.ts +71 -0
- package/src/engine/steps/wait.ts +69 -0
- package/src/engine/steps/webhook-send.ts +71 -0
- package/src/engine/system-user.ts +1 -1
- package/src/engine/types/feature.ts +92 -1
- package/src/engine/types/handlers.ts +18 -10
- package/src/engine/types/identifiers.ts +1 -0
- package/src/engine/types/index.ts +12 -1
- package/src/engine/types/step.ts +334 -0
- package/src/engine/types/target-ref.ts +21 -0
- package/src/engine/types/tree-node.ts +130 -0
- package/src/engine/types/workspace.ts +7 -0
- package/src/engine/validate-projection-allowlist.ts +161 -0
- package/src/event-store/snapshot.ts +1 -1
- package/src/event-store/upcaster-dead-letter.ts +1 -1
- package/src/event-store/upcaster.ts +1 -1
- package/src/files/file-routes.ts +1 -1
- package/src/files/types.ts +2 -2
- package/src/jobs/job-runner.ts +10 -10
- package/src/lifecycle/lifecycle.ts +0 -3
- package/src/logging/index.ts +1 -0
- package/src/logging/pino-logger.ts +11 -7
- package/src/logging/utils.ts +24 -0
- package/src/observability/prometheus-meter.ts +7 -5
- package/src/pipeline/__tests__/archive-stream.integration.ts +1 -1
- package/src/pipeline/__tests__/causation-chain.integration.ts +1 -1
- package/src/pipeline/__tests__/domain-events-projections.integration.ts +3 -3
- package/src/pipeline/__tests__/event-define-event-strict.integration.ts +4 -4
- package/src/pipeline/__tests__/load-aggregate-query.integration.ts +1 -1
- package/src/pipeline/__tests__/msp-multi-hop.integration.ts +3 -3
- package/src/pipeline/__tests__/msp-rebuild.integration.ts +3 -3
- package/src/pipeline/__tests__/multi-stream-projection.integration.ts +2 -2
- package/src/pipeline/__tests__/query-projection.integration.ts +5 -5
- package/src/pipeline/append-event-core.ts +22 -6
- package/src/pipeline/dispatcher-utils.ts +188 -0
- package/src/pipeline/dispatcher.ts +63 -283
- package/src/pipeline/distributed-lock.ts +1 -1
- package/src/pipeline/entity-cache.ts +2 -2
- package/src/pipeline/event-consumer-state.ts +0 -13
- package/src/pipeline/event-dispatcher.ts +4 -4
- package/src/pipeline/index.ts +0 -2
- package/src/pipeline/lifecycle-pipeline.ts +6 -12
- package/src/pipeline/msp-rebuild.ts +5 -5
- package/src/pipeline/multi-stream-apply-context.ts +6 -7
- package/src/pipeline/projection-rebuild.ts +2 -2
- package/src/pipeline/projection-state.ts +0 -12
- package/src/rate-limit/__tests__/resolver.integration.ts +8 -4
- package/src/rate-limit/resolver.ts +1 -1
- package/src/search/in-memory-adapter.ts +1 -1
- package/src/search/meilisearch-adapter.ts +3 -3
- package/src/search/types.ts +1 -1
- package/src/secrets/leak-guard.ts +2 -2
- package/src/stack/request-helper.ts +9 -5
- package/src/stack/test-stack.ts +1 -1
- package/src/testing/handler-context.ts +4 -4
- package/src/testing/http-cookies.ts +1 -1
- package/src/time/tz-context.ts +1 -2
- package/src/ui-types/index.ts +4 -0
- package/src/engine/feature-ast/extractors.ts +0 -2602
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,57 @@
|
|
|
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
|
+
|
|
3
55
|
## 0.2.3
|
|
4
56
|
|
|
5
57
|
## 0.2.2
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-framework",
|
|
3
|
-
"version": "0.
|
|
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,56 +28,141 @@
|
|
|
28
28
|
"runtime": "runtime"
|
|
29
29
|
},
|
|
30
30
|
"exports": {
|
|
31
|
-
"./compliance":
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
"./
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
"./
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
"./
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
"./
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
"./
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
"./
|
|
56
|
-
|
|
57
|
-
|
|
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.
|
|
61
|
-
"bun-types": "^1.3.
|
|
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.
|
|
65
|
-
"i18next": "^26.0
|
|
66
|
-
"ioredis": "^5.
|
|
67
|
-
"jose": "^6.
|
|
68
|
-
"meilisearch": "^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
|
|
159
|
+
"zod": "^4.4.3"
|
|
75
160
|
},
|
|
76
161
|
"devDependencies": {
|
|
77
|
-
"@cosmicdrift/kumiko-dispatcher-live": "0.
|
|
162
|
+
"@cosmicdrift/kumiko-dispatcher-live": "0.3.0",
|
|
78
163
|
"@types/uuid": "^11.0.0",
|
|
79
|
-
"bun-types": "^1.
|
|
80
|
-
"drizzle-kit": "^0.31.
|
|
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, "
|
|
49
|
+
ctx: Pick<HandlerContext, "unsafeAppendEvent">,
|
|
50
50
|
id: EntityId,
|
|
51
51
|
email: string,
|
|
52
52
|
): Promise<void> {
|
|
53
|
-
await ctx.
|
|
53
|
+
await ctx.unsafeAppendEvent({
|
|
54
54
|
aggregateId: String(id),
|
|
55
55
|
aggregateType: "user",
|
|
56
56
|
type: USER_CREATED_EVENT,
|
package/src/api/auth-routes.ts
CHANGED
|
@@ -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
|
|
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
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { and, asc, desc, eq, gt, inArray, lt, ne, type SQL, sql } from "drizzle-orm";
|
|
2
|
+
import type { AnyPgColumn } from "drizzle-orm/pg-core";
|
|
2
3
|
import { requestContext } from "../api/request-context";
|
|
3
4
|
import { checkWriteFieldOwnership } from "../engine/field-access";
|
|
4
5
|
import {
|
|
@@ -32,7 +33,7 @@ import {
|
|
|
32
33
|
} from "../event-store";
|
|
33
34
|
import type { EntityCache } from "../pipeline/entity-cache";
|
|
34
35
|
import type { SearchAdapter } from "../search/types";
|
|
35
|
-
import { generateId } from "../utils";
|
|
36
|
+
import { assertUnreachable, generateId } from "../utils";
|
|
36
37
|
import { applyEntityEvent } from "./apply-entity-event";
|
|
37
38
|
import { flattenCompoundTypes, rehydrateCompoundTypes } from "./compound-types";
|
|
38
39
|
import type { DbRow } from "./connection";
|
|
@@ -54,8 +55,11 @@ type Table = TableColumns<any>;
|
|
|
54
55
|
// vs-Type-Compat ohnehin Kontrolle was reinkommt.
|
|
55
56
|
//
|
|
56
57
|
// Empty-array IN ist explizit "no match" (SQL false), nicht "match all".
|
|
57
|
-
|
|
58
|
-
|
|
58
|
+
function buildFilterCondition(
|
|
59
|
+
col: AnyPgColumn,
|
|
60
|
+
op: "eq" | "ne" | "lt" | "gt" | "in",
|
|
61
|
+
value: unknown,
|
|
62
|
+
): SQL {
|
|
59
63
|
switch (op) {
|
|
60
64
|
case "eq":
|
|
61
65
|
return eq(col, value as never); // @cast-boundary db-operator
|
|
@@ -70,6 +74,8 @@ function buildFilterCondition(col: any, op: "eq" | "ne" | "lt" | "gt" | "in", va
|
|
|
70
74
|
return inArray(col, value as never); // @cast-boundary db-operator
|
|
71
75
|
}
|
|
72
76
|
return sql`false`;
|
|
77
|
+
default:
|
|
78
|
+
assertUnreachable(op, "filter op");
|
|
73
79
|
}
|
|
74
80
|
}
|
|
75
81
|
|
|
@@ -281,7 +287,7 @@ export function createEventStoreExecutor(
|
|
|
281
287
|
}
|
|
282
288
|
// Drizzle's variadic `and()` is typed `SQL | undefined`; conditions is
|
|
283
289
|
// guaranteed non-empty above (we pushed at least one).
|
|
284
|
-
return and(...conditions) as SQL;
|
|
290
|
+
return and(...conditions) as SQL; // @cast-boundary db-operator
|
|
285
291
|
}
|
|
286
292
|
|
|
287
293
|
async function loadById(id: EntityId, db: TenantDb): Promise<Record<string, unknown> | null> {
|
|
@@ -295,7 +301,7 @@ export function createEventStoreExecutor(
|
|
|
295
301
|
// Respect an explicit id in the payload (seed pattern, SCIM import). Without
|
|
296
302
|
// one the framework mints a fresh UUIDv7 via generateId. Strip it out of the
|
|
297
303
|
// event payload so defaults + downstream consumers don't see a redundant id field.
|
|
298
|
-
const explicitId = typeof payload["id"] === "string" ? (payload["id"] as string) : undefined;
|
|
304
|
+
const explicitId = typeof payload["id"] === "string" ? (payload["id"] as string) : undefined; // @cast-boundary engine-payload
|
|
299
305
|
const aggregateId = explicitId ?? generateId();
|
|
300
306
|
const { id: _id, ...payloadWithoutId } = payload;
|
|
301
307
|
const data = applyDefaults(payloadWithoutId);
|
|
@@ -561,7 +567,7 @@ export function createEventStoreExecutor(
|
|
|
561
567
|
isSuccess: true,
|
|
562
568
|
data: {
|
|
563
569
|
kind: "save",
|
|
564
|
-
id: data["id"] as EntityId,
|
|
570
|
+
id: data["id"] as EntityId, // @cast-boundary engine-payload
|
|
565
571
|
data,
|
|
566
572
|
changes: payload.changes,
|
|
567
573
|
previous,
|
|
@@ -793,7 +799,7 @@ export function createEventStoreExecutor(
|
|
|
793
799
|
}
|
|
794
800
|
}
|
|
795
801
|
|
|
796
|
-
const whereClause = conditions.length > 0 ? (and(...conditions) as SQL) : undefined;
|
|
802
|
+
const whereClause = conditions.length > 0 ? (and(...conditions) as SQL) : undefined; // @cast-boundary db-operator
|
|
797
803
|
let query = whereClause
|
|
798
804
|
? db.select().from(table).where(whereClause)
|
|
799
805
|
: db.select().from(table);
|
|
@@ -822,13 +828,13 @@ export function createEventStoreExecutor(
|
|
|
822
828
|
await entityCache.mset(
|
|
823
829
|
user.tenantId,
|
|
824
830
|
entityName,
|
|
825
|
-
rows.map((r) => ({ id: r["id"] as EntityId, data: r })),
|
|
831
|
+
rows.map((r) => ({ id: r["id"] as EntityId, data: r })), // @cast-boundary engine-payload
|
|
826
832
|
);
|
|
827
833
|
}
|
|
828
834
|
|
|
829
835
|
const lastRow = rows[rows.length - 1];
|
|
830
836
|
const nextCursor =
|
|
831
|
-
rows.length === limit && lastRow ? encodeCursor(lastRow["id"] as string) : null;
|
|
837
|
+
rows.length === limit && lastRow ? encodeCursor(lastRow["id"] as string) : null; // @cast-boundary engine-payload
|
|
832
838
|
|
|
833
839
|
// total: extra COUNT(*) — nur wenn explizit angefordert (Pager-UI).
|
|
834
840
|
// Postgres-Cost ist O(table-scan) ohne Filter, mit Filter so teuer
|
|
@@ -842,7 +848,7 @@ export function createEventStoreExecutor(
|
|
|
842
848
|
const countQuery = whereClause
|
|
843
849
|
? db.select({ count: sql<number>`count(*)::int` }).from(table).where(whereClause)
|
|
844
850
|
: db.select({ count: sql<number>`count(*)::int` }).from(table);
|
|
845
|
-
const countRow = (await countQuery) as Array<{ count: number }>;
|
|
851
|
+
const countRow = (await countQuery) as Array<{ count: number }>; // @cast-boundary db-row
|
|
846
852
|
total = countRow[0]?.count ?? 0;
|
|
847
853
|
}
|
|
848
854
|
}
|
|
@@ -872,7 +878,7 @@ export function createEventStoreExecutor(
|
|
|
872
878
|
const checked = await db
|
|
873
879
|
.select()
|
|
874
880
|
.from(table)
|
|
875
|
-
.where(and(idFilter(payload.id), ownership.sql) as SQL)
|
|
881
|
+
.where(and(idFilter(payload.id), ownership.sql) as SQL) // @cast-boundary db-operator
|
|
876
882
|
.limit(1);
|
|
877
883
|
if (checked.length === 0) return null;
|
|
878
884
|
}
|
|
@@ -887,11 +893,11 @@ export function createEventStoreExecutor(
|
|
|
887
893
|
// thread the ownership clause.
|
|
888
894
|
const baseFilter = idFilter(payload.id);
|
|
889
895
|
const whereClause =
|
|
890
|
-
ownership.kind === "sql" ? (and(baseFilter, ownership.sql) as SQL) : baseFilter;
|
|
896
|
+
ownership.kind === "sql" ? (and(baseFilter, ownership.sql) as SQL) : baseFilter; // @cast-boundary db-operator
|
|
891
897
|
const rows = (await db.select().from(table).where(whereClause).limit(1)) as Record<
|
|
892
898
|
string,
|
|
893
899
|
unknown
|
|
894
|
-
>[];
|
|
900
|
+
>[]; // @cast-boundary db-row
|
|
895
901
|
const raw = rows[0];
|
|
896
902
|
if (!raw) return null;
|
|
897
903
|
const row = rehydrateCompoundTypes(raw, entity);
|
|
@@ -51,7 +51,7 @@ export function flattenLocatedTimestamp(
|
|
|
51
51
|
`flattenLocatedTimestamp: field "${name}" expects { at, tz } or { utc, tz } object, got ${typeof raw}`,
|
|
52
52
|
);
|
|
53
53
|
}
|
|
54
|
-
const pair = raw as { at?: string; tz?: string; utc?: string };
|
|
54
|
+
const pair = raw as { at?: string; tz?: string; utc?: string }; // @cast-boundary schema-walk
|
|
55
55
|
|
|
56
56
|
delete result[name];
|
|
57
57
|
|