@cosmicdrift/kumiko-framework 0.397.0 → 0.399.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/agents/api.md ADDED
@@ -0,0 +1,20 @@
1
+ <!-- Generated by `bun run gen:agent-docs` from package.json exports of the listed entry points. Do not edit. -->
2
+
3
+ # API reference
4
+
5
+ Public signatures of the entry points an app imports, generated from their sources. Read these instead of `dist/*.d.ts`.
6
+
7
+ Format: one line per function or constant, object and options types as property lists (`name?: Type;`), and the first sentence of the doc comment after `//`. Bodies and `@internal` declarations are left out. A type that an entry point uses but does not export is listed in `api/types.md`.
8
+
9
+ | Import path | File | Exports |
10
+ | --- | --- | --- |
11
+ | `@cosmicdrift/kumiko-framework/engine` | `api/framework-engine.md` | 409 |
12
+ | `@cosmicdrift/kumiko-framework/errors` | `api/framework-errors.md` | 25 |
13
+ | `@cosmicdrift/kumiko-framework/ui-types` | `api/framework-ui-types.md` | 149 |
14
+ | `@cosmicdrift/kumiko-framework/db` | `api/framework-db.md` | 150 |
15
+ | `@cosmicdrift/kumiko-framework/event-store` | `api/framework-event-store.md` | 38 |
16
+ | `@cosmicdrift/kumiko-framework/seeding` | `api/framework-seeding.md` | 6 |
17
+ | `@cosmicdrift/kumiko-renderer/web` | `api/renderer-web.md` | 298 |
18
+ | `@cosmicdrift/kumiko-testing/stack` | `api/testing-stack.md` | 21 |
19
+ | `@cosmicdrift/kumiko-testing/helpers` | `api/testing-helpers.md` | 71 |
20
+ | `@cosmicdrift/kumiko-dev-server/testing` | `api/dev-server-testing.md` | 5 |
@@ -0,0 +1,176 @@
1
+ <!-- Generated by `bun run gen:agent-docs` from samples/apps/use-all-bundled/feature-manifest.json. Do not edit. -->
2
+
3
+ # Bundled features index
4
+
5
+ One entry per feature a package ships: purpose, import of its factory, required features, config keys without a default and secrets. Factory options: `api/` or the feature's types. Details: `features.md`.
6
+
7
+ - `account-security`: Self-service account dashboard: MFA enrollment, recovery codes and the user's own sessions, composed from the auth-mfa and sessions screens.
8
+ `import { createAccountSecurityFeature } from "@cosmicdrift/kumiko-bundled-features/account-security"`; requires `sessions`, `auth-mfa`
9
+ - `admin-shell`: Registers tenant-admin and platform-admin workspaces with provider nav into owner-feature screens (`tenant:screen:members`, `audit:screen:audit-log`, `audit:screen:audit-log-all-tenants`, `tenant:screen:tenant-list`, `jobs:screen:job-runs`, optional `tier-engine:screen:tier-admin`, optional `cap-overview:screen:my-caps`/`cap-overview:screen:tenant-cap-list`, optional `document-designer` template navs) and the two overview dashboards (the tenant one can be dropped with `includeTenantOverview: false`), built from the metrics list (`metrics` option, default DEFAULT_METRICS).
10
+ `import { createAdminShellFeature } from "@cosmicdrift/kumiko-bundled-features/admin-shell"`; requires `user`, `tenant`, `audit`, `jobs`, `metrics`, `metrics-system`, `tier-engine`, `cap-overview`, `document-designer`
11
+ - `agent-tools`: Builds a tool catalog and an agent manifest from the mounted registry so an LLM agent can call handlers and understand the app's shape.
12
+ `import { createAgentToolsFeature } from "@cosmicdrift/kumiko-bundled-features/agent-tools"`
13
+ - `audit`: Exposes the framework's event store as a paginated, filterable audit log via the `audit:query:list` handler (accessible to `Admin` and `SystemAdmin` roles).
14
+ `import { createAuditFeature } from "@cosmicdrift/kumiko-bundled-features/audit"`; requires `tenant`, `user`, `config`
15
+ - `auth-email-password`: Provides email+password authentication: the always-on handlers are `login`, `changePassword`, and `logout`; optional flows — password reset, email verification, magic-link self-signup, tenant invite, and account-unlock — are registered only when you pass their respective option objects (`passwordReset`, `emailVerification`, `signup`, `invite`, `accountUnlock`) to `createAuthEmailPasswordFeature(opts)`.
16
+ `import { createAuthEmailPasswordFeature } from "@cosmicdrift/kumiko-bundled-features/auth-email-password"`; requires `user`, `tenant`, `delivery`
17
+ - `auth-foundation`: Declares auth-middleware extension points: `tokenVerifier` (Bearer), `sessionStore` (revocable JWT sid), optional `tenantResolver` + `tenantExistence` for anonymous multi-tenant.
18
+ `import { createAuthFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/auth-foundation"`
19
+ - `auth-mfa`: TOTP-based two-factor authentication: enable/disable flow with QR-code setup, 8 single-use recovery codes, and (once wired into a login flow) a second login step after password verification.
20
+ `import { createAuthMfaFeature } from "@cosmicdrift/kumiko-bundled-features/auth-mfa"`; requires `user`, `config`, `tenant`
21
+ - `auth-mfa-user-data`: GDPR (Art.
22
+ `import { createAuthMfaUserDataFeature } from "@cosmicdrift/kumiko-bundled-features/auth-mfa-user-data"`; requires `user-data-rights`
23
+ - `auth-self-registration`: Runtime on/off switch for the auth-email-password self-signup flow.
24
+ `import { createAuthSelfRegistrationToggleFeature } from "@cosmicdrift/kumiko-bundled-features/auth-email-password"`
25
+ - `billing-foundation`: Plugin host for subscription billing.
26
+ `import { createBillingFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/billing-foundation"`; requires `tenant-lifecycle`, `compliance-profiles`
27
+ - `cap-counter`: Tracks per-tenant usage against configurable limits using two complementary storage models: calendar-period counters (one projection row per tenant/capName/period, reset implicitly by period rollover) and rolling-window counters (append-only event stream, no projection).
28
+ `import { createCapCounterFeature } from "@cosmicdrift/kumiko-bundled-features/cap-counter"`
29
+ - `cap-overview`: Read-only visibility into per-tenant tier assignment and cap usage.
30
+ `import { createCapOverviewFeature } from "@cosmicdrift/kumiko-bundled-features/cap-overview"`; requires `tenant`, `tier-engine`, `billing-foundation`
31
+ - `channel-discord`: Posts delivery notifications to a Discord webhook, registered as the `discord` channel in the delivery system.
32
+ `import { createChannelDiscordFeature } from "@cosmicdrift/kumiko-bundled-features/channel-discord"`; requires `delivery`, `secrets`
33
+ - `channel-email`: Wires an `EmailTransport` (typically `mail-transport-smtp` in production, `createInMemoryTransport()` in tests) into the delivery system as the `email` channel.
34
+ `import { createChannelEmailFeature } from "@cosmicdrift/kumiko-bundled-features/channel-email"`; requires `delivery`
35
+ - `channel-in-app`: Persists notifications to an in-app inbox table so users can page through them via `queries.inbox` (cursor, total, parsed `target`) and track unread state with `handlers.markRead` / `handlers.markAllRead` and `queries.unreadCount`.
36
+ `import { createChannelInAppFeature } from "@cosmicdrift/kumiko-bundled-features/channel-in-app"`; requires `delivery`
37
+ - `channel-push`: Delivers push notifications through a `PushTransport` (bring your own FCM/APNs adapter or use `createInMemoryPushTransport()` for tests) registered as the `push` channel in the delivery system.
38
+ `import { createChannelPushFeature } from "@cosmicdrift/kumiko-bundled-features/channel-push"`; requires `delivery`
39
+ - `channel-slack`: Posts delivery notifications to a Slack incoming webhook, registered as the `slack` channel in the delivery system.
40
+ `import { createChannelSlackFeature } from "@cosmicdrift/kumiko-bundled-features/channel-slack"`; requires `delivery`, `secrets`
41
+ - `channel-teams`: Posts delivery notifications to a Microsoft Teams webhook (Workflows or connector) as an Adaptive Card, registered as the `teams` channel in the delivery system.
42
+ `import { createChannelTeamsFeature } from "@cosmicdrift/kumiko-bundled-features/channel-teams"`; requires `delivery`, `secrets`
43
+ - `channel-telegram`: Sends delivery notifications through the Telegram Bot API (`sendMessage`, plain text), registered as the `telegram` channel in the delivery system.
44
+ `import { createChannelTelegramFeature } from "@cosmicdrift/kumiko-bundled-features/channel-telegram"`; requires `delivery`, `secrets`; secrets `channel-telegram:secret:bot-token`
45
+ - `compliance-profiles`: Lets each tenant select a compliance regime (e.g. `eu-dsgvo`, `swiss-dsg`, `de-hr-dsgvo-hgb`) that bundles user-rights grace periods, breach-disclosure deadlines, sub-processor requirements, and audit-retention rules into a single named profile.
46
+ `import { createComplianceProfilesFeature } from "@cosmicdrift/kumiko-bundled-features/compliance-profiles"`
47
+ - `compliance-profiles-ops`: Platform-wide SystemAdmin counterpart to `compliance-profiles`' `needs-profile` query (#2089): `tenants-missing-profile` lists every enabled tenant with no row in `tenantComplianceProfile` at all, tenant-wide instead of scoped to the caller's own tenant.
48
+ `import { createComplianceProfilesOpsFeature } from "@cosmicdrift/kumiko-bundled-features/compliance-profiles-ops"`; requires `compliance-profiles`, `tenant`
49
+ - `config`: Stores per-tenant (and optionally per-user) configuration values with a multi-layer cascade: user-row → tenant-row → system-row → app-override (deploy-time `AppConfigOverrides`) → computed → feature default.
50
+ `import { createConfigFeature } from "@cosmicdrift/kumiko-bundled-features/config"`
51
+ - `crypto-shredding`: Operator-level crypto-shredding trigger.
52
+ `import { createCryptoShreddingFeature } from "@cosmicdrift/kumiko-bundled-features/crypto-shredding"`
53
+ - `custom-fields`: Tenant- and system-scoped custom field definitions with generic value storage on any host entity.
54
+ `import { createCustomFieldsFeature } from "@cosmicdrift/kumiko-bundled-features/custom-fields"`
55
+ - `data-retention`: Resolves the effective retention policy for any entity using a 3-layer stack: entity-level default → tenant preset (`dsgvo-basic`, `dsgvo-hgb`, `swiss-dsg`) → per-tenant override stored in `tenantRetentionOverride`.
56
+ `import { createDataRetentionFeature } from "@cosmicdrift/kumiko-bundled-features/data-retention"`
57
+ - `delivery`: The notification dispatch core: call `ctx.notify(notificationType, { to, route, data, priority, idempotencyKey })` from any handler to fan out a notification across all registered channels (email, in-app, push).
58
+ `import { createDeliveryFeature } from "@cosmicdrift/kumiko-bundled-features/delivery"`; requires `tenant`
59
+ - `derivatives-sharp`: Registers an `image/*` renderer against the `derivativeRenderer` extension point declared by `file-derivatives`, backed by sharp.
60
+ `import { createDerivativesSharpFeature } from "@cosmicdrift/kumiko-bundled-features/derivatives-sharp"`; requires `file-derivatives`
61
+ - `document-designer`: Template overview and layout editor: lists the mail, PDF and layout templates of a tenant (platform admins see the system templates and how many tenants customised each) and edits the mail layout and the PDF letterhead in the browser.
62
+ `import { createDocumentDesignerFeature } from "@cosmicdrift/kumiko-document-designer"`; requires `template-resolver`
63
+ - `document-ingest-foundation`: Shared PDF/Scan/Image → normalized-text ingest primitive.
64
+ `import { createDocumentIngestFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/document-ingest-foundation"`; requires `config`, `tenant-lifecycle`
65
+ - `feature-toggles`: Persists per-feature enabled/disabled state in the `store_global_feature_state` table and exposes a `set` write-handler plus `list`/`registered` query-handlers so operators can flip features at runtime without redeploying.
66
+ `import { createFeatureTogglesFeature } from "@cosmicdrift/kumiko-bundled-features/feature-toggles"`
67
+ - `file-derivatives`: Declares the `derivativeRenderer` extension point.
68
+ `import { createFileDerivativesFeature } from "@cosmicdrift/kumiko-bundled-features/file-derivatives"`; requires `file-foundation`, `files`
69
+ - `file-foundation`: Defines the `fileProvider` extension point and a per-tenant `provider` config key that selects which registered storage plugin to use at runtime.
70
+ `import { createFileFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/file-foundation"`; requires `config`
71
+ - `file-provider-inmemory`: Registers an in-process `"inmemory"` provider for `file-foundation` that stores file bytes per tenant in a module-level Map.
72
+ `import { createFileProviderInMemoryFeature } from "@cosmicdrift/kumiko-bundled-features/file-provider-inmemory"`; requires `file-foundation`
73
+ - `file-provider-s3`: Registers itself as the `"s3"` provider for `file-foundation` and owns the per-tenant config keys (`bucket`, `region`, `endpoint`, `forcePathStyle`, `accessKeyId`) and the encrypted `s3.secretAccessKey` secret.
74
+ `import { createFileProviderS3Feature, createFileProviderS3NamedEnvFeature } from "@cosmicdrift/kumiko-bundled-features/file-provider-s3"`; requires `config`, `secrets`, `file-foundation`; secrets `file-provider-s3:secret:s3-secret-access-key`
75
+ - `file-provider-s3-env`: Registers an `"s3-env"` provider for `file-foundation` that reads one S3 credential set from `process.env` (`S3_BUCKET`/`S3_REGION`/`S3_ACCESS_KEY`/`S3_SECRET_KEY`, optional `S3_ENDPOINT`/`S3_FORCE_PATH_STYLE`) and serves every tenant from one shared bucket — no per-tenant config or secret seeding.
76
+ `import { createFileProviderS3Feature } from "@cosmicdrift/kumiko-bundled-features/file-provider-s3"`, call `createFileProviderS3Feature({ fromEnv: true })`; requires `file-foundation`
77
+ - `files`: Exposes the `fileRef` entity from the framework core so that uploaded files — tracked in the `file_refs` table by `createFileRoutes` — participate in cross-feature hooks: `user-data-rights-defaults` automatically includes file blobs in GDPR exports and forget flows, and tenant-lifecycle cleanup deletes all refs on tenant destroy.
78
+ `import { createFilesFeature } from "@cosmicdrift/kumiko-framework/files"`
79
+ - `files-tenant-data`: GDPR coverage for the `files` feature's `fileRef` entity: tenant-destroy row purge (EXT_TENANT_DATA) and full storage-prefix binary wipe (EXT_STORAGE_PROVIDER), plus a manual backfill/GC job that sweeps derivatives orphaned by a forget/tenant-destroy that ran before #2461 wired binary cleanup into those flows.
80
+ `import { createFilesTenantDataFeature } from "@cosmicdrift/kumiko-bundled-features/files-tenant-data"`; requires `tenant-lifecycle`
81
+ - `folders`: Generic, host-agnostic hierarchical folders for any entity.
82
+ `import { createFoldersFeature } from "@cosmicdrift/kumiko-bundled-features/folders"`
83
+ - `folders-user-data`: GDPR (Art.
84
+ `import { createFoldersUserDataFeature } from "@cosmicdrift/kumiko-bundled-features/folders-user-data"`; requires `user-data-rights`
85
+ - `form-draft`: Per-user, per-tenant working copy of an in-progress form, saved BEFORE the real domain entity exists.
86
+ `import { createFormDraftFeature } from "@cosmicdrift/kumiko-bundled-features/form-draft"`
87
+ - `form-draft-user-data`: GDPR (Art.
88
+ `import { createFormDraftUserDataFeature } from "@cosmicdrift/kumiko-bundled-features/form-draft-user-data"`; requires `user-data-rights`
89
+ - `host-routing`: Host- and path-based tenant routing: an authoritative tenant resolver from the subdomain (tenantBy host), a tenant existence check against enabled, active tenants, and the tenant-key query behind createHostDispatch, which picks the HTML file, CSP, locale redirect or 404 per host for runProdApp's hostDispatch.
90
+ `import { createHostRoutingFeature } from "@cosmicdrift/kumiko-bundled-features/host-routing"`; requires `auth-foundation`, `tenant`
91
+ - `inbound-mail-foundation`: Plugin host for inbound e-mail (inbox federation) — provider plugins (inbound-provider-imap, later M365 Graph / Gmail REST) register at the `inboundMailProvider` extension point with verify/fetch and optional oauth/watch (live push) and send.
92
+ `import { createInboundMailFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/inbound-mail-foundation"`; requires `tenant-lifecycle`
93
+ - `inbound-provider-imap`: Registers the `"imap"` provider for `inbound-mail-foundation` using imapflow — password/app-password auth covers classic IMAP hosts without any OAuth (plus an XOAUTH2 fallback consuming an access token from the secret slot).
94
+ `import { createInboundProviderImapFeature } from "@cosmicdrift/kumiko-bundled-features/inbound-provider-imap"`; requires `inbound-mail-foundation`, `secrets`
95
+ - `inbound-provider-inmemory`: Registers a scriptable in-process `"inmemory"` provider for `inbound-mail-foundation`.
96
+ `import { createInboundProviderInMemoryFeature } from "@cosmicdrift/kumiko-bundled-features/inbound-provider-inmemory"`; requires `inbound-mail-foundation`
97
+ - `jobs`: Persistence and operator tooling for background jobs registered via `r.job(...)`.
98
+ `import { createJobsFeature } from "@cosmicdrift/kumiko-bundled-features/jobs"`
99
+ - `ledger`: Double-entry bookkeeping primitive.
100
+ `import { createLedgerFeature } from "@cosmicdrift/kumiko-bundled-features/ledger"`
101
+ - `legal-pages`: Opt-in wrapper around `template-resolver` text-blocks that registers public HTML routes (default: `/legal/impressum`, `/legal/datenschutz`, `/legal/imprint`, `/legal/privacy`) with Markdown-to-HTML rendering and a boot-time job that hard-fails in production when the required blocks (default: `imprint/de`, `privacy/de`) are not seeded in `SYSTEM_TENANT`.
102
+ `import { createLegalPagesFeature } from "@cosmicdrift/kumiko-bundled-features/legal-pages"`; requires `template-resolver`
103
+ - `locale-de`: German UI and mail copy for framework screens.
104
+ `import { localeDe } from "@cosmicdrift/kumiko-locale-de"`
105
+ - `mail-foundation`: Defines the `mailTransport` extension point and a per-tenant `provider` config key that selects which registered transport plugin to use at runtime.
106
+ `import { createMailFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/mail-foundation"`; requires `config`
107
+ - `mail-transport-inmemory`: Registers an in-process `"inmemory"` provider for `mail-foundation` that buffers sent mails per tenant instead of contacting an SMTP server.
108
+ `import { createMailTransportInMemoryFeature } from "@cosmicdrift/kumiko-bundled-features/mail-transport-inmemory"`; requires `mail-foundation`
109
+ - `mail-transport-smtp`: Registers itself as the `"smtp"` provider for `mail-foundation` and owns the per-tenant config keys (`host`, `port`, `secure`, `from`, `authUser`) and the encrypted `smtp.password` secret.
110
+ `import { createMailTransportSmtpFeature } from "@cosmicdrift/kumiko-bundled-features/mail-transport-smtp"`; requires `config`, `secrets`, `mail-foundation`; secrets `mail-transport-smtp:secret:smtp-password`
111
+ - `managed-pages`: Tenant-editable, server-rendered public pages with per-tenant branding.
112
+ `import { createManagedPagesFeature } from "@cosmicdrift/kumiko-bundled-features/managed-pages"`; requires `config`
113
+ - `metrics`: Declarative tenant-scoped metrics: each metric declaration becomes an admin-only query that aggregates a read-model table for the caller's own tenant and returns the shape dashboard panels consume (stat value with period delta, time series, ranked rows).
114
+ `import { createMetricsFeature } from "@cosmicdrift/kumiko-bundled-features/metrics"`; requires `jobs`, `delivery`, `sessions`
115
+ - `metrics-system`: Declarative platform-wide metrics for SystemAdmin: each metric declaration becomes a query that aggregates a read-model table across all tenants (optionally narrowed to one tenant) and returns the shape dashboard panels consume.
116
+ `import { createSystemMetricsFeature } from "@cosmicdrift/kumiko-bundled-features/metrics"`; requires `jobs`, `delivery`, `sessions`, `tenant`
117
+ - `notes-history`: Generic, host-agnostic, append-only note history for any entity.
118
+ `import { createNotesHistoryFeature } from "@cosmicdrift/kumiko-bundled-features/notes-history"`
119
+ - `notes-history-user-data`: GDPR (Art.
120
+ `import { createNotesHistoryUserDataFeature } from "@cosmicdrift/kumiko-bundled-features/notes-history-user-data"`; requires `user-data-rights`
121
+ - `personal-access-tokens`: Long-lived, revocable Personal Access Tokens for headless HTTP-API access.
122
+ `import { createPersonalAccessTokensFeature } from "@cosmicdrift/kumiko-bundled-features/personal-access-tokens"`; requires `user`, `tenant`, `auth-foundation`
123
+ - `rate-limiting`: Adds an ops-side `rate-limiting:query:status` query handler for inspecting current bucket state; the actual request throttling is wired automatically by the dispatcher when any handler declares a `rateLimit` option (e.g. `{ per: 'user', limit: 3, windowSeconds: 60 }`) or when you pass `context.rateLimit` to `buildServer`.
124
+ `import { createRateLimitingFeature } from "@cosmicdrift/kumiko-bundled-features/rate-limiting"`
125
+ - `readiness`: One-call tenant-onboarding probe: `readiness:query:status` rolls up every config key and secret declared `required: true` across all mounted features and reports which still lack a usable value for the calling tenant, plus a single `ready` boolean.
126
+ `import { createReadinessFeature } from "@cosmicdrift/kumiko-bundled-features/readiness"`; requires `config`, `secrets`
127
+ - `renderer-foundation`: Plugin registry for content rendering (mail HTML, PDF, images): call `foundation.createRendererForTenant({ tenantId, kind })` at render time to get the right renderer plugin selected by kind, with tenant-level overrides via the `rendererPluginByKind` config key.
128
+ `import { createRendererFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/renderer-foundation"`; requires `template-resolver`
129
+ - `renderer-mail-html`: A `renderer-foundation` plugin that handles `RenderRequest { kind: "mail" }` and returns `{ subject, html, text, previewText?
130
+ `import { createRendererMailHtmlFeature } from "@cosmicdrift/kumiko-renderer-mail-html"`; requires `renderer-foundation`, `template-resolver`
131
+ - `renderer-pdf`: Renderer plugin for `kind="document-pdf"` that draws a table document (title, meta lines, weighted columns, repeating header, page footer) as a PDF in pure JavaScript, without a browser or Puppeteer service.
132
+ `import { createRendererPdfFeature } from "@cosmicdrift/kumiko-bundled-features/renderer-pdf"`; requires `renderer-foundation`
133
+ - `renderer-puppeteer-client`: A `renderer-foundation` plugin that prints documents to PDF through a separately deployed `puppeteer-service`.
134
+ `import { createRendererPuppeteerClientFeature } from "@cosmicdrift/kumiko-renderer-puppeteer-client"`; requires `renderer-foundation`
135
+ - `secrets`: Stores arbitrary per-tenant secrets (API keys, tokens, credentials) encrypted at rest using AES-256 with a KEK loaded from `KUMIKO_SECRETS_MASTER_KEY_V1` (and successive versions for rotation).
136
+ `import { createSecretsFeature } from "@cosmicdrift/kumiko-bundled-features/secrets"`
137
+ - `seo`: Site-discovery + SEO/AEO/GEO surface for apex/content pages.
138
+ `import { createSeoFeature } from "@cosmicdrift/kumiko-bundled-features/seo"`; requires `config`
139
+ - `sessions`: Tracks signed-in clients in the `store_user_sessions` table (one row per JWT, keyed by the `sid`/`jti` claim) and exposes handlers for `mine` (list your sessions), `revoke`, and `revokeAllOthers`.
140
+ `import { createSessionsFeature } from "@cosmicdrift/kumiko-bundled-features/sessions"`; requires `user`, `auth-foundation`
141
+ - `step-dispatcher`: Internal system feature that drains deferred Tier-2 side-effects (currently `webhook.send` and `mail.send`) after their originating transaction commits.
142
+ `import { createStepDispatcherFeature } from "@cosmicdrift/kumiko-bundled-features/step-dispatcher"`; requires `secrets`
143
+ - `subscription-mollie`: Mollie payment provider plugin for `billing-foundation`, covering the DACH/EU mid-market use case.
144
+ `import { createSubscriptionMollieFeature } from "@cosmicdrift/kumiko-bundled-features/subscription-mollie"`; requires `billing-foundation`
145
+ - `subscription-paddle`: Paddle Billing provider plugin for `billing-foundation`.
146
+ `import { createSubscriptionPaddleFeature } from "@cosmicdrift/kumiko-bundled-features/subscription-paddle"`; requires `billing-foundation`, `config`, `secrets`; config without default `api-key`, `client-token`, `tenant-binding-key`, `webhook-secret`
147
+ - `subscription-stripe`: Stripe payment provider plugin for `billing-foundation`.
148
+ `import { createSubscriptionStripeFeature } from "@cosmicdrift/kumiko-bundled-features/subscription-stripe"`; requires `billing-foundation`, `config`, `secrets`; config without default `api-key`, `webhook-secret`
149
+ - `tags`: Generic, host-agnostic tagging for any entity.
150
+ `import { createTagsFeature } from "@cosmicdrift/kumiko-bundled-features/tags"`
151
+ - `template-resolver`: Every piece of editable text lives here, in one entity: mail bodies, notification texts, PDF document templates, AI prompts and plain text blocks.
152
+ `import { createTemplateResolverFeature } from "@cosmicdrift/kumiko-bundled-features/template-resolver"`
153
+ - `template-resolver-user-data`: GDPR (Art.
154
+ `import { createTemplateResolverUserDataFeature } from "@cosmicdrift/kumiko-bundled-features/template-resolver-user-data"`; requires `user-data-rights`
155
+ - `tenant`: Registers the three core multi-tenancy entities — `tenant`, `tenant-membership`, and `tenant-invitation` (DB tables `read_tenants`, `read_tenant_memberships`, and `read_tenant_invitations`) — along with write handlers for create/update/disable/enable/addMember/removeMember/updateMemberRoles and the matching queries.
156
+ `import { createTenantFeature } from "@cosmicdrift/kumiko-bundled-features/tenant"`; requires `config`; config without default `smtp-host`, `smtp-pass`
157
+ - `tenant-handover`: Try-before-signup ownership handover: claims the rows of a declared-transferable entity graph (root plus its parentRef-linked children) from an anonymous/public tenant into the caller's own tenant, legitimized by a row-bound grant the anonymous flow minted.
158
+ `import { createTenantHandoverFeature } from "@cosmicdrift/kumiko-bundled-features/tenant-handover"`
159
+ - `tenant-lifecycle`: Tenant-destroy lifecycle: request/cancel destruction with compliance-profile grace, auth 410 gate for teardown states, cron trigger after grace, and staged destroy runner (extension fan-out, subject-key erase, tenant tombstone).
160
+ `import { createTenantLifecycleFeature } from "@cosmicdrift/kumiko-bundled-features/tenant-lifecycle"`; requires `tenant`, `compliance-profiles`
161
+ - `tenant-settings`: Per-tenant Currency + Locale defaults, exposed as two config keys (tenant-settings:config:currency, tenant-settings:config:locale) that surface in the self-populating Settings-Hub via `mask`.
162
+ `import { createTenantSettingsFeature } from "@cosmicdrift/kumiko-bundled-features/tenant-settings"`; requires `config`
163
+ - `tier-engine`: Stores a `tier-assignment` entity per tenant (which pricing tier is active) and, when configured with a `TierMap`, registers itself as the `tenantTierResolver` extension so the dispatcher automatically gates `r.toggleable()` features per tenant based on their assigned tier.
164
+ `import { createTierEngineFeature } from "@cosmicdrift/kumiko-bundled-features/tier-engine"`; requires `config`, `tenant`
165
+ - `user`: Manages the cross-tenant user identity: the `read_users` table holds each user's email, `displayName`, global `roles`, `emailVerified` flag, and lifecycle `status` (active / restricted / deletionRequested / deleted).
166
+ `import { createUserFeature } from "@cosmicdrift/kumiko-bundled-features/user"`
167
+ - `user-data-rights`: Implements GDPR Art.
168
+ `import { createUserDataRightsFeature } from "@cosmicdrift/kumiko-bundled-features/user-data-rights"`; requires `user`, `data-retention`, `compliance-profiles`, `sessions`
169
+ - `user-data-rights-defaults`: Registers ready-made `EXT_USER_DATA` export and delete hooks for the bundled entities that hold per-user data: `user` (delete strategy sets email to `deleted-<id>@anonymized.invalid`, nulls `passwordHash`, sets status to `Deleted`; anonymize strategy sets email to `anonymized-<id>@anonymized.invalid` without touching `passwordHash`), `fileRef` (delete removes both the DB row and the storage binary), plus — gated on the source feature being mounted — `user-session` (ip/userAgent, hard-delete), `api-token` (hard-delete = revoke), `in-app-message` (hard-delete), `tenant-invitation` (invitee email forgotten/pseudonymized, inviter link severed), `notification-preference` and user-scoped `config-value` (purged via the forget verb), plus export-only hooks for the events-only aggregates `delivery-attempt` (recipientAddress) and `job-run` (payload) whose erasure runs via crypto-shredding, and `download-attempt` (rejected export-download attempts of the user: result, path, source IP, user agent, time; delete purges the user's own attempts via the forget verb, token-path rows without an owner expire by retention).
170
+ `import { createUserDataRightsDefaultsFeature } from "@cosmicdrift/kumiko-bundled-features/user-data-rights-defaults"`; requires `user`, `files`, `user-data-rights`
171
+ - `user-profile`: Self-service account page (fw#2312: declarative `projectionDetail` bound to the signed-in user's own `me` row) plus its `change-email` write handler (re-auth via current password, uniqueness check, resets emailVerified and expects the app to trigger the verification flow; opt out with `allowEmailChange: false`, which drops handler and section).
172
+ `import { createUserProfileFeature } from "@cosmicdrift/kumiko-bundled-features/user-profile"`; requires `user`, `auth-email-password`, `user-data-rights`, `user-data-rights-defaults`
173
+ - `waitlist`: Public waitlist: an anonymous, rate-limited `submit` handler collects name, email and optional company, portfolio and message (a honeypot field and per-email dedupe keep bots and repeat submits out, and the response body is identical whether or not an address was already listed), and mails the submitter a confirmation.
174
+ `import { createWaitlistFeature } from "@cosmicdrift/kumiko-bundled-features/waitlist"`; requires `tenant`, `auth-email-password`, `user-data-rights`
175
+ - `workflow-runner`: Writes the run-envelope (`workflow.run-started` / `workflow.run-completed` / `workflow.run-failed`) for a workflow run onto its `workflow-run` aggregate stream.
176
+ `import { createWorkflowRunnerFeature } from "@cosmicdrift/kumiko-bundled-features/workflow-runner"`
@@ -124,7 +124,7 @@ Requires: `delivery`
124
124
 
125
125
  ## channel-in-app
126
126
 
127
- Persists notifications to an in-app inbox table so users can retrieve them via `handlers.inbox` and track unread state with `handlers.markRead` / `handlers.markAllRead` and `queries.unreadCount`. Requires `delivery`; no external service needed — messages are stored in the app's own database.
127
+ Persists notifications to an in-app inbox table so users can page through them via `queries.inbox` (cursor, total, parsed `target`) and track unread state with `handlers.markRead` / `handlers.markAllRead` and `queries.unreadCount`. A notification links to a screen when `data.target` is set with `inAppTarget(screenId, entityId?)`. The renderer shell shows a bell with a live unread badge whenever this feature is mounted. Requires `delivery`; no external service needed — messages are stored in the app's own database.
128
128
 
129
129
  Category: notifications
130
130
 
@@ -1,8 +1,11 @@
1
+ import type { AggregateKey } from "@cosmicdrift/kumiko-types/aggregate-types";
1
2
  import type { ListRuntimeOptions } from "@cosmicdrift/kumiko-types/event-store-executor-types";
2
3
  import type { EagerLoadEntityResolver, EagerLoadViewer } from "../db/eagerload.ts";
4
+ import type { TenantDb } from "../db/tenant-db.ts";
3
5
  import type { HandlerContext, SessionUser } from "./types/index.ts";
4
6
  export declare function entityListRuntime(ctx: HandlerContext, entityName: string, user: SessionUser): {
5
7
  readonly runtimeOptions: ListRuntimeOptions;
6
8
  readonly eagerLoadViewer: EagerLoadViewer;
7
9
  readonly resolveEntity: EagerLoadEntityResolver;
8
10
  };
11
+ export declare function referenceGroupLabels(ctx: HandlerContext, user: SessionUser, sourceTableName: string, groupByField: string, keys: readonly AggregateKey[], db: TenantDb): Promise<ReadonlyMap<string, string>>;
@@ -1,3 +1,5 @@
1
+ import { resolveTableName } from "../db/entity-table-meta.js";
2
+ import { labelListAggregateGroups } from "../db/list-aggregate-labels.js";
1
3
  // Registry access only exists at request time, so the searchable/sortable-reference
2
4
  // caches, the entity resolver and the join-row read gate travel through the
3
5
  // runtime-override extension point of the executor verbs.
@@ -18,3 +20,19 @@ export function entityListRuntime(ctx, entityName, user) {
18
20
  resolveEntity,
19
21
  };
20
22
  }
23
+ // Resolved through the viewer's read access, like list-aggregate labels; non-entity sources yield none.
24
+ export async function referenceGroupLabels(ctx, user, sourceTableName, groupByField, keys, db) {
25
+ const labels = new Map();
26
+ const owner = [...ctx.registry.getAllEntities()].find(([entityName, entity]) => resolveTableName(entityName, entity, undefined) === sourceTableName);
27
+ if (owner === undefined || keys.length === 0)
28
+ return labels;
29
+ const [entityName, entity] = owner;
30
+ const { eagerLoadViewer, resolveEntity } = entityListRuntime(ctx, entityName, user);
31
+ const labeled = await labelListAggregateGroups({ groups: keys.map((key) => ({ key, count: 0, measures: [] })) }, groupByField, { entity, resolveEntity, viewer: eagerLoadViewer }, db);
32
+ for (const group of labeled.groups) {
33
+ if (group.key !== null && group.label != null && group.label !== "") {
34
+ labels.set(String(group.key), group.label);
35
+ }
36
+ }
37
+ return labels;
38
+ }
@@ -45,6 +45,7 @@ export { computeDefinitionFingerprint, defineWorkflow } from "./define-workflow.
45
45
  export type { ToggleReader } from "./effective-features.ts";
46
46
  export { computeEffectiveFeatures, isToggleableFeature } from "./effective-features.ts";
47
47
  export { createEntityExecutor, defineEntityCreateHandler, defineEntityDeleteHandler, defineEntityDetailHandler, defineEntityListHandler, defineEntityQueryHandler, defineEntityRestoreHandler, defineEntityUpdateHandler, defineEntityWriteHandler, defineProjectionQueryHandler, entityListSchema, MAX_LIST_LIMIT, } from "./entity-handlers.ts";
48
+ export { referenceGroupLabels } from "./entity-list-runtime-options.ts";
48
49
  export { declareEscapeHatch } from "./escape-hatch-declaration.ts";
49
50
  export { CODEMOD_PLACEHOLDER_REASON_MARKER } from "./escape-hatch-reason.ts";
50
51
  export { emitEvent, typedPayload } from "./event-helpers.ts";
@@ -27,6 +27,7 @@ export { createEntityExecutor, defineEntityCreateHandler, defineEntityDeleteHand
27
27
  // apps use the verb-specific wrappers above. Existing callers (integration
28
28
  // tests, old bundled-features) remain runnable without changes.
29
29
  defineEntityQueryHandler, defineEntityRestoreHandler, defineEntityUpdateHandler, defineEntityWriteHandler, defineProjectionQueryHandler, entityListSchema, MAX_LIST_LIMIT, } from "./entity-handlers.js";
30
+ export { referenceGroupLabels } from "./entity-list-runtime-options.js";
30
31
  export { declareEscapeHatch } from "./escape-hatch-declaration.js";
31
32
  export { CODEMOD_PLACEHOLDER_REASON_MARKER } from "./escape-hatch-reason.js";
32
33
  export { emitEvent, typedPayload } from "./event-helpers.js";
@@ -150,6 +150,7 @@ function buildWorkerServer(opts, lifecycle, jobRunner) {
150
150
  lifecycle,
151
151
  processLane: "worker",
152
152
  instanceId: opts.instanceId,
153
+ sseBroker: opts.sseBroker,
153
154
  observability: opts.observability,
154
155
  dispatcherOptions,
155
156
  systemHooks: opts.systemHooks,
@@ -1,5 +1,6 @@
1
1
  import type { JwtKeyring } from "../api/jwt.ts";
2
2
  import type { ServerOptions } from "../api/server.ts";
3
+ import type { SseBroker } from "../api/sse-broker.ts";
3
4
  import type { RowLevelSecurityMode } from "../db/render-rls-ddl.ts";
4
5
  import type { AppContext, Registry } from "../engine/types/index.ts";
5
6
  import type { JobRunnerOptions } from "../jobs/job-runner.ts";
@@ -36,5 +37,6 @@ export type JobsBlock = {
36
37
  };
37
38
  export type WorkerEntrypointOptions = BaseEntrypointOptions & JobsBlock & {
38
39
  readonly eventDispatcher?: ServerOptions["eventDispatcher"];
40
+ readonly sseBroker?: SseBroker;
39
41
  };
40
42
  export type AllInOneEntrypointOptions = ApiEntrypointOptions & WorkerEntrypointOptions;
@@ -24,6 +24,7 @@ import { collectRecordOnlyFileEntityTypes } from "./record-only-file-entity-type
24
24
  import { isStoredRecordLocked, RecordWriteDecisions, } from "./record-write-check.js";
25
25
  import { neutralFileNameForMimeType } from "./resolve-content-type.js";
26
26
  import { parseMaxSize, resolveServedContentType } from "./types.js";
27
+ import { isAnonymousPendingUploadAllowed, registryHasAnonymousUploadField, } from "./upload-field-resolution.js";
27
28
  import { withUploadLedger } from "./upload-ledger.js";
28
29
  // Exactly the httpStatus values declared by KumikoError subclasses across packages/*.
29
30
  const ERROR_STATUSES = [400, 401, 403, 404, 409, 422, 429, 500, 503, 504];
@@ -148,21 +149,32 @@ export function createFileRoutes(options) {
148
149
  // POST /files — multipart upload.
149
150
  api.post("/files", uploadBodyLimit, async (c) => {
150
151
  const user = getUser(c);
151
- // Anonymous visitors share one user id and cannot bind pending uploads (bind-pending-file-refs),
152
- // so an anonymous upload could never be used; 401 lets the client tell an ended session apart.
153
- if (isAnonymousSessionUser(user))
152
+ const isAnonymous = isAnonymousSessionUser(user);
153
+ // 401 (not 403) lets the client tell an ended session apart. Without any opted-in field
154
+ // no body is worth reading.
155
+ if (isAnonymous && !registryHasAnonymousUploadField(options.registry)) {
154
156
  return unauthenticatedUploadResponse(c);
157
+ }
155
158
  const body = await c.req.parseBody();
156
159
  const file = body["file"];
157
- if (!file || !(file instanceof File)) {
158
- return c.json({ error: "missing_file: expected multipart field 'file'" }, 400);
159
- }
160
160
  const entityType = typeof body["entityType"] === "string" ? body["entityType"] : undefined;
161
161
  // Post-ES migration entities use UUID ids; we accept the raw string and
162
162
  // store it in the text entityId column.
163
163
  const entityId = typeof body["entityId"] === "string" ? body["entityId"] : undefined;
164
164
  const fieldName = typeof body["fieldName"] === "string" ? body["fieldName"] : undefined;
165
165
  const actionForm = typeof body["actionForm"] === "string" ? body["actionForm"] : undefined;
166
+ if (isAnonymous &&
167
+ !isAnonymousPendingUploadAllowed(options.registry, {
168
+ entityType,
169
+ entityId,
170
+ fieldName,
171
+ actionForm,
172
+ })) {
173
+ return unauthenticatedUploadResponse(c);
174
+ }
175
+ if (!file || !(file instanceof File)) {
176
+ return c.json({ error: "missing_file: expected multipart field 'file'" }, 400);
177
+ }
166
178
  // Ahead of the record check below, which would answer 404/403 for the entity half.
167
179
  if (actionForm !== undefined && (entityType !== undefined || entityId !== undefined)) {
168
180
  return c.json({ error: "invalid_action_form: actionForm cannot be combined with entityType or entityId" }, 400);
@@ -21,4 +21,6 @@ type UploadFieldResolution = AttachedFieldResolution | {
21
21
  readonly error: string;
22
22
  };
23
23
  export declare function resolveUploadField(registry: Registry | undefined, input: UploadTarget, attachTargets: AttachTargets | undefined): UploadFieldResolution;
24
+ export declare function registryHasAnonymousUploadField(registry: Registry | undefined): boolean;
25
+ export declare function isAnonymousPendingUploadAllowed(registry: Registry | undefined, target: UploadTarget): boolean;
24
26
  export {};
@@ -49,3 +49,28 @@ export function resolveUploadField(registry, input, attachTargets) {
49
49
  }
50
50
  return { kind: "resolved", fieldDef };
51
51
  }
52
+ const anonymousUploadRegistries = new WeakMap();
53
+ export function registryHasAnonymousUploadField(registry) {
54
+ if (registry === undefined)
55
+ return false;
56
+ const cached = anonymousUploadRegistries.get(registry);
57
+ if (cached !== undefined)
58
+ return cached;
59
+ let found = false;
60
+ for (const [, entity] of registry.getAllEntities()) {
61
+ for (const fieldDef of Object.values(entity.fields)) {
62
+ if (isFileField(fieldDef) && fieldDef.anonymousUpload === true)
63
+ found = true;
64
+ }
65
+ }
66
+ anonymousUploadRegistries.set(registry, found);
67
+ return found;
68
+ }
69
+ // Anonymous visitors share one user id and cannot bind, so only an opted-in field's
70
+ // pending upload (no record, no action form) is allowed.
71
+ export function isAnonymousPendingUploadAllowed(registry, target) {
72
+ if (target.entityId !== undefined || target.actionForm !== undefined)
73
+ return false;
74
+ const resolution = resolveAttachedField(registry, target.entityType, target.fieldName);
75
+ return resolution.kind === "resolved" && resolution.fieldDef.anonymousUpload === true;
76
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosmicdrift/kumiko-framework",
3
- "version": "0.397.0",
3
+ "version": "0.399.0",
4
4
  "description": "Framework core — engine, pipeline, API, DB, and every other bit that makes Kumiko go.",
5
5
  "license": "MIT",
6
6
  "author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
@@ -203,12 +203,12 @@
203
203
  "./package.json": "./package.json"
204
204
  },
205
205
  "dependencies": {
206
- "@cosmicdrift/kumiko-types": "0.397.0",
206
+ "@cosmicdrift/kumiko-types": "0.399.0",
207
207
  "bullmq": "^5.76.7",
208
- "hono": "^4.13.1",
208
+ "hono": "^4.13.14",
209
209
  "i18next": "^26.1.0",
210
210
  "ioredis": "^5.10.1",
211
- "jose": "^6.2.3",
211
+ "jose": "^6.2.13",
212
212
  "pino": "^10.3.1",
213
213
  "temporal-polyfill": "^0.3.2",
214
214
  "uuid": "^14.0.0",
@@ -225,7 +225,7 @@
225
225
  }
226
226
  },
227
227
  "devDependencies": {
228
- "@cosmicdrift/kumiko-testing": "0.397.0",
228
+ "@cosmicdrift/kumiko-testing": "0.399.0",
229
229
  "bun-types": "^1.4.4",
230
230
  "fflate": "^0.8.3",
231
231
  "meilisearch": "^0.58.0",
package/src/changes.json CHANGED
@@ -1,4 +1,22 @@
1
1
  [
2
+ {
3
+ "version": "0.399.0",
4
+ "type": "improvement",
5
+ "title": "Agent docs ship an API reference and a compact feature index",
6
+ "detail": "`packages/framework/agents/` now contains `api.md` with one file per app-facing entry point (engine, errors, ui-types, db, event-store, seeding, renderer/web, testing/stack, testing/helpers, dev-server/testing) plus `api/types.md` for the types they reference. Signatures are one line each, option types are property lists with the first doc sentence, and `@internal` declarations are left out. `features-index.md` lists every bundled feature with one sentence, the import of its factory, its required features, config keys without a default and secrets. `AGENTS.md` points agents at the index first and at `api.md` instead of `dist/*.d.ts`. Both files are generated by `bun run gen:agent-docs` and checked by the agent-docs-drift guard."
7
+ },
8
+ {
9
+ "version": "0.399.0",
10
+ "type": "improvement",
11
+ "title": "Opt-in anonymous uploads per file field",
12
+ "detail": "Since the upload route rejects anonymous callers, `POST /api/files` answered 401 for every anonymous visitor. A file, image, files or images field with `anonymousUpload: true` now accepts a pending upload from an anonymous visitor (entityType and fieldName, no entityId, no actionForm); the app binds the file itself. Everything else stays 401 for anonymous callers."
13
+ },
14
+ {
15
+ "version": "0.398.0",
16
+ "type": "fix",
17
+ "title": "jose updated to 6.2.13",
18
+ "detail": "Dependency bump of jose in framework and bundled-features. No API change for consumers."
19
+ },
2
20
  {
3
21
  "version": "0.397.0",
4
22
  "type": "improvement",