@cosmicdrift/kumiko-framework 0.385.0 → 0.387.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/LICENSE +21 -57
- package/README.md +2 -2
- package/agents/widgets.md +39 -39
- package/dist/api/api-constants.js +2 -2
- package/dist/api/auth-middleware.js +7 -7
- package/dist/api/auth-rate-limiters.js +7 -3
- package/dist/api/auth-routes-mfa.js +1 -1
- package/dist/api/auth-routes-session.js +1 -1
- package/dist/api/auth-routes-shared.js +7 -3
- package/dist/api/auth-routes-signup.js +1 -1
- package/dist/api/client-ip.js +3 -1
- package/dist/api/extra-route.js +1 -1
- package/dist/api/pii-leak-guard.js +4 -2
- package/dist/api/readiness.js +5 -5
- package/dist/api/redis-sse-broker.js +8 -6
- package/dist/api/request-context.d.ts +2 -0
- package/dist/api/request-context.js +16 -7
- package/dist/api/routes.js +2 -2
- package/dist/api/server.js +34 -33
- package/dist/api/sse-broker.js +1 -1
- package/dist/api/sse-route.d.ts +13 -13
- package/dist/api/sse-route.js +15 -15
- package/dist/auth/index.js +4 -4
- package/dist/auth/roles.d.ts +4 -4
- package/dist/auth/roles.js +24 -24
- package/dist/compliance/duration-spec.js +18 -20
- package/dist/compliance/index.js +2 -3
- package/dist/compliance/override-schema.d.ts +4 -4
- package/dist/compliance/override-schema.js +19 -22
- package/dist/compliance/profiles.d.ts +26 -29
- package/dist/compliance/profiles.js +62 -69
- package/dist/compliance/sub-processors.d.ts +28 -30
- package/dist/compliance/sub-processors.js +17 -21
- package/dist/consumer-cli.js +4 -4
- package/dist/crypto/blind-index.js +18 -17
- package/dist/crypto/ciphertext-pattern.js +1 -1
- package/dist/crypto/event-pii.js +3 -3
- package/dist/crypto/kek-source.d.ts +1 -1
- package/dist/crypto/kek-source.js +3 -2
- package/dist/crypto/kms-adapter.d.ts +1 -1
- package/dist/crypto/kms-adapter.js +4 -6
- package/dist/crypto/kms-wiring.d.ts +1 -2
- package/dist/crypto/kms-wiring.js +2 -2
- package/dist/crypto/pg-kms-adapter.js +2 -6
- package/dist/crypto/pii-field-encryption.js +4 -4
- package/dist/crypto/subject-resolver.d.ts +1 -1
- package/dist/crypto/subject-resolver.js +2 -2
- package/dist/db/apply-entity-event.d.ts +6 -6
- package/dist/db/apply-entity-event.js +55 -55
- package/dist/db/blind-index-cleanup.js +7 -7
- package/dist/db/collect-table-metas.js +10 -11
- package/dist/db/compound-types.d.ts +1 -1
- package/dist/db/compound-types.js +12 -12
- package/dist/db/cross-tenant-reads.d.ts +8 -0
- package/dist/db/cross-tenant-reads.js +23 -0
- package/dist/db/cursor.js +3 -4
- package/dist/db/dialect.d.ts +2 -2
- package/dist/db/dialect.js +2 -2
- package/dist/db/eagerload.d.ts +3 -3
- package/dist/db/eagerload.js +21 -12
- package/dist/db/entity-table-meta.d.ts +2 -2
- package/dist/db/entity-table-meta.js +4 -4
- package/dist/db/event-store-executor-context.d.ts +1 -1
- package/dist/db/event-store-executor-context.js +13 -13
- package/dist/db/event-store-executor-read.js +27 -28
- package/dist/db/event-store-executor-write.js +65 -70
- package/dist/db/located-timestamp.d.ts +11 -11
- package/dist/db/located-timestamp.js +29 -30
- package/dist/db/migrate-generator.d.ts +1 -1
- package/dist/db/migrate-generator.js +26 -19
- package/dist/db/migrate-runner.js +25 -25
- package/dist/db/money.d.ts +6 -6
- package/dist/db/money.js +29 -27
- package/dist/db/queries/backfill-pii.js +5 -5
- package/dist/db/queries/ddl.js +1 -1
- package/dist/db/queries/es-ops.d.ts +1 -1
- package/dist/db/queries/es-ops.js +1 -1
- package/dist/db/queries/event-consumer.js +3 -3
- package/dist/db/queries/event-store.js +4 -2
- package/dist/db/queries/projection-rebuild.js +3 -3
- package/dist/db/queries/raw-sql.d.ts +2 -2
- package/dist/db/queries/raw-sql.js +2 -2
- package/dist/db/queries/shadow-swap.js +19 -19
- package/dist/db/queries/test-stack.d.ts +4 -1
- package/dist/db/queries/test-stack.js +15 -2
- package/dist/db/query.d.ts +1 -1
- package/dist/db/query.js +6 -6
- package/dist/db/rebuild-marker.js +20 -21
- package/dist/db/replay-migration-sql.js +2 -2
- package/dist/db/schema-inspection.js +4 -5
- package/dist/db/table-builder.d.ts +1 -2
- package/dist/db/table-builder.js +82 -82
- package/dist/db/tenant-db.d.ts +2 -2
- package/dist/db/tenant-db.js +9 -2
- package/dist/derivatives/delete-stored-file.js +5 -2
- package/dist/derivatives/derivatives-context.js +1 -1
- package/dist/engine/boot-validator/access-roles.js +4 -3
- package/dist/engine/boot-validator/api-ext.js +14 -14
- package/dist/engine/boot-validator/config-deps.js +7 -6
- package/dist/engine/boot-validator/detail-screens.js +3 -3
- package/dist/engine/boot-validator/entity-handler.js +60 -61
- package/dist/engine/boot-validator/gdpr-storage.js +4 -4
- package/dist/engine/boot-validator/i18n-keys.js +1 -1
- package/dist/engine/boot-validator/index.d.ts +2 -2
- package/dist/engine/boot-validator/index.js +20 -20
- package/dist/engine/boot-validator/nav.js +5 -5
- package/dist/engine/boot-validator/ownership.js +12 -9
- package/dist/engine/boot-validator/parent-ref.js +1 -1
- package/dist/engine/boot-validator/pii-retention.js +50 -57
- package/dist/engine/boot-validator/projection-list-screens.js +7 -7
- package/dist/engine/boot-validator/query-refs.js +1 -1
- package/dist/engine/boot-validator/screens-actions.js +15 -16
- package/dist/engine/boot-validator/screens-forms.js +10 -11
- package/dist/engine/boot-validator/screens-lists.js +14 -15
- package/dist/engine/boot-validator/screens.js +34 -40
- package/dist/engine/boot-validator/security-baseline.js +3 -2
- package/dist/engine/boot-validator/transfer-graph.js +2 -2
- package/dist/engine/boot-validator/workspaces.js +3 -3
- package/dist/engine/bridge-stub.js +3 -4
- package/dist/engine/build-app-schema.js +75 -75
- package/dist/engine/build-config-feature-schema.js +50 -50
- package/dist/engine/build-target.js +20 -23
- package/dist/engine/codemod/pipeline-codemod.js +20 -13
- package/dist/engine/config-helpers.js +4 -4
- package/dist/engine/constants.js +2 -2
- package/dist/engine/create-app.js +2 -2
- package/dist/engine/define-feature.js +12 -11
- package/dist/engine/define-handler.js +12 -11
- package/dist/engine/define-step.js +1 -1
- package/dist/engine/define-workflow.d.ts +5 -5
- package/dist/engine/define-workflow.js +6 -6
- package/dist/engine/entity-handlers.js +13 -14
- package/dist/engine/event-def.js +2 -2
- package/dist/engine/extension-names.d.ts +16 -17
- package/dist/engine/extension-names.js +37 -38
- package/dist/engine/extensions/escape-hatch-usage.js +1 -1
- package/dist/engine/extensions/tenant-data.js +2 -2
- package/dist/engine/extensions/user-data.d.ts +23 -24
- package/dist/engine/factories.d.ts +14 -13
- package/dist/engine/factories.js +38 -37
- package/dist/engine/feature-ast/entity-field-types.js +1 -1
- package/dist/engine/feature-ast/extractors/handlers.js +1 -1
- package/dist/engine/feature-ast/extractors/hooks.js +1 -1
- package/dist/engine/feature-ast/extractors/shared.d.ts +2 -3
- package/dist/engine/feature-ast/extractors/shared.js +2 -3
- package/dist/engine/feature-ast/parse.js +13 -13
- package/dist/engine/feature-ast/patch.js +1 -1
- package/dist/engine/feature-ast/pattern-change-schema.d.ts +8 -0
- package/dist/engine/feature-ast/pattern-change-schema.js +1 -1
- package/dist/engine/feature-ast/patterns.d.ts +2 -2
- package/dist/engine/feature-ast/render.js +2 -2
- package/dist/engine/feature-config-events-jobs.js +15 -12
- package/dist/engine/feature-entity-handlers.js +9 -5
- package/dist/engine/feature-manifest.d.ts +9 -9
- package/dist/engine/feature-manifest.js +6 -7
- package/dist/engine/feature-ui-extensions.d.ts +3 -3
- package/dist/engine/feature-ui-extensions.js +30 -22
- package/dist/engine/field-access.js +5 -6
- package/dist/engine/index.d.ts +1 -0
- package/dist/engine/index.js +4 -4
- package/dist/engine/object-form.d.ts +1 -0
- package/dist/engine/object-form.js +14 -4
- package/dist/engine/ownership.js +3 -3
- package/dist/engine/parse-ref-target.js +5 -7
- package/dist/engine/pattern-library/index.js +2 -2
- package/dist/engine/pattern-library/library.js +2 -2
- package/dist/engine/pattern-library/types.js +3 -3
- package/dist/engine/pipeline.js +2 -2
- package/dist/engine/project-app-schema-for-roles.js +1 -1
- package/dist/engine/projection-helpers.js +9 -9
- package/dist/engine/qualified-name.js +2 -2
- package/dist/engine/registry-facade.js +1 -1
- package/dist/engine/registry-ingest.js +12 -12
- package/dist/engine/registry-state.js +17 -19
- package/dist/engine/registry-validate.js +12 -12
- package/dist/engine/run-in.js +2 -2
- package/dist/engine/run-pipeline.js +7 -0
- package/dist/engine/schema-builder.js +40 -38
- package/dist/engine/screen-filter-ops.js +17 -20
- package/dist/engine/screen-helpers.js +4 -3
- package/dist/engine/state-machine.d.ts +12 -12
- package/dist/engine/state-machine.js +7 -7
- package/dist/engine/steps/_resolver-utils.js +4 -3
- package/dist/engine/steps/for-each.js +4 -4
- package/dist/engine/steps/unsafe-projection-delete.js +1 -1
- package/dist/engine/steps/unsafe-projection-upsert.js +4 -4
- package/dist/engine/steps/wait-for-event.js +4 -4
- package/dist/engine/steps/workflow-system-events.js +3 -3
- package/dist/engine/tier-resolver-extension.d.ts +29 -27
- package/dist/engine/tier-resolver-extension.js +26 -26
- package/dist/engine/transfer-adjacency.js +2 -2
- package/dist/engine/validate-projection-allowlist.js +7 -27
- package/dist/engine/walk-step-instances.d.ts +2 -0
- package/dist/engine/walk-step-instances.js +38 -0
- package/dist/engine/where-rule-lint.js +1 -1
- package/dist/engine/write-handler-qn-extract.js +1 -1
- package/dist/entrypoint/index.js +3 -3
- package/dist/env/index.d.ts +7 -6
- package/dist/env/index.js +10 -9
- package/dist/errors/transition-details.js +7 -7
- package/dist/errors/write-error-info.d.ts +6 -6
- package/dist/errors/write-error-info.js +6 -6
- package/dist/es-ops/context.d.ts +9 -9
- package/dist/es-ops/context.js +28 -28
- package/dist/es-ops/index.js +7 -9
- package/dist/es-ops/operations-schema.js +4 -10
- package/dist/es-ops/runner.d.ts +11 -11
- package/dist/es-ops/runner.js +49 -53
- package/dist/es-ops/types.d.ts +55 -55
- package/dist/es-ops/types.js +11 -11
- package/dist/event-store/admin-api.js +6 -7
- package/dist/event-store/errors.js +1 -1
- package/dist/event-store/event-store.js +2 -2
- package/dist/event-store/rebuild-dead-letter.js +1 -1
- package/dist/files/feature.js +9 -10
- package/dist/files/file-handle.js +5 -6
- package/dist/files/file-ref-entity.js +15 -15
- package/dist/files/file-ref-table.js +7 -8
- package/dist/files/file-routes.d.ts +7 -0
- package/dist/files/file-routes.js +11 -5
- package/dist/files/in-memory-provider.js +8 -9
- package/dist/files/local-provider.js +18 -25
- package/dist/files/provider-resolver.d.ts +7 -7
- package/dist/files/storage-tracking.js +2 -2
- package/dist/files/zip-stream.d.ts +12 -19
- package/dist/files/zip-stream.js +44 -63
- package/dist/i18n/required-surface-keys.d.ts +1 -1
- package/dist/i18n/required-surface-keys.js +8 -8
- package/dist/internal/stack-support.d.ts +1 -1
- package/dist/internal/stack-support.js +1 -1
- package/dist/jobs/job-runner-helpers.d.ts +1 -2
- package/dist/jobs/job-runner-helpers.js +8 -18
- package/dist/jobs/job-runner-schedulers.d.ts +1 -1
- package/dist/jobs/job-runner-schedulers.js +3 -4
- package/dist/jobs/job-runner.js +17 -18
- package/dist/lifecycle/lifecycle.js +3 -5
- package/dist/lifecycle/signal-handlers.js +5 -4
- package/dist/logging/utils.d.ts +2 -1
- package/dist/logging/utils.js +38 -10
- package/dist/migrations/index.js +1 -1
- package/dist/migrations/kumiko-drift.js +14 -13
- package/dist/migrations/pending-rebuilds.d.ts +21 -20
- package/dist/migrations/pending-rebuilds.js +27 -27
- package/dist/migrations/projection-table-index.d.ts +3 -3
- package/dist/migrations/projection-table-index.js +8 -8
- package/dist/observability/metric-validator.js +1 -1
- package/dist/observability/metrics-handle.js +3 -3
- package/dist/pipeline/append-event-core.js +11 -2
- package/dist/pipeline/cascade-handler.d.ts +1 -1
- package/dist/pipeline/dispatch-batch.js +2 -2
- package/dist/pipeline/dispatch-query.js +10 -10
- package/dist/pipeline/dispatch-shared.d.ts +1 -0
- package/dist/pipeline/dispatch-shared.js +42 -19
- package/dist/pipeline/dispatch-stream.js +1 -1
- package/dist/pipeline/dispatch-write.js +3 -3
- package/dist/pipeline/dispatcher-utils.d.ts +1 -1
- package/dist/pipeline/dispatcher-utils.js +1 -1
- package/dist/pipeline/event-consumer-state.js +2 -2
- package/dist/pipeline/event-dispatcher-delivery.js +1 -1
- package/dist/pipeline/event-dispatcher.js +17 -28
- package/dist/pipeline/irreversible-operation-gate.js +1 -1
- package/dist/pipeline/lifecycle-pipeline.js +9 -9
- package/dist/pipeline/member-read-only-transaction.js +1 -3
- package/dist/pipeline/msp-rebuild.js +1 -1
- package/dist/pipeline/projection-rebuild.js +13 -12
- package/dist/pipeline/system-hooks.js +39 -41
- package/dist/pipeline/system-identity-switch.js +1 -3
- package/dist/pipeline/tenant-timezone-cache.js +1 -1
- package/dist/random/generate.d.ts +29 -29
- package/dist/random/generate.js +30 -30
- package/dist/random/words.js +20 -20
- package/dist/rate-limit/middleware.js +9 -5
- package/dist/redis/client.js +2 -2
- package/dist/redis/pubsub-signal.js +14 -17
- package/dist/schema-cli.js +116 -22
- package/dist/search/index.js +4 -4
- package/dist/search/purge-subject.js +4 -5
- package/dist/search/reindex-entity.js +6 -9
- package/dist/time/boot-tz-warning.d.ts +2 -2
- package/dist/time/boot-tz-warning.js +8 -8
- package/dist/time/iana.js +10 -10
- package/dist/time/index.js +9 -9
- package/dist/time/legacy-date.d.ts +2 -2
- package/dist/time/legacy-date.js +6 -8
- package/dist/time/polyfill.js +1 -1
- package/dist/time/tz-context.d.ts +1 -1
- package/dist/time/tz-context.js +1 -1
- package/dist/ui-types/app-schema.js +7 -7
- package/dist/ui-types/list-row-meta.js +4 -4
- package/dist/upgrade-cli.js +10 -9
- package/dist/utils/compare.js +1 -1
- package/package.json +4 -4
- package/src/changes.json +54 -0
- package/src/scripts/codemod/crypto-shredding-testing-move.ts +2 -2
- package/src/scripts/codemod/migrate-cross-tenant.ts +1 -1
- package/src/scripts/codemod/migrate-open-to-all.ts +1 -1
- package/src/scripts/codemod/pii-personal-migration.ts +17 -8
- package/src/scripts/codemod/unprocessable-error-details-reason.ts +2 -2
package/LICENSE
CHANGED
|
@@ -1,57 +1,21 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
controlling, or under common control with the Licensor ("Affiliates"), or
|
|
23
|
-
contractors acting on their behalf. The Licensor remains free to use the
|
|
24
|
-
Licensed Work for any purpose, including for the operation of kumiko.rocks.
|
|
25
|
-
|
|
26
|
-
Change Date: 2030-05-05
|
|
27
|
-
Change License: Apache License, Version 2.0
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
Terms
|
|
31
|
-
|
|
32
|
-
The Licensor hereby grants you the right to copy, modify, create derivative works,
|
|
33
|
-
redistribute, and make non-production use of the Licensed Work. The Licensor may
|
|
34
|
-
make an Additional Use Grant, above, permitting limited production use.
|
|
35
|
-
|
|
36
|
-
Effective on the Change Date, or the fourth anniversary of the first publicly
|
|
37
|
-
available distribution of the Licensed Work under this License, whichever comes
|
|
38
|
-
first, this License will convert to the Change License.
|
|
39
|
-
|
|
40
|
-
This Business Source License governs use of the Licensed Work in all cases, except
|
|
41
|
-
as to any use that is explicitly granted in the Additional Use Grant above or
|
|
42
|
-
under the Change License after the Change Date.
|
|
43
|
-
|
|
44
|
-
If your use of the Licensed Work does not comply with the requirements of this
|
|
45
|
-
License, you must cease use of the Licensed Work immediately.
|
|
46
|
-
|
|
47
|
-
All copies of the Licensed Work, and all derivative works thereof, must include
|
|
48
|
-
this License.
|
|
49
|
-
|
|
50
|
-
This License does not grant you any right, title, or interest in any trademark,
|
|
51
|
-
logo, or branding of the Licensor, except as required to comply with this License.
|
|
52
|
-
|
|
53
|
-
TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON AN
|
|
54
|
-
“AS IS” BASIS. LICENSOR DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
|
|
55
|
-
WITHOUT LIMITATION WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
|
|
56
|
-
TITLE, AND NON-INFRINGEMENT. IN NO EVENT WILL LICENSOR BE LIABLE FOR ANY DAMAGES
|
|
57
|
-
ARISING OUT OF OR RELATED TO THIS LICENSE OR THE USE OF THE LICENSED WORK.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Marc Frost
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @cosmicdrift/kumiko-framework
|
|
2
2
|
|
|
3
|
-
[](../../LICENSE)
|
|
4
4
|
[](https://www.typescriptlang.org/)
|
|
5
5
|
|
|
6
6
|
Framework core for Kumiko — engine, pipeline, API, DB, event-store, and
|
|
@@ -131,4 +131,4 @@ sample under `samples/`; the roadmap lives in [docs/plans/uebersicht.md](../../d
|
|
|
131
131
|
|
|
132
132
|
## License
|
|
133
133
|
|
|
134
|
-
|
|
134
|
+
MIT, see [LICENSE](../../LICENSE).
|
package/agents/widgets.md
CHANGED
|
@@ -16,11 +16,11 @@ Value exports of `@cosmicdrift/kumiko-renderer/web`.
|
|
|
16
16
|
| `BareFormProvider` | |
|
|
17
17
|
| `BooleanField` | Checkbox/Switch = Field(layout:"inline") + Input(kind:"boolean"). |
|
|
18
18
|
| `Card` | |
|
|
19
|
-
| `CollapsibleSection` |
|
|
20
|
-
| `ComparisonTable` |
|
|
21
|
-
| `DateField` |
|
|
19
|
+
| `CollapsibleSection` | Collapsible section using native <details> with React-controlled state; otherwise the open attribute resets to its initial value on every parent re-render. |
|
|
20
|
+
| `ComparisonTable` | Transposed comparison table: row = metric, column = variant, with the best column optionally highlighted for each metric. |
|
|
21
|
+
| `DateField` | Date field = Field + Input(kind:"date"), ISO yyyy-mm-dd. |
|
|
22
22
|
| `DefaultAppShell` | |
|
|
23
|
-
| `DetailList` | Read-only
|
|
23
|
+
| `DetailList` | Read-only key-value list for detail views (dimmed label on the left, value on the right). |
|
|
24
24
|
| `DispatcherProvider` | |
|
|
25
25
|
| `Drawer` | Slide-in panel beside a list (e.g. mail reader next to the inbox) — thin wrapper over the Sheet primitive with header/body/footer slots so screens skip per-route Radix boilerplate. |
|
|
26
26
|
| `DropdownMenu` | |
|
|
@@ -32,10 +32,10 @@ Value exports of `@cosmicdrift/kumiko-renderer/web`.
|
|
|
32
32
|
| `DropdownMenuSeparator` | |
|
|
33
33
|
| `DropdownMenuTrigger` | |
|
|
34
34
|
| `EditorPanel` | |
|
|
35
|
-
| `EmptyState` |
|
|
36
|
-
| `ErrorState` |
|
|
37
|
-
| `FeedList` |
|
|
38
|
-
| `FileField` |
|
|
35
|
+
| `EmptyState` | Empty state with an optional icon and CTA, using the dashed-box style of entityList empty states as a standalone widget for custom screens. |
|
|
36
|
+
| `ErrorState` | Error state based on the Banner primitive, with a retry button. |
|
|
37
|
+
| `FeedList` | Non-tabular compact list (e.g. "upcoming appointments") — primary text plus an optional right-aligned trailing value for each row. |
|
|
38
|
+
| `FileField` | File upload = Field + Input(kind:"file"\|"image"), based on FileRef. |
|
|
39
39
|
| `FloatingPanel` | Non-modal panel floating above the page: draggable by its header, resizable from all edges and corners, clamped to the viewport, geometry optionally remembered. |
|
|
40
40
|
| `FormScreenShell` | |
|
|
41
41
|
| `HeaderActionGroup` | |
|
|
@@ -46,14 +46,14 @@ Value exports of `@cosmicdrift/kumiko-renderer/web`.
|
|
|
46
46
|
| `LanguageMenuItems` | Locale choices as dropdown entries, for a user menu that carries the shell controls. |
|
|
47
47
|
| `LanguageSwitcher` | |
|
|
48
48
|
| `LiveEventsProvider` | |
|
|
49
|
-
| `LoadingState` |
|
|
49
|
+
| `LoadingState` | Loading state with skeleton rows styled like cards; `rows` controls the height. |
|
|
50
50
|
| `LocaleProvider` | |
|
|
51
|
-
| `MiniStat` |
|
|
52
|
-
| `ModeSwitch` | Segmented
|
|
51
|
+
| `MiniStat` | Compact metric tile (without an icon) — for dense KPI grids. |
|
|
52
|
+
| `ModeSwitch` | Segmented control for mutually exclusive modes—the prominent alternative to the buried <select>. |
|
|
53
53
|
| `MoneyField` | |
|
|
54
54
|
| `NavProvider` | |
|
|
55
55
|
| `NavTree` | |
|
|
56
|
-
| `NumberField` |
|
|
56
|
+
| `NumberField` | Number field = Field + Input(kind:"number"); avoids repeating the id/name/value/onChange wiring at each screen. |
|
|
57
57
|
| `PageSection` | |
|
|
58
58
|
| `PdfPreview` | |
|
|
59
59
|
| `PercentField` | |
|
|
@@ -64,22 +64,22 @@ Value exports of `@cosmicdrift/kumiko-renderer/web`.
|
|
|
64
64
|
| `PrimitivesProvider` | |
|
|
65
65
|
| `ProfileMenu` | |
|
|
66
66
|
| `ProgressBar` | Progress bar, `value` 0..1 (clamped). |
|
|
67
|
-
| `ProgressList` |
|
|
67
|
+
| `ProgressList` | Label/value header and progress bar for each entry (for example, loan repayment progress). |
|
|
68
68
|
| `PublicShell` | One header and footer for anonymous pages; `focus` narrows to a task column, `card` centers a single card (sign-in). |
|
|
69
69
|
| `QueryTable` | |
|
|
70
|
-
| `RangeField` |
|
|
70
|
+
| `RangeField` | Slider = Field + Input(kind:"range"). |
|
|
71
71
|
| `RenderEdit` | |
|
|
72
72
|
| `RenderField` | |
|
|
73
73
|
| `RenderList` | |
|
|
74
74
|
| `ResizableHandle` | |
|
|
75
75
|
| `ResizablePanel` | |
|
|
76
76
|
| `ResizablePanelGroup` | |
|
|
77
|
-
| `ResultPanel` |
|
|
78
|
-
| `ResultTable` |
|
|
77
|
+
| `ResultPanel` | Result section for a calculator: SectionCard with an empty state (Banner) or a metric list (DetailList) plus optional extras (table, notes). |
|
|
78
|
+
| `ResultTable` | Static, prop-driven result table for calculated rows (tranches, scenarios). |
|
|
79
79
|
| `RichContentEditor` | |
|
|
80
80
|
| `ScreenWidthProvider` | |
|
|
81
|
-
| `SectionCard` |
|
|
82
|
-
| `SelectField` |
|
|
81
|
+
| `SectionCard` | Section with a title and optional action slot (range switcher, filter) — replaces repeated `<section className={CARD}>…<h2>` blocks in custom screens. |
|
|
82
|
+
| `SelectField` | Select field = Field + Input(kind:"select"). |
|
|
83
83
|
| `SideBySideTable` | Small read-only grid with real table semantics (`th scope`) whose cells are arbitrary React nodes — for side-by-side comparisons like "from the document" vs. |
|
|
84
84
|
| `Sidebar` | |
|
|
85
85
|
| `SidebarBrand` | |
|
|
@@ -91,15 +91,15 @@ Value exports of `@cosmicdrift/kumiko-renderer/web`.
|
|
|
91
91
|
| `SidebarUser` | |
|
|
92
92
|
| `Sparkline` | |
|
|
93
93
|
| `Stack` | |
|
|
94
|
-
| `StatCard` | Icon-
|
|
94
|
+
| `StatCard` | Icon-bearing metric tile with an optional delta chip, trend line, and sparkline. |
|
|
95
95
|
| `StatusBadge` | Pill badge with a status dot. |
|
|
96
96
|
| `StatusBarChart` | Status bar strip (e.g. 90-day uptime): variable-height bars with gradient fade + tick line on top; the last entry gets a "now" accent stripe. |
|
|
97
97
|
| `StepBar` | Wizard step overview — numbered chips with a connector line between them. |
|
|
98
|
-
| `TextField` |
|
|
99
|
-
| `TextareaField` |
|
|
98
|
+
| `TextField` | Text field = Field + Input(kind:"text"). |
|
|
99
|
+
| `TextareaField` | Multi-line text field = Field + Input(kind:"textarea"). |
|
|
100
100
|
| `ThemeMenuItem` | Theme toggle as a dropdown entry, for a user menu that carries the shell controls. |
|
|
101
101
|
| `ThemeToggle` | |
|
|
102
|
-
| `TimeseriesChart` |
|
|
102
|
+
| `TimeseriesChart` | Time-series line chart (smoothed line with area fill). |
|
|
103
103
|
| `ToastProvider` | |
|
|
104
104
|
| `TokensProvider` | |
|
|
105
105
|
| `Topbar` | |
|
|
@@ -112,16 +112,16 @@ Value exports of `@cosmicdrift/kumiko-renderer/web`.
|
|
|
112
112
|
|
|
113
113
|
| Name | Description |
|
|
114
114
|
| --- | --- |
|
|
115
|
-
| `useBrowserNavApi` | React
|
|
116
|
-
| `useBrowserTokensApi` |
|
|
115
|
+
| `useBrowserNavApi` | React hook that builds a NavApi from browser history. |
|
|
116
|
+
| `useBrowserTokensApi` | Builds a TokensApi for the browser. |
|
|
117
117
|
| `useDispatcher` | |
|
|
118
118
|
| `useDispatcherStatus` | |
|
|
119
|
-
| `useDraft` |
|
|
119
|
+
| `useDraft` | Form state for calculator screens: a draft object plus `patch` for partial updates, and `field(name)` that provides `{ id, name, value, onChange }` props ready for field widgets (NumberField/MoneyField/PercentField). |
|
|
120
120
|
| `useForm` | |
|
|
121
121
|
| `useIsNarrowViewport` | |
|
|
122
122
|
| `useIsViewportBelow` | |
|
|
123
|
-
| `useLiveEvents` | Hook
|
|
124
|
-
| `useLocale` |
|
|
123
|
+
| `useLiveEvents` | Hook for a useQuery-like consumer. |
|
|
124
|
+
| `useLocale` | Provides the current LocaleResolver and automatically subscribes to locale changes — the calling component re-renders when the language changes. |
|
|
125
125
|
| `useNav` | |
|
|
126
126
|
| `usePrimitives` | |
|
|
127
127
|
| `useQuery` | |
|
|
@@ -129,8 +129,8 @@ Value exports of `@cosmicdrift/kumiko-renderer/web`.
|
|
|
129
129
|
| `useStore` | |
|
|
130
130
|
| `useStoreSelector` | |
|
|
131
131
|
| `useToast` | |
|
|
132
|
-
| `useTokenController` |
|
|
133
|
-
| `useTokens` | Read-only
|
|
132
|
+
| `useTokenController` | Full API including theme toggling, for a dark/light switch button. |
|
|
133
|
+
| `useTokens` | Read-only tokens hook. |
|
|
134
134
|
| `useTranslation` | `t("key", params)` resolves in this order: 1. |
|
|
135
135
|
| `useUserRoles` | |
|
|
136
136
|
|
|
@@ -138,17 +138,17 @@ Value exports of `@cosmicdrift/kumiko-renderer/web`.
|
|
|
138
138
|
|
|
139
139
|
| Name | Description |
|
|
140
140
|
| --- | --- |
|
|
141
|
-
| `applyTokensToCssVars` |
|
|
141
|
+
| `applyTokensToCssVars` | Historically wrote tokens to :root as CSS variables. |
|
|
142
142
|
| `buildFileUploadFormData` | |
|
|
143
143
|
| `buildNavRegistrySlice` | |
|
|
144
144
|
| `buildNavRegistrySliceForApp` | |
|
|
145
|
-
| `cn` | shadcn
|
|
145
|
+
| `cn` | Standard shadcn helper: `clsx` combines conditional classes, while `tailwind-merge` resolves conflicts (for example, `px-2 px-4` → `px-4`). |
|
|
146
146
|
| `createBrowserDraftStorage` | Default DraftStorage when createKumikoApp boots without one. |
|
|
147
|
-
| `createBrowserLocaleResolver` | Default
|
|
148
|
-
| `createEventSourceLiveEvents` |
|
|
147
|
+
| `createBrowserLocaleResolver` | Default resolver when createKumikoApp is booted without a `locale` option. |
|
|
148
|
+
| `createEventSourceLiveEvents` | Returns an EventSource-backed `LiveEventSubscriber`. |
|
|
149
149
|
| `createKumikoApp` | |
|
|
150
150
|
| `createPublicSurface` | |
|
|
151
|
-
| `createStaticLocaleResolver` | Default
|
|
151
|
+
| `createStaticLocaleResolver` | Default resolver for apps without their own i18n layer. |
|
|
152
152
|
| `downloadFileRef` | |
|
|
153
153
|
| `fetchFileRefBlob` | |
|
|
154
154
|
| `fileNameFromContentDisposition` | |
|
|
@@ -157,16 +157,16 @@ Value exports of `@cosmicdrift/kumiko-renderer/web`.
|
|
|
157
157
|
| `formatMoney` | |
|
|
158
158
|
| `formatPath` | |
|
|
159
159
|
| `parsePath` | |
|
|
160
|
-
| `parseTargetFromSearchParams` |
|
|
160
|
+
| `parseTargetFromSearchParams` | Parses the current search parameters back into a TargetRef. |
|
|
161
161
|
| `postWithDownload` | |
|
|
162
162
|
| `qualifyScreenId` | |
|
|
163
163
|
| `readLoadedBuild` | |
|
|
164
164
|
| `reparentAppSchemaNavs` | |
|
|
165
165
|
| `resolveDefaultId` | |
|
|
166
166
|
| `saveBlobAsFile` | |
|
|
167
|
-
| `smoothPath` | Quadratic
|
|
168
|
-
| `synthesizeActionFormEntity` |
|
|
169
|
-
| `synthesizeActionFormScreen` |
|
|
167
|
+
| `smoothPath` | Quadratic midpoint technique: smooths the zigzag line without overshoot (~5 lines instead of a Catmull-Rom/Bezier fit). |
|
|
168
|
+
| `synthesizeActionFormEntity` | Builds a minimal EntityDefinition from the inline fields of an ActionForm or SecretMint screen. |
|
|
169
|
+
| `synthesizeActionFormScreen` | Converts an ActionFormScreenDefinition or SecretMintScreenDefinition to the EntityEditScreen shape expected by RenderEdit. |
|
|
170
170
|
| `uploadFileRef` | Posts one file to the stack's file endpoint (double-submit CSRF) and returns the new FileRef id. |
|
|
171
171
|
|
|
172
172
|
## Constants
|
|
@@ -177,7 +177,7 @@ Value exports of `@cosmicdrift/kumiko-renderer/web`.
|
|
|
177
177
|
| `STATUS_TONE_TEXT` | |
|
|
178
178
|
| `cssVarTokens` | Tokens with var-string values. |
|
|
179
179
|
| `defaultPrimitives` | |
|
|
180
|
-
| `defaultTokens` | Default
|
|
180
|
+
| `defaultTokens` | Default tokens—identical to `cssVarTokens` (variable-string references). |
|
|
181
181
|
| `lightTokens` | |
|
|
182
182
|
|
|
183
183
|
## Classes
|
|
@@ -137,8 +137,8 @@ export const TENANT_HEADER_NAME = "X-Tenant";
|
|
|
137
137
|
export const LOCALE_HEADER_NAME = "X-Locale";
|
|
138
138
|
// Internal-only signal set by buildServer's app.notFound() to mark "no
|
|
139
139
|
// route matched" 404s so tryHonoFirst (server-runtime) can tell them apart
|
|
140
|
-
// from a matched route's own deliberate 404 (e.g. default-deny reads)
|
|
141
|
-
//
|
|
140
|
+
// from a matched route's own deliberate 404 (e.g. default-deny reads).
|
|
141
|
+
// Never reaches a client: tryHonoFirst and every
|
|
142
142
|
// direct app.fetch() passthrough strip it before the response leaves the
|
|
143
143
|
// process, since its presence would otherwise let a caller distinguish
|
|
144
144
|
// "unregistered path" from "registered path, access denied" — a leak the
|
|
@@ -126,13 +126,13 @@ export function authMiddleware(jwt, options = {}) {
|
|
|
126
126
|
// No JWT → either fall through as anonymous (when the server opts in)
|
|
127
127
|
// or reject with 401 (preserving the pre-anonymous default).
|
|
128
128
|
//
|
|
129
|
-
//
|
|
130
|
-
// PUBLIC_API_PATHS
|
|
131
|
-
//
|
|
132
|
-
//
|
|
133
|
-
//
|
|
134
|
-
//
|
|
135
|
-
//
|
|
129
|
+
// Special case for auth routes: `/api/auth/*` paths that are NOT in
|
|
130
|
+
// PUBLIC_API_PATHS (tenants, switch-tenant, logout) require a JWT —
|
|
131
|
+
// but not tenant resolution. If they went through handleAnonymous,
|
|
132
|
+
// resolveTenant would throw 400 tenant_required when no tenant is
|
|
133
|
+
// declared. That would be the wrong diagnosis: the problem is missing
|
|
134
|
+
// authentication, not a missing tenant. Therefore, return 401 here
|
|
135
|
+
// without entering the anonymous-tenant flow.
|
|
136
136
|
if (anonymousAccess) {
|
|
137
137
|
if (isSessionRequiredApiPath(c.req.path)) {
|
|
138
138
|
return middlewareReject(c, {
|
|
@@ -93,10 +93,14 @@ export function createRedisLoginRateLimiter(redis, maxAttempts = 10, windowMs =
|
|
|
93
93
|
// shipped (PTTL returns -1 for "no expiry set, key exists") — without
|
|
94
94
|
// it, those pre-existing keys would grow forever with no way back to
|
|
95
95
|
// a normal window.
|
|
96
|
-
const count =
|
|
96
|
+
const count = await redis.eval(`local c = redis.call('INCR', KEYS[1])
|
|
97
97
|
if c == 1 or redis.call('PTTL', KEYS[1]) < 0 then redis.call('PEXPIRE', KEYS[1], ARGV[1]) end
|
|
98
|
-
return c`, 1, redisKey, windowMs)
|
|
99
|
-
|
|
98
|
+
return c`, 1, redisKey, windowMs);
|
|
99
|
+
const attempts = typeof count === "string" && /^\d+$/.test(count) ? Number(count) : count;
|
|
100
|
+
if (typeof attempts !== "number" || !Number.isInteger(attempts)) {
|
|
101
|
+
throw new TypeError(`redis INCR script returned ${String(count)}, expected an integer`);
|
|
102
|
+
}
|
|
103
|
+
return attempts <= maxAttempts;
|
|
100
104
|
},
|
|
101
105
|
async reset(key) {
|
|
102
106
|
await redis.del(`${prefix}${key}`);
|
|
@@ -130,7 +130,7 @@ export function registerMfaPreauthEnableStartRoute(ctx) {
|
|
|
130
130
|
// Second axis, independent of the IP-derived bucket above: the
|
|
131
131
|
// preauthSetupToken is minted by login.write.ts and not freely
|
|
132
132
|
// choosable, so this cap survives an attacker rotating
|
|
133
|
-
// x-forwarded-for to bypass the IP bucket
|
|
133
|
+
// x-forwarded-for to bypass the IP bucket.
|
|
134
134
|
const tokenAllowed = await rateLimiter.check(preauthTokenKeyOf(body.preauthSetupToken));
|
|
135
135
|
if (!tokenAllowed) {
|
|
136
136
|
return c.json({ isSuccess: false, error: "rate_limited" }, 429);
|
|
@@ -121,7 +121,7 @@ export function registerSessionRoutes(ctx) {
|
|
|
121
121
|
id: user.id,
|
|
122
122
|
tenantId: targetTenantId,
|
|
123
123
|
roles: sessionRoles,
|
|
124
|
-
// Tenant-independent prefs must survive the switch
|
|
124
|
+
// Tenant-independent prefs must survive the switch.
|
|
125
125
|
...tenantIndependentPrefs(user),
|
|
126
126
|
};
|
|
127
127
|
const claims = await dispatcher.resolveAuthClaims(targetSession);
|
|
@@ -2,8 +2,9 @@ import * as z from "zod";
|
|
|
2
2
|
import { createAnonymousUser } from "../engine/system-user.js";
|
|
3
3
|
import { SYSTEM_TENANT_ID } from "../engine/types/index.js";
|
|
4
4
|
import { NotFoundError } from "../errors/index.js";
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
import { createFallbackLogger } from "../logging/utils.js";
|
|
6
|
+
const log = createFallbackLogger("kumiko");
|
|
7
|
+
// Cross-device try-first handover: an optional tenant-handover grant the signup-request
|
|
7
8
|
// route forwards to the handler for server-side verification and binding —
|
|
8
9
|
// the grant itself never appears anywhere else (URL, mail, log, response).
|
|
9
10
|
export const SignupRequestBody = z.object({
|
|
@@ -68,7 +69,10 @@ export function registerTokenRequestRoute(opts) {
|
|
|
68
69
|
// rate_limited is an expected, attacker-triggerable outcome; logging it
|
|
69
70
|
// would flood the sink and bury real misconfiguration errors.
|
|
70
71
|
if (!result.isSuccess && result.error.code !== "rate_limited") {
|
|
71
|
-
|
|
72
|
+
log.error("token request handler failed", {
|
|
73
|
+
handler: opts.requestHandler,
|
|
74
|
+
code: result.error.code,
|
|
75
|
+
});
|
|
72
76
|
}
|
|
73
77
|
return c.json({ isSuccess: true });
|
|
74
78
|
});
|
|
@@ -63,7 +63,7 @@ export function registerSignupRoutes(ctx) {
|
|
|
63
63
|
// switcher), signup knows exactly ONE membership — the frontend UI uses
|
|
64
64
|
// it directly as the redirect target.
|
|
65
65
|
tenantKey: data.tenantKey,
|
|
66
|
-
// Cross-device try-first handover
|
|
66
|
+
// Cross-device try-first handover:
|
|
67
67
|
// present only when signup-confirm actually claimed a bound grant —
|
|
68
68
|
// absent, not null, so a client that doesn't know the field sees
|
|
69
69
|
// nothing unusual.
|
package/dist/api/client-ip.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { createFallbackLogger } from "../logging/utils.js";
|
|
2
|
+
const log = createFallbackLogger("kumiko");
|
|
1
3
|
// Single source for client-IP extraction, shared by every IP-keyed rate
|
|
2
4
|
// limit and audit field. Deriving the caller IP independently per call site
|
|
3
5
|
// makes it easy for one to silently trust the first XFF entry, which is
|
|
@@ -70,7 +72,7 @@ export function createClientIpResolver(trustedProxyHops, context = "resolveClien
|
|
|
70
72
|
if (trustedProxyHops === 0) {
|
|
71
73
|
if (xff !== undefined && xff.trim().length > 0 && !warnedUnexpectedForwardedFor) {
|
|
72
74
|
warnedUnexpectedForwardedFor = true;
|
|
73
|
-
|
|
75
|
+
log.warn("received X-Forwarded-For but trustedProxyHops is 0 (no proxy trusted) — " +
|
|
74
76
|
"ignoring it. Running behind a proxy? Set trustedProxyHops, otherwise all clients " +
|
|
75
77
|
"share one rate-limit bucket.");
|
|
76
78
|
}
|
package/dist/api/extra-route.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// ExtraRoute — declarative replacement for the old `extraRoutes: (app, deps)
|
|
2
|
-
// => void` closure
|
|
2
|
+
// => void` closure. Every entry declares its access
|
|
3
3
|
// tier (`entry`) up front; buildServer wires the matching guard + deps
|
|
4
4
|
// instead of handing every route a raw db/redis escape hatch.
|
|
5
5
|
export const ExtraRouteEntries = {
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { PII_CIPHERTEXT_PREFIX } from "../crypto/index.js";
|
|
2
|
+
import { createFallbackLogger } from "../logging/utils.js";
|
|
2
3
|
import { resolveRouteLabel } from "./route-label.js";
|
|
4
|
+
const log = createFallbackLogger("kumiko:pii-leak-guard");
|
|
3
5
|
const isProductionEnv = () => process.env["NODE_ENV"] === "production";
|
|
4
6
|
// Version-agnostic: catches both the current PII_CIPHERTEXT_PREFIX and any
|
|
5
7
|
// older/decrypt-only format version still present in unmigrated rows.
|
|
@@ -28,7 +30,7 @@ export function piiCiphertextResponseGuard() {
|
|
|
28
30
|
// skip: clean response — the common case
|
|
29
31
|
if (!CIPHERTEXT_RE.test(text))
|
|
30
32
|
return;
|
|
31
|
-
const detail = `
|
|
33
|
+
const detail = `JSON response for ${c.req.method} ${resolveRouteLabel(c)} contains a PII ciphertext ` +
|
|
32
34
|
`("${PII_CIPHERTEXT_PREFIX}…") — a raw DB read leaked to the API surface. ` +
|
|
33
35
|
`Decrypt before returning (decryptStoredPii / executor read path).`;
|
|
34
36
|
if (!isProductionEnv()) {
|
|
@@ -36,7 +38,7 @@ export function piiCiphertextResponseGuard() {
|
|
|
36
38
|
// skip: response replaced with the loud 500 above — nothing left to do
|
|
37
39
|
return;
|
|
38
40
|
}
|
|
39
|
-
|
|
41
|
+
log.error(detail);
|
|
40
42
|
const headers = new Headers(c.res.headers);
|
|
41
43
|
headers.delete("content-length");
|
|
42
44
|
c.res = new Response(text.replace(CIPHERTEXT_REDACT_RE, "[pii-redacted]"), {
|
package/dist/api/readiness.js
CHANGED
|
@@ -74,11 +74,11 @@ export function redisPingCheck(redis) {
|
|
|
74
74
|
},
|
|
75
75
|
};
|
|
76
76
|
}
|
|
77
|
-
// Lag
|
|
78
|
-
//
|
|
79
|
-
// (bigint),
|
|
80
|
-
//
|
|
81
|
-
//
|
|
77
|
+
// Lag check for the asynchronous event dispatcher. Reads the HWM and consumer cursor from
|
|
78
|
+
// the database, without relying on extra Redis or runtime state. The failure threshold is
|
|
79
|
+
// measured in event IDs (`bigint`), the natural unit because events have monotonically
|
|
80
|
+
// increasing IDs. For example, `maxLagEvents = 1_000` stops routing new traffic when the
|
|
81
|
+
// projection falls more than 1,000 events behind the HWM.
|
|
82
82
|
export function dispatcherLagCheck(db, consumerNames, maxLagEvents) {
|
|
83
83
|
return {
|
|
84
84
|
name: "dispatcher_lag",
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { isPlainObject } from "@cosmicdrift/kumiko-types/type-guards";
|
|
2
|
+
import { createFallbackLogger } from "../logging/utils.js";
|
|
2
3
|
import { redisChannelPrefixFromEnv, redisClientOptionsFromEnv, } from "../redis/client.js";
|
|
3
4
|
import { createRedisPubSubSignal } from "../redis/pubsub-signal.js";
|
|
4
5
|
import { createSseBroker, readNonEmptyStringArray, } from "./sse-broker.js";
|
|
5
|
-
|
|
6
|
+
const log = createFallbackLogger("kumiko:sse-broker");
|
|
7
|
+
// Channel namespace for cross-replica fanout. Every pod publishes
|
|
6
8
|
// here and every pod's psubscribe listens here, so a push on one instance
|
|
7
9
|
// reaches SSE clients connected to any other instance without a shared
|
|
8
10
|
// cursor — Pub/Sub for connected-client fanout, the event-store's shared
|
|
@@ -21,9 +23,9 @@ export function isRedisSseBroker(broker) {
|
|
|
21
23
|
// runProdApp must funnel through this rather than each deciding on their
|
|
22
24
|
// own — runProdApp doing exactly that (building an in-memory broker
|
|
23
25
|
// unconditionally, then passing it in as ServerOptions.sseBroker) is why
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
//
|
|
26
|
+
// the fanout fix shipped without ever taking effect in production: an
|
|
27
|
+
// explicit ServerOptions.sseBroker always wins in buildServer, so its own
|
|
28
|
+
// REDIS_URL-based default never ran.
|
|
27
29
|
export function createDefaultSseBroker(env = process.env) {
|
|
28
30
|
const redisUrl = env["REDIS_URL"];
|
|
29
31
|
const ownedRedisSseBroker = redisUrl
|
|
@@ -109,7 +111,7 @@ export function createRedisSseBroker(opts) {
|
|
|
109
111
|
signal.onMessage((channel, payload) => {
|
|
110
112
|
if (channel.startsWith(channelPrefix)) {
|
|
111
113
|
if (!isSseEvent(payload)) {
|
|
112
|
-
|
|
114
|
+
log.error(`dropping malformed event on "${channel}"`);
|
|
113
115
|
// skip: malformed payload already logged above, nothing to deliver
|
|
114
116
|
return;
|
|
115
117
|
}
|
|
@@ -132,7 +134,7 @@ export function createRedisSseBroker(opts) {
|
|
|
132
134
|
pushToChannel(channel, event) {
|
|
133
135
|
return signal.publishConfirmed(`${channelPrefix}${channel}`, event);
|
|
134
136
|
},
|
|
135
|
-
//
|
|
137
|
+
// This is the security-critical call — a revoked session's
|
|
136
138
|
// stream must close on every replica, not just the one that observed
|
|
137
139
|
// the revocation event. Publishing (rather than calling inner directly,
|
|
138
140
|
// like the in-memory broker does) is what makes that true here.
|
|
@@ -21,6 +21,7 @@ export type RequestContextData = {
|
|
|
21
21
|
readonly risk: AgentRisk;
|
|
22
22
|
};
|
|
23
23
|
readonly chargedRateLimitBuckets?: Set<string>;
|
|
24
|
+
readonly agentToolCall?: true;
|
|
24
25
|
};
|
|
25
26
|
export declare const requestContext: {
|
|
26
27
|
run<T>(data: RequestContextData, fn: () => T): T;
|
|
@@ -41,4 +42,5 @@ export declare function runWithOrigin<T>(origin: {
|
|
|
41
42
|
};
|
|
42
43
|
}, fn: () => T): T;
|
|
43
44
|
export declare function runAsDirectCallEntry<T>(fn: () => T): T;
|
|
45
|
+
export declare function isAgentToolCallScope(): boolean;
|
|
44
46
|
export declare function runWithWriteOrigin<T>(origin: WriteOrigin, fn: () => T): T;
|
|
@@ -48,15 +48,24 @@ export function runWithOrigin(origin, fn) {
|
|
|
48
48
|
: new Set(),
|
|
49
49
|
}, fn);
|
|
50
50
|
}
|
|
51
|
-
// Clears entryHandler so the next dispatch inside fn becomes the new entry
|
|
52
|
-
//
|
|
53
|
-
// turn/approve handler
|
|
51
|
+
// Clears entryHandler so the next dispatch inside fn becomes the new entry, letting
|
|
52
|
+
// the irreversible-operation gate see the invoked tool handler instead of the
|
|
53
|
+
// surrounding turn/approve handler. Reserved for dispatchToolCall, so it also marks
|
|
54
|
+
// the scope as an agent tool call; it always mints a scope, because an agent loop
|
|
55
|
+
// running outside any request must still carry that marker.
|
|
54
56
|
export function runAsDirectCallEntry(fn) {
|
|
55
57
|
const current = requestContext.get();
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
const requestId = current?.requestId ?? requestContext.generateId();
|
|
59
|
+
const { entryHandler: _entryHandler, ...rest } = current ?? {};
|
|
60
|
+
return requestContext.run({
|
|
61
|
+
...rest,
|
|
62
|
+
requestId,
|
|
63
|
+
correlationId: current?.correlationId ?? requestId,
|
|
64
|
+
agentToolCall: true,
|
|
65
|
+
}, fn);
|
|
66
|
+
}
|
|
67
|
+
export function isAgentToolCallScope() {
|
|
68
|
+
return requestContext.get()?.agentToolCall === true;
|
|
60
69
|
}
|
|
61
70
|
// An ungated origin never mints a scope: seeds and boot writes keep their missing context.
|
|
62
71
|
export function runWithWriteOrigin(origin, fn) {
|
package/dist/api/routes.js
CHANGED
|
@@ -129,7 +129,7 @@ export function createApiRoutes(dispatcher, options = {}) {
|
|
|
129
129
|
// dispatch gates (feature/rate-limit/access/validation) plus the handler's
|
|
130
130
|
// first yield — is raced against a heartbeat timeout BEFORE streamSSE, so
|
|
131
131
|
// a gate failure that settles in time maps to its real HTTP status via
|
|
132
|
-
// queryErrorResponse instead of a flushed-200 error frame
|
|
132
|
+
// queryErrorResponse instead of a flushed-200 error frame.
|
|
133
133
|
api.post(Routes.stream, async (c) => {
|
|
134
134
|
const user = getUser(c);
|
|
135
135
|
const body = await readDispatchBody(c);
|
|
@@ -149,7 +149,7 @@ export function createApiRoutes(dispatcher, options = {}) {
|
|
|
149
149
|
// Hook the raw request signal directly so a client disconnect during the
|
|
150
150
|
// pre-pull still reclaims the generator instead of leaking a Redis
|
|
151
151
|
// subscription/DB cursor held open inside its still-unresolved first
|
|
152
|
-
// `.next()
|
|
152
|
+
// `.next()`.
|
|
153
153
|
const signal = c.req.raw.signal;
|
|
154
154
|
const onPrePullAbort = () => void generator.return(undefined);
|
|
155
155
|
signal.addEventListener("abort", onPrePullAbort);
|