create-kumiko-app 0.361.0 → 0.363.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/feature-manifest.json +123 -22
- package/package.json +5 -5
- package/src/changes.json +8 -0
- package/src/feature-constructors.ts +0 -6
package/feature-manifest.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"source": "samples/apps/use-all-bundled APP_FEATURES (composeFeatures includeBundled + signup)",
|
|
3
|
-
"featureCount":
|
|
3
|
+
"featureCount": 84,
|
|
4
4
|
"features": [
|
|
5
5
|
{
|
|
6
6
|
"name": "admin-shell",
|
|
7
|
-
"description": "Registers tenant-admin and platform-admin workspaces with provider nav into owner-feature screens (`tenant:screen:members`, `audit:screen:audit-log`, `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`) and the two overview dashboards, built from the metrics list (`metrics` option, default DEFAULT_METRICS). Mount after user, tenant, audit, jobs, metrics and metrics-system; pass `workspaceIds` to match app URL conventions (e.g. Studio `d`/`s`, PublicStatus `admin`/`sysadmin`). Client: `adminShellClient()`, `tenantClient()`, `auditClient()`, `jobsClient()`, optional `tierEngineClient()`.",
|
|
7
|
+
"description": "Registers tenant-admin and platform-admin workspaces with provider nav into owner-feature screens (`tenant:screen:members`, `audit:screen:audit-log`, `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, built from the metrics list (`metrics` option, default DEFAULT_METRICS). Mount after user, tenant, audit, jobs, metrics and metrics-system; pass `workspaceIds` to match app URL conventions (e.g. Studio `d`/`s`, PublicStatus `admin`/`sysadmin`). Client: `adminShellClient()`, `tenantClient()`, `auditClient()`, `jobsClient()`, optional `tierEngineClient()`.",
|
|
8
8
|
"toggleableDefault": null,
|
|
9
9
|
"requires": [
|
|
10
10
|
"user",
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
"metrics",
|
|
15
15
|
"metrics-system",
|
|
16
16
|
"tier-engine",
|
|
17
|
-
"cap-overview"
|
|
17
|
+
"cap-overview",
|
|
18
|
+
"document-designer"
|
|
18
19
|
],
|
|
19
20
|
"optionalRequires": [],
|
|
20
21
|
"configReads": [],
|
|
@@ -406,7 +407,7 @@
|
|
|
406
407
|
},
|
|
407
408
|
{
|
|
408
409
|
"name": "channel-email",
|
|
409
|
-
"description": "Wires an `EmailTransport` (typically `mail-transport-smtp` in production, `createInMemoryTransport()` in tests) into the delivery system as the `email` channel. Requires `delivery`; pass an `EmailChannelOptions` with a `transport
|
|
410
|
+
"description": "Wires an `EmailTransport` (typically `mail-transport-smtp` in production, `createInMemoryTransport()` in tests) into the delivery system as the `email` channel. Requires `delivery`; pass an `EmailChannelOptions` with a `transport` and a `resolveEmail` function that maps a user ID to their email address. Mails render from stored templates (`renderer-mail-html`): a notification routed to this channel needs `template` + `meta` on `r.notification`, otherwise its delivery fails.",
|
|
410
411
|
"toggleableDefault": null,
|
|
411
412
|
"requires": [
|
|
412
413
|
"delivery"
|
|
@@ -724,7 +725,7 @@
|
|
|
724
725
|
},
|
|
725
726
|
{
|
|
726
727
|
"name": "delivery",
|
|
727
|
-
"description": "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). It stores per-user channel preferences in the `notification-preference` entity, opt-outs for no-account recipient addresses in `notification-address-opt-out` (keyed by a blind-index hash, never the plaintext address), logs every attempt to `store_delivery_attempts`, and enforces idempotency and rate-limiting — add `channel-email`, `channel-in-app`, or `channel-push` on top to actually send anything. Unsubscribe/resubscribe links are served by `createUnsubscribeRoutes({ secret })` mounted via the app's `extraRoutes` at `/api/delivery/unsubscribe` and `/api/delivery/resubscribe`: `GET /unsubscribe?token=` renders a confirmation page (no write), `POST /unsubscribe` performs the opt-out (RFC 8058 one-click, token from the form body or query), `POST /resubscribe` undoes it (JSON `{token}` or form body only, never the query — reachable by a mail-client link prefetcher) — sign links with `signUnsubscribeToken` / `signAddressUnsubscribeToken` using the same secret. `channel-email` sets `List-Unsubscribe` / `List-Unsubscribe-Post` automatically when a message's `data.unsubscribeUrl` points at the unsubscribe route.",
|
|
728
|
+
"description": "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). Inside a write handler the delivery runs after the write has committed (the call returns `{ kind: \"deferred\" }`, a rolled-back write sends nothing); `immediate: true` delivers synchronously and returns `{ kind: \"dispatched\", deliveries }`. It stores per-user channel preferences in the `notification-preference` entity, opt-outs for no-account recipient addresses in `notification-address-opt-out` (keyed by a blind-index hash, never the plaintext address), logs every attempt to `store_delivery_attempts`, and enforces idempotency and rate-limiting — add `channel-email`, `channel-in-app`, or `channel-push` on top to actually send anything. Unsubscribe/resubscribe links are served by `createUnsubscribeRoutes({ secret })` mounted via the app's `extraRoutes` at `/api/delivery/unsubscribe` and `/api/delivery/resubscribe`: `GET /unsubscribe?token=` renders a confirmation page (no write), `POST /unsubscribe` performs the opt-out (RFC 8058 one-click, token from the form body or query), `POST /resubscribe` undoes it (JSON `{token}` or form body only, never the query — reachable by a mail-client link prefetcher) — sign links with `signUnsubscribeToken` / `signAddressUnsubscribeToken` using the same secret. `channel-email` sets `List-Unsubscribe` / `List-Unsubscribe-Post` automatically when a message's `data.unsubscribeUrl` points at the unsubscribe route.",
|
|
728
729
|
"toggleableDefault": null,
|
|
729
730
|
"requires": [
|
|
730
731
|
"tenant"
|
|
@@ -775,6 +776,27 @@
|
|
|
775
776
|
"recommended": false
|
|
776
777
|
}
|
|
777
778
|
},
|
|
779
|
+
{
|
|
780
|
+
"name": "document-designer",
|
|
781
|
+
"description": "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. Mount after template-resolver and, for the branding rail, managed-pages. Client: `documentDesignerClient()`; admin-shell lists the navs with `includeDocumentDesigner`.",
|
|
782
|
+
"toggleableDefault": null,
|
|
783
|
+
"requires": [
|
|
784
|
+
"template-resolver"
|
|
785
|
+
],
|
|
786
|
+
"optionalRequires": [],
|
|
787
|
+
"configReads": [],
|
|
788
|
+
"exposesApis": [],
|
|
789
|
+
"usesApis": [],
|
|
790
|
+
"extensionsUsed": [],
|
|
791
|
+
"configKeys": [],
|
|
792
|
+
"secrets": [],
|
|
793
|
+
"writeHandlers": [],
|
|
794
|
+
"uiHints": {
|
|
795
|
+
"displayLabel": "Documents · Template Designer",
|
|
796
|
+
"category": "content",
|
|
797
|
+
"recommended": false
|
|
798
|
+
}
|
|
799
|
+
},
|
|
778
800
|
{
|
|
779
801
|
"name": "document-ingest-foundation",
|
|
780
802
|
"description": "Shared PDF/Scan/Image → normalized-text ingest primitive. Owns the `documentExtract` entity (fileRefId, storageKey, per-page text + metadata) as an implicit entity-projection, the per-tenant `ocrLanguage`/`maxPagesPerFile` config keys, the `documentIngestProvider` extension-point providers register accepted mimeTypes/size caps under, and a fileRef.created/fileRef.restored trigger that resolves the provider for a file's mimeType and requests ingest via `documentIngest.requested` tagged with the winning provider — a delete→restore round-trip re-requests ingest the same way a fresh upload does. An orphan-extract guard forgets any `documentExtract` whose fileRef is no longer live at processing time, regardless of how a provider's own write races the fileRef's delete/forget — providers should write through `writeDocumentExtractForLiveFileRef`.",
|
|
@@ -1143,7 +1165,9 @@
|
|
|
1143
1165
|
"extensionsUsed": [],
|
|
1144
1166
|
"configKeys": [],
|
|
1145
1167
|
"secrets": [],
|
|
1146
|
-
"writeHandlers": [
|
|
1168
|
+
"writeHandlers": [
|
|
1169
|
+
"files:write:bind-referenced-files"
|
|
1170
|
+
],
|
|
1147
1171
|
"uiHints": {
|
|
1148
1172
|
"displayLabel": "Files · Metadata",
|
|
1149
1173
|
"category": "storage",
|
|
@@ -1798,6 +1822,30 @@
|
|
|
1798
1822
|
"all"
|
|
1799
1823
|
]
|
|
1800
1824
|
},
|
|
1825
|
+
{
|
|
1826
|
+
"key": "branding-logo-file",
|
|
1827
|
+
"qualifiedName": "managed-pages:config:branding-logo-file",
|
|
1828
|
+
"type": "text",
|
|
1829
|
+
"scope": "tenant",
|
|
1830
|
+
"default": "",
|
|
1831
|
+
"encrypted": false,
|
|
1832
|
+
"computed": false,
|
|
1833
|
+
"validated": false,
|
|
1834
|
+
"options": null,
|
|
1835
|
+
"bounds": null,
|
|
1836
|
+
"pattern": {
|
|
1837
|
+
"regex": "^$|^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"
|
|
1838
|
+
},
|
|
1839
|
+
"writeRoles": [
|
|
1840
|
+
"system",
|
|
1841
|
+
"TenantAdmin",
|
|
1842
|
+
"Admin",
|
|
1843
|
+
"SystemAdmin"
|
|
1844
|
+
],
|
|
1845
|
+
"readRoles": [
|
|
1846
|
+
"all"
|
|
1847
|
+
]
|
|
1848
|
+
},
|
|
1801
1849
|
{
|
|
1802
1850
|
"key": "branding-logo-url",
|
|
1803
1851
|
"qualifiedName": "managed-pages:config:branding-logo-url",
|
|
@@ -2053,7 +2101,7 @@
|
|
|
2053
2101
|
},
|
|
2054
2102
|
{
|
|
2055
2103
|
"name": "renderer-foundation",
|
|
2056
|
-
"description": "Plugin registry for content rendering (
|
|
2104
|
+
"description": "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. Requires `template-resolver` (declared via `r.requires`). Low-level building block — add `renderer-mail-html` (or write a custom plugin via `r.useExtension(\"renderer\", name, { kinds, render })`) rather than using this feature alone.",
|
|
2057
2105
|
"toggleableDefault": null,
|
|
2058
2106
|
"requires": [
|
|
2059
2107
|
"template-resolver"
|
|
@@ -2072,6 +2120,28 @@
|
|
|
2072
2120
|
"recommended": false
|
|
2073
2121
|
}
|
|
2074
2122
|
},
|
|
2123
|
+
{
|
|
2124
|
+
"name": "renderer-mail-html",
|
|
2125
|
+
"description": "A `renderer-foundation` plugin that handles `RenderRequest { kind: \"mail\" }` and returns `{ subject, html, text, previewText? }` suitable for sending via `channel-email`. The mail template (`kind: \"mail\"`, document HTML, subject in `meta`) resolves through `template-resolver`'s DB fallback, its body is compiled to Handlebars by template-resolver and rendered with escaping, then placed into an MJML layout (`kind: \"mail-layout\"`, slug `default` unless `meta.layoutSlug` says otherwise, in-code `DEFAULT_MAIL_LAYOUT_MJML` as last resort) that `mjml()` compiles to email-safe table HTML, with `html-to-text` producing the plain-text alternative. Seed an editable system layout with `seedSystemMailLayout(db)`. Requires `renderer-foundation` + `template-resolver`.",
|
|
2126
|
+
"toggleableDefault": null,
|
|
2127
|
+
"requires": [
|
|
2128
|
+
"renderer-foundation",
|
|
2129
|
+
"template-resolver"
|
|
2130
|
+
],
|
|
2131
|
+
"optionalRequires": [],
|
|
2132
|
+
"configReads": [],
|
|
2133
|
+
"exposesApis": [],
|
|
2134
|
+
"usesApis": [],
|
|
2135
|
+
"extensionsUsed": [
|
|
2136
|
+
{
|
|
2137
|
+
"extensionName": "renderer",
|
|
2138
|
+
"entityName": "mail-html"
|
|
2139
|
+
}
|
|
2140
|
+
],
|
|
2141
|
+
"configKeys": [],
|
|
2142
|
+
"secrets": [],
|
|
2143
|
+
"writeHandlers": []
|
|
2144
|
+
},
|
|
2075
2145
|
{
|
|
2076
2146
|
"name": "renderer-pdf",
|
|
2077
2147
|
"description": "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. `payload.variables` must be a `PdfTableDocument`. Requires `renderer-foundation`; mount a Puppeteer-based plugin instead when you need HTML/CSS layouts.",
|
|
@@ -2099,32 +2169,25 @@
|
|
|
2099
2169
|
}
|
|
2100
2170
|
},
|
|
2101
2171
|
{
|
|
2102
|
-
"name": "renderer-
|
|
2103
|
-
"description": "
|
|
2172
|
+
"name": "renderer-puppeteer-client",
|
|
2173
|
+
"description": "A `renderer-foundation` plugin that prints documents to PDF through a separately deployed `puppeteer-service`. A `document-pdf` request names a stored template (`{ slug, locale, variables }`): the plugin resolves the template and its `pdf-layout` (header, signature and footer regions), embeds every image as a data URI, turns repeating table header rows into a real table head and sends the finished HTML to the service, which performs no network fetches itself. The page format, margins, file name pattern and title come from the template meta. `renderAndStorePdf(ctx, ...)` renders and stores the PDF as a file with a `rendered-pdf` record that keeps the template and layout version, so a stored document can always be traced to the text it was created from. The `puppeteer-status` query tells a TenantAdmin whether the service answers; while it does not, rendering fails with a `puppeteer_unavailable` error (HTTP 503) and no PDF is created. `image-snapshot` requests still take ready HTML in `payload.content`. Configure the endpoint and token with `PUPPETEER_SERVICE_URL` and `PUPPETEER_AUTH_TOKEN`; the client retries 5xx and network errors with exponential backoff (default 3 retries), 4xx fails at once. The feature adds the `rendered_pdfs` table, so apps that mount it need a schema migration.",
|
|
2104
2174
|
"toggleableDefault": null,
|
|
2105
2175
|
"requires": [
|
|
2106
2176
|
"renderer-foundation"
|
|
2107
2177
|
],
|
|
2108
|
-
"optionalRequires": [
|
|
2109
|
-
"template-resolver"
|
|
2110
|
-
],
|
|
2178
|
+
"optionalRequires": [],
|
|
2111
2179
|
"configReads": [],
|
|
2112
2180
|
"exposesApis": [],
|
|
2113
2181
|
"usesApis": [],
|
|
2114
2182
|
"extensionsUsed": [
|
|
2115
2183
|
{
|
|
2116
2184
|
"extensionName": "renderer",
|
|
2117
|
-
"entityName": "
|
|
2185
|
+
"entityName": "puppeteer"
|
|
2118
2186
|
}
|
|
2119
2187
|
],
|
|
2120
2188
|
"configKeys": [],
|
|
2121
2189
|
"secrets": [],
|
|
2122
|
-
"writeHandlers": []
|
|
2123
|
-
"uiHints": {
|
|
2124
|
-
"displayLabel": "Renderer · Simple",
|
|
2125
|
-
"category": "notifications",
|
|
2126
|
-
"recommended": false
|
|
2127
|
-
}
|
|
2190
|
+
"writeHandlers": []
|
|
2128
2191
|
},
|
|
2129
2192
|
{
|
|
2130
2193
|
"name": "secrets",
|
|
@@ -2500,7 +2563,7 @@
|
|
|
2500
2563
|
},
|
|
2501
2564
|
{
|
|
2502
2565
|
"name": "template-resolver",
|
|
2503
|
-
"description": "Every piece of editable text lives here, in one entity: mail bodies, notification texts, PDF document templates, AI prompts and plain text blocks. What a record is used for is the `kind` (`notification`, `mail-
|
|
2566
|
+
"description": "Every piece of editable text lives here, in one entity: mail bodies, notification texts, PDF document templates, AI prompts and plain text blocks. What a record is used for is the `kind` (`notification`, `mail`, `mail-layout`, `document-pdf`, `pdf-layout`, `ai-prompt`, `text-block`, `snippet`, `image-snapshot`).\n\nReading is one call — `ctx.templateResolver.resolveTemplate({ tenantId, slug, kind, locale })`. It walks four levels: tenant+locale, system+locale, tenant+fallback-locale, system+fallback-locale. A tenant overrides a system default by simply having its own record; no application code changes. Writing programmatically goes through the `upsertSystem`, `upsertTenant`, `publish` and `archive` write handlers; a SystemAdmin retires or restores a system default with `publishSystem` and `archiveSystem`, and a tenant drops its override with `resetToSystem`.\n\nText an editor should be able to change belongs in a collection, declared at mount: `createTemplateResolverFeature({ collections: [{ id, kind, access: { roles }, nav }] })`. It appears in the navigation, and `access` is part of the mount because a bundled feature cannot know the host's roles. Each collection gets its own `<id>-list` / `<id>-item` / `<id>-set` handlers, so the dispatcher enforces the separation.\n\nHow a collection is edited follows from `contentFormat`: `plain` gives a text area, `rich` a small WYSIWYG (bold, italic, headings, lists, links), `markdown` a text area that stores markdown text (same insertable chips as `plain`). All three offer the collection's `variableSchema` as insertable chips and a preview rendered with sample data — an editor sees what `{{firstName}}` becomes without sending a mail. An app can register its own editor for a format; the last `clientFeature` that registers a format wins (a conflict logs a warning).\n\nA collection is tenant-wide by default. With `ownership: \"user\"` every user keeps their own entries — mail signatures being the obvious case. Those rows live in the separate `user-content-entry` entity and count as user data, so mounting one also requires the `template-resolver-user-data` feature and a migration on the app side.\n\n`validate` dry-runs the write pipeline for a draft (sanitizer, layout rules, placeholders) and reports what the template uses and misses. `export-bundle` / `import-bundle` (and `import-bundle-system`) move one template with all locales and resource files between tenants or environments; `copy-locale` starts a translation from an existing locale. Layouts are structured: a mail or PDF layout is `data-header` / `data-content` / `data-signature` / `data-footer` regions, and mjml layout source is system-only.\n\n`preview` renders a draft (or the stored template) with sample values taken from the `example` of each declared variable, through the same gates and the same mail renderer as a real send, and returns subject, html, `displayHtml` (the html with cid images as data URIs for an editor), text, attachments (inline images and files) and `checks` (images resolved, plain text present, size under 10 MiB, required placeholders used); nothing is saved or sent. `send-test` mails that rendering to the calling user's own address only, subject prefixed with `[TEST]`, at most 10 per user and hour; it needs `delivery`, `channel-email` and `renderer-mail-html` to be mounted.\n\nImages used by a template (`<img data-resource=\"logo\">`) are uploaded with `resourceUpload`, which re-encodes them, stores a file and links it under the key; `resourceRemove` unlinks it. `linkedResources` is written only through these two handlers. With `purpose: \"attachment\"` and a `fileName`, `resourceUpload` stores a PDF, PNG or JPEG as a file attachment of a mail template (original bytes, at most 10) that the mail renderer attaches after the inline images. `resourceLinkPdf` instead links a `document-pdf` template under a key: every mail sent from the template then carries that PDF, rendered at send time with the mail's own variables and locale, under a file name pattern such as `invoice-{{number}}.pdf`; files and linked PDFs share the limit of 10.\n\nReplaces the former `text-content` feature; its blocks now live here as kind `text-block`.",
|
|
2504
2567
|
"toggleableDefault": null,
|
|
2505
2568
|
"requires": [],
|
|
2506
2569
|
"optionalRequires": [],
|
|
@@ -2508,12 +2571,46 @@
|
|
|
2508
2571
|
"exposesApis": [],
|
|
2509
2572
|
"usesApis": [],
|
|
2510
2573
|
"extensionsUsed": [],
|
|
2511
|
-
"configKeys": [
|
|
2574
|
+
"configKeys": [
|
|
2575
|
+
{
|
|
2576
|
+
"key": "resource-max-bytes",
|
|
2577
|
+
"qualifiedName": "template-resolver:config:resource-max-bytes",
|
|
2578
|
+
"type": "number",
|
|
2579
|
+
"scope": "system",
|
|
2580
|
+
"default": 2097152,
|
|
2581
|
+
"encrypted": false,
|
|
2582
|
+
"computed": false,
|
|
2583
|
+
"validated": false,
|
|
2584
|
+
"options": null,
|
|
2585
|
+
"bounds": {
|
|
2586
|
+
"min": 1
|
|
2587
|
+
},
|
|
2588
|
+
"pattern": null,
|
|
2589
|
+
"writeRoles": [
|
|
2590
|
+
"system"
|
|
2591
|
+
],
|
|
2592
|
+
"readRoles": [
|
|
2593
|
+
"TenantAdmin",
|
|
2594
|
+
"Admin",
|
|
2595
|
+
"SystemAdmin"
|
|
2596
|
+
]
|
|
2597
|
+
}
|
|
2598
|
+
],
|
|
2512
2599
|
"secrets": [],
|
|
2513
2600
|
"writeHandlers": [
|
|
2514
2601
|
"template-resolver:write:archive",
|
|
2602
|
+
"template-resolver:write:archive-system",
|
|
2603
|
+
"template-resolver:write:copy-locale",
|
|
2604
|
+
"template-resolver:write:import-bundle",
|
|
2605
|
+
"template-resolver:write:import-bundle-system",
|
|
2515
2606
|
"template-resolver:write:publish",
|
|
2607
|
+
"template-resolver:write:publish-system",
|
|
2516
2608
|
"template-resolver:write:reply-snippets-set",
|
|
2609
|
+
"template-resolver:write:reset-to-system",
|
|
2610
|
+
"template-resolver:write:resource-link-pdf",
|
|
2611
|
+
"template-resolver:write:resource-remove",
|
|
2612
|
+
"template-resolver:write:resource-upload",
|
|
2613
|
+
"template-resolver:write:send-test",
|
|
2517
2614
|
"template-resolver:write:set",
|
|
2518
2615
|
"template-resolver:write:signatures-set",
|
|
2519
2616
|
"template-resolver:write:upsert-system",
|
|
@@ -3008,7 +3105,7 @@
|
|
|
3008
3105
|
},
|
|
3009
3106
|
{
|
|
3010
3107
|
"name": "user-data-rights-defaults",
|
|
3011
|
-
"description": "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. Mount this alongside `user-data-rights` for standard GDPR compliance; omit it only if your app needs custom anonymization logic for these entities.",
|
|
3108
|
+
"description": "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). Mount this alongside `user-data-rights` for standard GDPR compliance; omit it only if your app needs custom anonymization logic for these entities.",
|
|
3012
3109
|
"toggleableDefault": null,
|
|
3013
3110
|
"requires": [
|
|
3014
3111
|
"user",
|
|
@@ -3064,6 +3161,10 @@
|
|
|
3064
3161
|
"extensionName": "userData",
|
|
3065
3162
|
"entityName": "delivery-attempt"
|
|
3066
3163
|
},
|
|
3164
|
+
{
|
|
3165
|
+
"extensionName": "userData",
|
|
3166
|
+
"entityName": "download-attempt"
|
|
3167
|
+
},
|
|
3067
3168
|
{
|
|
3068
3169
|
"extensionName": "userData",
|
|
3069
3170
|
"entityName": "job-run"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-kumiko-app",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.363.0",
|
|
4
4
|
"description": "`bun create kumiko-app <name>` — scaffold a new Kumiko app with an interactive feature picker.",
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
|
|
@@ -27,10 +27,10 @@
|
|
|
27
27
|
}
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@cosmicdrift/kumiko-dev-server": "0.
|
|
31
|
-
"@cosmicdrift/kumiko-framework": "0.
|
|
32
|
-
"@cosmicdrift/kumiko-server-runtime": "0.
|
|
33
|
-
"@cosmicdrift/kumiko-testing": "0.
|
|
30
|
+
"@cosmicdrift/kumiko-dev-server": "0.363.0",
|
|
31
|
+
"@cosmicdrift/kumiko-framework": "0.363.0",
|
|
32
|
+
"@cosmicdrift/kumiko-server-runtime": "0.363.0",
|
|
33
|
+
"@cosmicdrift/kumiko-testing": "0.363.0",
|
|
34
34
|
"@inquirer/core": "^10.0.0",
|
|
35
35
|
"@inquirer/prompts": "^7.4.0"
|
|
36
36
|
},
|
package/src/changes.json
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"version": "0.363.0",
|
|
4
|
+
"type": "improvement",
|
|
5
|
+
"title": "The scaffold no longer offers renderer-simple",
|
|
6
|
+
"detail": "renderer-simple is removed from the bundled features, so the feature picker and the bundled feature manifest no longer list it. Template-based mails need template-resolver, renderer-foundation and renderer-mail-html."
|
|
7
|
+
}
|
|
8
|
+
]
|
|
@@ -152,12 +152,6 @@ export const FEATURE_CONSTRUCTORS: Readonly<Record<string, ScaffoldFeatureEntry>
|
|
|
152
152
|
exportName: "createRendererFoundationFeature",
|
|
153
153
|
callExpression: "createRendererFoundationFeature()",
|
|
154
154
|
},
|
|
155
|
-
"renderer-simple": {
|
|
156
|
-
name: "renderer-simple",
|
|
157
|
-
importPath: "@cosmicdrift/kumiko-bundled-features/renderer-simple",
|
|
158
|
-
exportName: "createRendererSimpleFeature",
|
|
159
|
-
callExpression: "createRendererSimpleFeature()",
|
|
160
|
-
},
|
|
161
155
|
"template-resolver": {
|
|
162
156
|
name: "template-resolver",
|
|
163
157
|
importPath: "@cosmicdrift/kumiko-bundled-features/template-resolver",
|