@cosmicdrift/kumiko-framework 0.337.1 → 0.339.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/dist/api/api-constants.d.ts +1 -0
- package/dist/api/api-constants.js +9 -0
- package/dist/api/auth-middleware.d.ts +1 -0
- package/dist/api/auth-middleware.js +56 -12
- package/dist/api/routes.d.ts +2 -1
- package/dist/api/routes.js +29 -6
- package/dist/api/server.js +20 -2
- package/dist/bun-db/query.js +8 -2
- package/dist/compliance/sub-processors.js +10 -1
- package/dist/crypto/event-pii.js +13 -4
- package/dist/crypto/index.d.ts +1 -1
- package/dist/crypto/index.js +1 -1
- package/dist/crypto/pii-ciphertext-format.d.ts +4 -0
- package/dist/crypto/pii-ciphertext-format.js +11 -0
- package/dist/crypto/pii-field-encryption.d.ts +4 -2
- package/dist/crypto/pii-field-encryption.js +50 -21
- package/dist/crypto/system-event-pii.js +2 -2
- package/dist/db/blind-index-cleanup.d.ts +1 -0
- package/dist/db/blind-index-cleanup.js +11 -0
- package/dist/db/event-store-executor-read.js +18 -3
- package/dist/db/index.d.ts +1 -1
- package/dist/db/index.js +1 -1
- package/dist/db/queries/backfill-pii.js +4 -0
- package/dist/db/tenant-db.js +25 -13
- package/dist/engine/boot-validator/access-declarations.js +13 -7
- package/dist/engine/boot-validator/entity-handler.d.ts +2 -0
- package/dist/engine/boot-validator/entity-handler.js +45 -0
- package/dist/engine/boot-validator/index.js +2 -1
- package/dist/engine/boot-validator/pii-retention.js +41 -1
- package/dist/engine/config-helpers.d.ts +2 -1
- package/dist/engine/config-helpers.js +1 -0
- package/dist/engine/define-handler.js +1 -0
- package/dist/engine/extensions/escape-hatch-usage.js +3 -1
- package/dist/engine/extensions/user-data.d.ts +1 -1
- package/dist/engine/feature-ast/extractors/handlers.js +1 -0
- package/dist/engine/feature-ast/extractors/hooks.js +7 -1
- package/dist/engine/feature-ast/pattern-change-schema.d.ts +40 -0
- package/dist/engine/feature-ast/pattern-change-schema.js +2 -0
- package/dist/engine/feature-config-events-jobs.js +5 -1
- package/dist/engine/feature-entity-handlers.js +1 -0
- package/dist/engine/feature-manifest.d.ts +8 -0
- package/dist/engine/feature-manifest.js +9 -0
- package/dist/engine/feature-ui-extensions.js +9 -0
- package/dist/engine/index.d.ts +3 -2
- package/dist/engine/index.js +1 -0
- package/dist/engine/registry-ingest.d.ts +1 -0
- package/dist/engine/registry-ingest.js +9 -0
- package/dist/engine/registry.js +2 -1
- package/dist/engine/steps/retry.js +0 -1
- package/dist/engine/steps/wait-for-event.js +0 -1
- package/dist/engine/steps/wait.js +0 -4
- package/dist/engine/steps/workflow-system-events.d.ts +2 -0
- package/dist/engine/steps/workflow-system-events.js +89 -0
- package/dist/engine/types/index.d.ts +1 -1
- package/dist/jobs/job-runner.js +2 -1
- package/dist/pipeline/append-event-core.d.ts +3 -1
- package/dist/pipeline/append-event-core.js +8 -4
- package/dist/pipeline/dispatch-shared.js +37 -7
- package/dist/pipeline/system-identity-switch.js +10 -7
- package/package.json +4 -4
- package/src/changes.json +102 -0
package/src/changes.json
CHANGED
|
@@ -1,4 +1,106 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "0.339.0",
|
|
4
|
+
"type": "improvement",
|
|
5
|
+
"title": "Write handlers can declare tenantlessAnonymous",
|
|
6
|
+
"detail": "`r.writeHandler({ ..., tenantlessAnonymous: true })` lets an anonymous `POST /api/write` for exactly that handler run under `SYSTEM_TENANT_ID` where the anonymous middleware would otherwise answer `400 tenant_required` (resolver silent, no client tenant). It never applies to `/api/batch`, to a client-supplied `X-Tenant` or `kumiko_tenant`, to `tenant_mismatch` or when a tenant resolved; `tenantExists` and the lifecycle gate are skipped for the system tenant. Boot rejects the flag unless `access.roles` is exactly `[\"anonymous\"]` and a real `rateLimit` (not `{ disabled: true }`) is declared. `authMiddleware` gets the option `isTenantlessAnonymousWrite`, which `buildServer` builds from the registry.",
|
|
7
|
+
"migration": "keine"
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"version": "0.339.0",
|
|
11
|
+
"type": "breaking",
|
|
12
|
+
"title": "fetchForWriting handles reject appends after a concurrent write",
|
|
13
|
+
"migration": "Appends through a ctx.fetchForWriting handle now use the version the handle was read at. When two requests write the same aggregate concurrently, the later append no longer stacks on top silently: the request fails with 409 version_conflict (no automatic retry), and its transaction rolls back. Tests that fire concurrent writes on one aggregate and expected both to succeed must expect exactly one success. Clients should treat 409 version_conflict like a stale form: reload and retry."
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"version": "0.339.0",
|
|
17
|
+
"type": "improvement",
|
|
18
|
+
"title": "PII encryption for jsonb fields",
|
|
19
|
+
"detail": "`createJsonbField({ personal: \"tenant\" })` (or `\"self\"` / `{ of }`) is encrypted per subject like a text field, so crypto-shredding applies: after the key is erased, list and detail return `[[erased]]` for the field. The ciphertext uses the self-describing format `kumiko-pii:v3:` (v2 plus a JSON-typed plaintext, AAD-bound to subject and field), so a string, number or object round-trips by type. Existing plaintext objects, arrays and strings are read as-is and are not re-encrypted; they become ciphertext on the next write of the field, and the backfill skips jsonb fields. There is no search, filter, sort or index inside an encrypted jsonb field: boot rejects `personal` on a jsonb field that is in an entity index, has `sortable`, `filterable`, `searchable` or `lookupable`, or is the custom-fields `customFields` column. Event payload fields declared in `piiFields` may now be objects or arrays; they are encrypted in the same JSON format. Non-jsonb fields still require a string.",
|
|
20
|
+
"migration": "Runtimes before this release cannot read `kumiko-pii:v3:` ciphertext. Before annotating a jsonb field with `personal`, roll this version out to every instance, so a rolling deploy never has an old instance reading a value a new one wrote."
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"version": "0.339.0",
|
|
24
|
+
"type": "fix",
|
|
25
|
+
"title": "Uncaught route errors and client aborts go through one error handler",
|
|
26
|
+
"detail": "`buildServer` calls `app.onError(handleUncaughtRouteError)`, which reuses the `/api/query` helpers: a rethrown abort of the request signal maps to 499, everything else is classified with `toKumikoError`, logged with `logServerFault` and serialized with `serializeError`. Sub-apps mounted via `app.route()` inherit it.",
|
|
27
|
+
"migration": "keine"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"version": "0.339.0",
|
|
31
|
+
"type": "breaking",
|
|
32
|
+
"title": "ctx.db.unsafeRaw() takes no reason; the audit uses the declared escapeHatch reason",
|
|
33
|
+
"detail": "`TenantDb.unsafeRaw` is now `unsafeRaw(): DbRunner`. The `unsafe-raw` audit entry carries the reason from the `escapeHatch` declaration, as before. Calls without a matching declaration are still rejected with an `AccessDeniedError`.",
|
|
34
|
+
"migration": "Replace `ctx.db.unsafeRaw(\"...\")` with `ctx.db.unsafeRaw()`. The reason lives only in `escapeHatch: { reason }` on the handler, hook or job; TypeScript flags old calls. `ctx.systemDb.unsafeRaw(reason)` is unchanged."
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"version": "0.339.0",
|
|
38
|
+
"type": "improvement",
|
|
39
|
+
"title": "Workflow run-stream events are registered, so projections can apply them",
|
|
40
|
+
"detail": "`WORKFLOW_SYSTEM_EVENT_DEFS` declares the seven `kumiko:system:workflow.*` events with a payload schema, version 1 and the stance from `SYSTEM_EVENT_PII_STANCES`. `createRegistry` seeds them into the event map next to `r.defineEvent` events, so apply-key validation, the PII catalog and the upcaster chain see them. A typo such as `kumiko:system:workflow.run-faild` still fails boot. `appendDomainEventCore` keeps skipping schema validation for `kumiko:system:*` types. The feature manifest gains an optional top-level `systemEvents` list.",
|
|
41
|
+
"migration": "A projection whose row ids are not the workflow-run aggregate id (for example uuidv5-derived ids) still cannot pass the ghost-row guard and needs its own change: its row id must equal the workflow-run aggregate id (this applies to kumiko-enterprise's run-state projection). Hand-built `FeatureManifest` objects stay valid because `systemEvents` is optional; regenerate committed manifests with `bun run gen:manifest` in `samples/apps/use-all-bundled` to pick the list up."
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"version": "0.338.0",
|
|
45
|
+
"type": "improvement",
|
|
46
|
+
"title": "ctx.configFor(tenantId) resolves config for another tenant",
|
|
47
|
+
"detail": "Query and write handler contexts get `configFor(tenantId)` next to `config`, present when the config feature wired its accessor factory. The accessor runs as the target tenant's system user on a db scoped to that tenant, so the caller's user-scope values never leak into the result. Calls for another tenant throw `AccessDeniedError` unless the caller is the system identity or has the `SystemAdmin` role; the caller's own tenant needs no privilege and returns the same accessor as `ctx.config`.",
|
|
48
|
+
"migration": "No code change needed. Handlers that compute values for a tenant other than the caller's (limits, quotas, billing previews) call `ctx.configFor(tenantId)` instead of passing `ctx.config`."
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"version": "0.338.0",
|
|
52
|
+
"type": "improvement",
|
|
53
|
+
"title": "escapeHatch grants split the SYSTEM identity switch from raw access",
|
|
54
|
+
"detail": "escapeHatch: new optional `grants` (`systemIdentity`, `globalWrites`, `unsafeRaw`) narrows what a declaration unlocks. Without `grants` the declaration keeps unlocking all three. An empty or unknown `grants` list is a boot error. Bundled handlers that only needed the raw runner now declare `grants: [\"unsafeRaw\"]`, so they no longer gain the SYSTEM identity switch.",
|
|
55
|
+
"migration": "No action needed: `grants` is additive and an escapeHatch without it behaves as before.\nDeclare `grants: [\"unsafeRaw\"]` on handlers that only call ctx.db.unsafeRaw to drop the SYSTEM identity switch."
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"version": "0.338.0",
|
|
59
|
+
"type": "improvement",
|
|
60
|
+
"title": "boot warning for file and image fields without a personal annotation",
|
|
61
|
+
"detail": "Boot validation warns for every `file`, `image`, `files` and `images` field without a `personal` annotation. A forget with strategy `delete` keeps the binary of such a field and only severs the uploader link, so the stance has to be declared.",
|
|
62
|
+
"migration": "Mark fields that hold personal data with `personal: \"self\"` or `personal: { of: \"<ownerField>\" }`, and business data with `personal: false, reason: \"is_business_data\"`. Unannotated fields keep their binary on forget."
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"version": "0.338.0",
|
|
66
|
+
"type": "fix",
|
|
67
|
+
"title": "reference label search applies target read access on the plaintext path",
|
|
68
|
+
"detail": "Searching a list by a reference field's label now respects the target entity's row-level `access.read` and the label field's own `access.read` on the plaintext (ILIKE) path. Before, a viewer who could not read the target row or its label could probe names through the match results."
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"version": "0.338.0",
|
|
72
|
+
"type": "fix",
|
|
73
|
+
"title": "/api/schema answers 401 instead of 400 tenant_required after logout",
|
|
74
|
+
"detail": "With `anonymousAccess` and an authoritative tenant resolver, a request without a token to `/api/schema` ran through tenant resolution and failed with 400 `tenant_required`. Paths that need a session but no tenant (`/api/auth/*` and `/api/schema`) are now listed in `isSessionRequiredApiPath` and answer 401 `missing_token` before any tenant lookup.",
|
|
75
|
+
"migration": "No code change needed."
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"version": "0.338.0",
|
|
79
|
+
"type": "breaking",
|
|
80
|
+
"title": "searchable text fields must not declare a restricted access.read",
|
|
81
|
+
"detail": "Boot validation rejects a text field that is `searchable` (or `find: \"fuzzy\"`) and also has a restricted `access.read`. The search index is not filtered per role, so search matches leaked the value to roles that cannot read the field.",
|
|
82
|
+
"migration": "Remove `searchable` / `find: \"fuzzy\"` from the field, or remove the read restriction. Write-only `access.write` restrictions are unaffected."
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"version": "0.338.0",
|
|
86
|
+
"type": "breaking",
|
|
87
|
+
"title": "signature routes with a :param segment or wildcard must not match framework paths",
|
|
88
|
+
"detail": "Boot validation rejects `entry: \"signature\"` extra routes whose pattern, including `:param` segments, matches a framework path such as `/api/write`. Such a route skipped the JWT, origin and CSRF guards for that path.",
|
|
89
|
+
"migration": "Give the route a static prefix, for example `/api/webhooks/:provider` instead of `/api/:provider` or `/:provider/write`."
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
"version": "0.338.0",
|
|
93
|
+
"type": "fix",
|
|
94
|
+
"title": "planned Scaleway purposes listed as planned sub-processor entry",
|
|
95
|
+
"detail": "The sub-processor list now carries the planned Scaleway purposes (key custody and transactional email) as their own `status: \"planned\"` entry. The active Scaleway entry only names the backup mirror, so the public `planned` section and change notifications see the upcoming processing."
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"version": "0.338.0",
|
|
99
|
+
"type": "improvement",
|
|
100
|
+
"title": "unsafeRaw audit events carry the declared escapeHatch reason",
|
|
101
|
+
"detail": "`ctx.db.unsafeRaw(reason)` and `ctx.systemDb.unsafeRaw(reason)` now write the reason declared on the handler or hook `escapeHatch` into the escape-hatch audit event, not the string passed at the call. A call-site string can no longer put arbitrary text into the audit trail.",
|
|
102
|
+
"migration": "No action needed: calls that pass the same reason as the declaration audit unchanged. Calls with a different string now audit the declared reason."
|
|
103
|
+
},
|
|
2
104
|
{
|
|
3
105
|
"version": "0.337.1",
|
|
4
106
|
"type": "improvement",
|