@cosmicdrift/kumiko-samples 0.391.1 → 0.392.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.
Files changed (137) hide show
  1. package/package.json +1 -1
  2. package/packages/bundled-features/package.json +1 -1
  3. package/packages/bundled-features/src/admin-shell/changes.json +6 -0
  4. package/packages/bundled-features/src/admin-shell/feature.ts +60 -31
  5. package/packages/bundled-features/src/auth-email-password/handlers/invite-accept-with-login.write.ts +2 -2
  6. package/packages/bundled-features/src/auth-email-password/handlers/invite-signup-complete.write.ts +2 -2
  7. package/packages/bundled-features/src/cap-overview/changes.json +12 -0
  8. package/packages/bundled-features/src/cap-overview/handlers/caps-usage.query.ts +1 -0
  9. package/packages/bundled-features/src/cap-overview/handlers/tenant-caps-list.query.ts +3 -2
  10. package/packages/bundled-features/src/cap-overview/i18n.ts +4 -0
  11. package/packages/bundled-features/src/cap-overview/types.ts +4 -1
  12. package/packages/bundled-features/src/cap-overview/web/cap-amount-format.ts +37 -0
  13. package/packages/bundled-features/src/cap-overview/web/cap-cards-panel.tsx +138 -54
  14. package/packages/bundled-features/src/cap-overview/web/cap-usage-bar.tsx +18 -4
  15. package/packages/bundled-features/src/cap-overview/web/cap-usage-cell.tsx +6 -3
  16. package/packages/bundled-features/src/cap-overview/web/usage-math.ts +5 -1
  17. package/packages/bundled-features/src/channel-email/app-transport.ts +37 -0
  18. package/packages/bundled-features/src/channel-email/changes.json +6 -0
  19. package/packages/bundled-features/src/channel-email/feature.ts +6 -0
  20. package/packages/bundled-features/src/channel-email/index.ts +5 -0
  21. package/packages/bundled-features/src/channel-email/smtp-transport.ts +2 -22
  22. package/packages/bundled-features/src/foundation-shared/changes.json +6 -0
  23. package/packages/bundled-features/src/foundation-shared/index.ts +1 -0
  24. package/packages/bundled-features/src/foundation-shared/mail-display-name.ts +20 -0
  25. package/packages/bundled-features/src/inbound-mail-foundation/account-record.ts +48 -0
  26. package/packages/bundled-features/src/inbound-mail-foundation/changes.json +18 -0
  27. package/packages/bundled-features/src/inbound-mail-foundation/feature.ts +1 -1
  28. package/packages/bundled-features/src/inbound-mail-foundation/i18n.ts +17 -0
  29. package/packages/bundled-features/src/inbound-mail-foundation/index.ts +18 -0
  30. package/packages/bundled-features/src/inbound-mail-foundation/outbound-mail.ts +66 -0
  31. package/packages/bundled-features/src/inbound-mail-foundation/probe.ts +8 -45
  32. package/packages/bundled-features/src/inbound-mail-foundation/screens.ts +14 -0
  33. package/packages/bundled-features/src/inbound-mail-foundation/send-from-account.ts +118 -0
  34. package/packages/bundled-features/src/inbound-mail-foundation/types.ts +8 -0
  35. package/packages/bundled-features/src/inbound-mail-foundation/watch-supervisor.ts +13 -8
  36. package/packages/bundled-features/src/inbound-provider-imap/changes.json +12 -0
  37. package/packages/bundled-features/src/inbound-provider-imap/connect-mailbox.ts +156 -0
  38. package/packages/bundled-features/src/inbound-provider-imap/constants.ts +42 -0
  39. package/packages/bundled-features/src/inbound-provider-imap/credential-document.ts +49 -1
  40. package/packages/bundled-features/src/inbound-provider-imap/feature.ts +12 -35
  41. package/packages/bundled-features/src/inbound-provider-imap/i18n.ts +48 -0
  42. package/packages/bundled-features/src/inbound-provider-imap/imap-client.ts +1 -1
  43. package/packages/bundled-features/src/inbound-provider-imap/screens.ts +44 -0
  44. package/packages/bundled-features/src/inbound-provider-imap/send-mail.ts +73 -0
  45. package/packages/bundled-features/src/inbound-provider-imap/sent-folder.ts +49 -0
  46. package/packages/bundled-features/src/inbound-provider-imap/smtp-client.ts +131 -0
  47. package/packages/bundled-features/src/legal-pages/README.md +5 -10
  48. package/packages/bundled-features/src/mail-foundation/changes.json +6 -0
  49. package/packages/bundled-features/src/mail-foundation/i18n.ts +9 -1
  50. package/packages/bundled-features/src/mail-foundation/probe.ts +43 -22
  51. package/packages/bundled-features/src/readiness/built-in-probes.ts +15 -2
  52. package/packages/bundled-features/src/readiness/changes.json +30 -0
  53. package/packages/bundled-features/src/readiness/constants.ts +2 -0
  54. package/packages/bundled-features/src/readiness/i18n.ts +7 -1
  55. package/packages/bundled-features/src/readiness/index.ts +6 -1
  56. package/packages/bundled-features/src/readiness/screens.ts +25 -2
  57. package/packages/bundled-features/src/readiness/visible-results.ts +2 -1
  58. package/packages/bundled-features/src/renderer-foundation/README.md +19 -21
  59. package/packages/bundled-features/src/template-resolver/README.md +144 -165
  60. package/packages/bundled-features/src/tier-engine/changes.json +6 -0
  61. package/packages/bundled-features/src/tier-engine/feature.ts +17 -6
  62. package/packages/bundled-features/src/user-data-rights/handlers/download-by-token.query.ts +3 -3
  63. package/packages/bundled-features/src/user-data-rights/schema/download-token.ts +5 -3
  64. package/packages/bundled-features/src/user-data-rights/token-helpers.ts +5 -4
  65. package/samples/apps/cap-billing-demo/bin/server.ts +7 -7
  66. package/samples/apps/cap-billing-demo/src/feature.ts +38 -38
  67. package/samples/apps/cap-billing-demo/src/run-config.ts +20 -20
  68. package/samples/apps/cap-billing-demo/src/tier-map.ts +14 -14
  69. package/samples/apps/config-demo/src/app/server.ts +4 -4
  70. package/samples/apps/config-demo/src/features/demo/feature.ts +4 -4
  71. package/samples/apps/marketing-demo/playwright.config.ts +3 -3
  72. package/samples/apps/marketing-demo/src/app/client.tsx +2 -2
  73. package/samples/apps/marketing-demo/src/app/seed-data.ts +9 -9
  74. package/samples/apps/marketing-demo/src/app/seed.ts +14 -14
  75. package/samples/apps/marketing-demo/src/app/server.ts +7 -7
  76. package/samples/apps/marketing-demo/src/app/shell.tsx +3 -3
  77. package/samples/apps/marketing-demo/src/features/assets/feature.ts +3 -3
  78. package/samples/apps/marketing-demo/src/features/assets/i18n.ts +7 -7
  79. package/samples/apps/marketing-demo/src/features/assets/web/index.ts +2 -2
  80. package/samples/apps/showcase/src/app/client.tsx +3 -3
  81. package/samples/apps/showcase/src/app/seed-items.ts +14 -14
  82. package/samples/apps/showcase/src/app/server.ts +7 -7
  83. package/samples/apps/showcase/src/features/demos/feature.ts +13 -13
  84. package/samples/apps/showcase/src/features/demos/i18n.ts +2 -2
  85. package/samples/apps/showcase/src/features/demos/web/components/page.tsx +5 -5
  86. package/samples/apps/showcase/src/features/demos/web/index.ts +4 -4
  87. package/samples/apps/showcase/src/features/demos/web/pages/dialog.tsx +1 -1
  88. package/samples/apps/showcase/src/features/demos/web/pages/inputs.tsx +8 -8
  89. package/samples/apps/showcase/src/features/demos/web/pages/layout.tsx +5 -5
  90. package/samples/apps/showcase/src/features/demos/web/pages/toast.tsx +3 -3
  91. package/samples/apps/showcase/src/features/items/feature.ts +8 -8
  92. package/samples/apps/showcase/src/features/items/i18n.ts +3 -3
  93. package/samples/apps/showcase/src/features/items/index.ts +3 -3
  94. package/samples/apps/showcase/src/features/items/schema/index.ts +4 -4
  95. package/samples/apps/showcase/src/features/items/schema/item.ts +27 -27
  96. package/samples/apps/showcase/src/features/items/web/index.ts +6 -6
  97. package/samples/apps/styleguide/src/app/seed.ts +3 -3
  98. package/samples/apps/styleguide/src/app/server.ts +2 -2
  99. package/samples/apps/styleguide/src/app/shell.tsx +4 -4
  100. package/samples/apps/styleguide/src/features/content/web.tsx +6 -6
  101. package/samples/apps/styleguide/src/features/examples/feature.ts +8 -8
  102. package/samples/apps/styleguide/src/features/gallery/feature.ts +9 -9
  103. package/samples/apps/styleguide/src/features/gallery/web/Gallery.tsx +12 -12
  104. package/samples/apps/styleguide/src/features/widgets/feature.ts +2 -2
  105. package/samples/apps/styleguide/src/features/widgets/web/DashboardFilterEcho.tsx +3 -3
  106. package/samples/apps/styleguide/src/features/widgets/web/DashboardKpiIcon.tsx +4 -4
  107. package/samples/apps/ui-walkthrough/playwright.config.ts +6 -6
  108. package/samples/apps/ui-walkthrough/scripts/emit-e2e-data.ts +5 -5
  109. package/samples/apps/ui-walkthrough/src/app/auth-constants.ts +13 -13
  110. package/samples/apps/ui-walkthrough/src/app/client.tsx +2 -2
  111. package/samples/apps/ui-walkthrough/src/app/server.ts +14 -14
  112. package/samples/apps/ui-walkthrough/src/app/shell.tsx +3 -3
  113. package/samples/apps/use-all-bundled/bin/main.ts +5 -5
  114. package/samples/apps/use-all-bundled/feature-manifest.json +10 -4
  115. package/samples/apps/use-all-bundled/kumiko/schema.ts +7 -7
  116. package/samples/apps/use-all-bundled/scripts/check-coverage.ts +20 -20
  117. package/samples/apps/use-all-bundled/src/app/auth-constants.ts +4 -4
  118. package/samples/apps/use-all-bundled/src/app/client.tsx +4 -4
  119. package/samples/apps/use-all-bundled/src/app/dev-app-options.ts +12 -12
  120. package/samples/apps/use-all-bundled/src/app/seed.ts +6 -6
  121. package/samples/apps/user-data-rights-demo/src/feature.ts +23 -23
  122. package/samples/apps/workspaces/playwright.config.ts +5 -5
  123. package/samples/apps/workspaces/src/app/auth-constants.ts +3 -3
  124. package/samples/apps/workspaces/src/app/server.ts +16 -16
  125. package/samples/apps/workspaces/src/app/shell.tsx +2 -2
  126. package/samples/apps/workspaces/src/features/demo/schema/order.ts +4 -4
  127. package/samples/apps/workspaces/src/features/demo-driver/feature.ts +5 -5
  128. package/samples/recipes/apex-surface-auth/src/feature.ts +31 -31
  129. package/samples/recipes/designer-demo/src/feature.ts +6 -6
  130. package/samples/recipes/encrypted-tenant-config/src/feature.ts +20 -20
  131. package/samples/recipes/lane-routing/src/feature.ts +5 -5
  132. package/samples/recipes/legal-pages/src/feature.ts +29 -29
  133. package/samples/recipes/record-detail-layout/src/feature.ts +21 -2
  134. package/samples/recipes/seed-migration/scripts/smoke.ts +20 -20
  135. package/samples/recipes/seed-migration/seeds/2026-01-01-initial-tenants.ts +11 -11
  136. package/samples/recipes/seed-migration/seeds/2026-02-01-fix-admin-roles.ts +13 -13
  137. package/samples/recipes/user-data-rights/src/feature.ts +13 -13
@@ -1,8 +1,8 @@
1
- // Emitter für e2e/.e2e-data.json. Läuft unter bun, lädt die volle
2
- // framework-runtime, baut die Registry und schreibt generateE2ESpec()-
3
- // Output als JSON. Playwrights globalSetup ruft dieses Script vor
4
- // jedem Run als Subprozess — dadurch landet framework-runtime NIE im
5
- // Playwright-Worker (wo sie mit Playwrights expect kollidiert).
1
+ // Emitter for e2e/.e2e-data.json. Runs under bun, loads the full
2
+ // framework-runtime, builds the registry and writes the generateE2ESpec()
3
+ // output as JSON. Playwright's globalSetup calls this script before
4
+ // every run as a subprocess — so framework-runtime NEVER lands in the
5
+ // Playwright worker (where it collides with Playwright's expect).
6
6
 
7
7
  import { mkdir, writeFile } from "node:fs/promises";
8
8
  import { dirname, resolve } from "node:path";
@@ -1,21 +1,21 @@
1
- // Reine Konstanten — kein Framework-Import. Wird sowohl von seed.ts
2
- // (server-Seite, idempotent admin-Anlage) als auch vom E2E-Helper
3
- // `e2e/_helpers/login.ts` gelesen.
1
+ // Pure constants — no framework import. Read both by seed.ts
2
+ // (server side, idempotent admin creation) and by the E2E helper
3
+ // `e2e/_helpers/login.ts`.
4
4
  //
5
- // Warum getrennt: e2e-Specs laufen im Playwright-Worker, der nichts aus
6
- // `@cosmicdrift/kumiko-testing/helpers` (zieht `vitest`) oder
7
- // `@cosmicdrift/kumiko-bundled-features/.../testing` (zieht das ebenfalls) ertragen
8
- // kann — Vitest's expect redefined Object.prototype-Symbole und kollidiert
9
- // mit Playwrights expect. Diese Datei bleibt importchain-leer und ist die
10
- // einzige sichere shared Quelle für die Login-Strings.
5
+ // Why separate: e2e specs run in the Playwright worker, which cannot tolerate anything from
6
+ // `@cosmicdrift/kumiko-testing/helpers` (pulls in `vitest`) or
7
+ // `@cosmicdrift/kumiko-bundled-features/.../testing` (pulls that in too) —
8
+ // Vitest's expect redefines Object.prototype symbols and collides
9
+ // with Playwright's expect. This file stays import-chain-empty and is the
10
+ // only safe shared source for the login strings.
11
11
 
12
12
  export const ADMIN_EMAIL = "admin@kumiko.dev";
13
13
  export const ADMIN_PASSWORD = "kumiko-admin";
14
14
 
15
- // Tenant-IDs gespiegelt zwischen server.ts (TenantId-Cast) und client.tsx
16
- // (plain string für tenantName-Mapping). Hier zentral als plain strings,
17
- // da diese Datei framework-frei bleibt — server.ts macht den
18
- // `as TenantId`-Cast lokal.
15
+ // Tenant IDs mirrored between server.ts (TenantId cast) and client.tsx
16
+ // (plain string for tenantName mapping). Kept centrally here as plain strings
17
+ // since this file stays framework-free — server.ts does the
18
+ // `as TenantId` cast locally.
19
19
  export const DEV_TENANT_ID = "00000000-0000-4000-8000-000000000001";
20
20
  export const BETA_TENANT_ID = "00000000-0000-4000-8000-000000000002";
21
21
 
@@ -1,5 +1,5 @@
1
- // Browser-Entry. Sammelt nur die ClientFeatures (auth + tasks) und
2
- // übergibt Shell + clientFeatures an createKumikoApp.
1
+ // Browser entry. Only collects the ClientFeatures (auth + tasks) and
2
+ // hands shell + clientFeatures to createKumikoApp.
3
3
 
4
4
  import { emailPasswordClient } from "@cosmicdrift/kumiko-bundled-features/auth-email-password/web";
5
5
  import { billingFoundationClient } from "@cosmicdrift/kumiko-bundled-features/billing-foundation/web";
@@ -1,13 +1,13 @@
1
1
  // @runtime dev
2
2
  //
3
- // Dev-Server für ui-walkthrough. runDevApp mischt die Standard-Features
4
- // (config/user/tenant/auth) automatisch dazu wenn `auth` gesetzt ist und
5
- // ruft seedAdmin im onAfterSetup. Schema landet als window.__KUMIKO_
6
- // SCHEMA__-Injection im Browser.
3
+ // Dev server for ui-walkthrough. runDevApp mixes in the standard features
4
+ // (config/user/tenant/auth) automatically when `auth` is set and
5
+ // calls seedAdmin in onAfterSetup. The schema lands in the browser as a
6
+ // window.__KUMIKO_SCHEMA__ injection.
7
7
  //
8
- // Auth-Mode aktiv: Client muss sich über den Login-Screen anmelden,
9
- // kein Auto-Mint-JWT mehr. Persistent-DB-Modus über KUMIKO_DEV_DB_NAME
10
- // in der Umgebung.
8
+ // Auth mode active: the client must log in via the login screen,
9
+ // no more auto-mint JWT. Persistent DB mode via KUMIKO_DEV_DB_NAME
10
+ // in the environment.
11
11
 
12
12
  import { createUnsubscribeRoutes } from "@cosmicdrift/kumiko-bundled-features/delivery";
13
13
  import { runDevApp } from "@cosmicdrift/kumiko-dev-server";
@@ -27,17 +27,17 @@ import { seedRental } from "./seed-rental";
27
27
  import { seedTemplates } from "./seed-templates";
28
28
  import { seedVehicles } from "./seed-vehicles";
29
29
 
30
- // Zwei feste Tenants — Admin ist in beiden Mitglied damit der
31
- // TenantSwitcher im Sample sichtbar ist (rendert nur bei >1 Tenant).
32
- // Unterschiedliche Rollen pro Tenant beweisen tenant-isolierte
33
- // Memberships. TenantId-Cast lokal an der Use-Site (auth-constants.ts
34
- // bleibt framework-frei für den E2E-Helper-Pfad).
30
+ // Two fixed tenants — the admin is a member of both so the
31
+ // TenantSwitcher is visible in the sample (renders only with >1 tenant).
32
+ // Different roles per tenant prove tenant-isolated
33
+ // memberships. TenantId cast local at the use site (auth-constants.ts
34
+ // stays framework-free for the E2E helper path).
35
35
 
36
36
  await runDevApp({
37
37
  features: [...APP_FEATURES],
38
38
  seeds: [seedTasks, seedRental, seedVehicles, seedTemplates, seedPlanTerms],
39
- // PORT env-var override für Playwright-e2e-Runs (config zeigt auf 4174);
40
- // sonst lokal-Default 4173.
39
+ // PORT env var override for Playwright e2e runs (config points to 4174);
40
+ // otherwise local default 4173.
41
41
  port: Number.parseInt(process.env["PORT"] ?? "4173", 10),
42
42
  // Logo and attachment uploads of the mail editor need a file store.
43
43
  files: { storageProvider: createInMemoryFileProvider() },
@@ -1,6 +1,6 @@
1
- // AppShell mit Brand und User-Menü im Sidebar-Footer. Tenant-, Sprach- und
2
- // Theme-Wahl hängen im User-Menü. Tenant-Name-Mapping bleibt im Shell —
3
- // App-spezifische Logik, kein Framework-Konzept.
1
+ // AppShell with brand and user menu in the sidebar footer. Tenant, language and
2
+ // theme choice live in the user menu. Tenant name mapping stays in the shell —
3
+ // app-specific logic, not a framework concept.
4
4
 
5
5
  import {
6
6
  TenantMenuItems,
@@ -33,11 +33,11 @@ await runProdApp({
33
33
  // every bundled feature mounted. Real apps wire createPgKmsAdapter(...).
34
34
  kms: new InMemoryKmsAdapter(),
35
35
  blindIndexKey: Buffer.alloc(32, 7).toString("base64"),
36
- // migrations default ("./kumiko/migrations") — Boot-mode springt vor dem
37
- // Schema-Drift-Gate raus, der Pfad ist hier nur der runProdApp-Default.
38
- // auth.admin triggert composeFeatures(includeBundled:true) — auto-mounts
39
- // config + user + tenant + auth-email-password. Boot-mode exitiert
40
- // bevor admin-Seeding läuft, der Wert ist nur ein Stub für die Typen.
36
+ // migrations default ("./kumiko/migrations") — boot mode exits before the
37
+ // schema drift gate, the path here is only the runProdApp default.
38
+ // auth.admin triggers composeFeatures(includeBundled:true) — auto-mounts
39
+ // config + user + tenant + auth-email-password. Boot mode exits
40
+ // before admin seeding runs, the value is only a stub for the types.
41
41
  auth: {
42
42
  admin: {
43
43
  email: "smoke@use-all-bundled.local",
@@ -35,7 +35,7 @@
35
35
  },
36
36
  {
37
37
  "name": "admin-shell",
38
- "description": "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, 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()`.",
38
+ "description": "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). 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()`.",
39
39
  "toggleableDefault": null,
40
40
  "requires": [
41
41
  "user",
@@ -452,6 +452,10 @@
452
452
  "exposesApis": [],
453
453
  "usesApis": [],
454
454
  "extensionsUsed": [
455
+ {
456
+ "extensionName": "emailAppTransport",
457
+ "entityName": "app"
458
+ },
455
459
  {
456
460
  "extensionName": "deliveryChannel",
457
461
  "entityName": "email"
@@ -1390,7 +1394,7 @@
1390
1394
  },
1391
1395
  {
1392
1396
  "name": "inbound-mail-foundation",
1393
- "description": "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). The foundation owns three event-sourced streams (mail-account lifecycle, inbound-message with exactly-once ingest via deterministic aggregate ids, mail-thread rollup) and their projections `read_mail_accounts`/`read_inbound_messages`/`read_mail_threads`; PII fields are envelope-encrypted per tenant subject key before every append (crypto-shredding on tenant destroy). Wire `createInboundMailConnectRoutes` (OAuth connect + anonymous callback outside /api) and `createInboundMailSupervisor` (IDLE watch with backoff + reconciliation poll) in bin/server.ts. Consume `inbound-mail-foundation:event:inbound-message-received` from app features to attach business processes.",
1397
+ "description": "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. `sendMailFromAccount(ctx, { accountId, message })` sends from a connected mailbox through its provider (From = the mailbox address, no fallback to the app sender) and answers typed failures (not configured, login rejected, server unreachable, rejected, invalid message); app write handlers call it, it is no endpoint of its own. The foundation owns three event-sourced streams (mail-account lifecycle, inbound-message with exactly-once ingest via deterministic aggregate ids, mail-thread rollup) and their projections `read_mail_accounts`/`read_inbound_messages`/`read_mail_threads`; PII fields are envelope-encrypted per tenant subject key before every append (crypto-shredding on tenant destroy). Wire `createInboundMailConnectRoutes` (OAuth connect + anonymous callback outside /api) and `createInboundMailSupervisor` (IDLE watch with backoff + reconciliation poll) in bin/server.ts. Consume `inbound-mail-foundation:event:inbound-message-received` from app features to attach business processes.",
1394
1398
  "toggleableDefault": null,
1395
1399
  "adoptsBundledLists": true,
1396
1400
  "requires": [
@@ -1430,7 +1434,7 @@
1430
1434
  },
1431
1435
  {
1432
1436
  "name": "inbound-provider-imap",
1433
- "description": "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). Incremental sync via a UIDVALIDITY:lastUid cursor (UIDVALIDITY change triggers a cursor-invalid full resync), initial backfill bounded by the foundation backfill window, and live push via IMAP IDLE for the watch supervisor. Store the per-account connection JSON (host/port/secure/user/password) in the account credential secret slot before the first sync.",
1437
+ "description": "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). Incremental sync via a UIDVALIDITY:lastUid cursor (UIDVALIDITY change triggers a cursor-invalid full resync), initial backfill bounded by the foundation backfill window, and live push via IMAP IDLE for the watch supervisor. Tenant admins connect a mailbox through the \"Connect mailbox\" form on the mail accounts list (`connect-mailbox` checks the IMAP login and the SMTP login of the outgoing server first, then creates the account and its credential secret in one transaction); programmatic setups store the per-account connection JSON (host/port/secure/user/password, plus smtpHost/smtpPort for sending) in the account credential secret slot before the first sync. Implements the provider `send`: `sendMailFromAccount` sends over the SMTP server of the mailbox itself (implicit TLS, same login) with the mailbox address as From and stores a copy in its sent folder.",
1434
1438
  "toggleableDefault": null,
1435
1439
  "requires": [
1436
1440
  "inbound-mail-foundation",
@@ -1448,7 +1452,9 @@
1448
1452
  ],
1449
1453
  "configKeys": [],
1450
1454
  "secrets": [],
1451
- "writeHandlers": [],
1455
+ "writeHandlers": [
1456
+ "inbound-provider-imap:write:connect-mailbox"
1457
+ ],
1452
1458
  "uiHints": {
1453
1459
  "displayLabel": "Inbound Mail · IMAP",
1454
1460
  "category": "notifications",
@@ -1,12 +1,12 @@
1
1
  // @runtime dev
2
- // App-Schema für die `kumiko-schema`-CLI (generate | validate). Komponiert die
3
- // App-Features einmal und leitet beide CLI-Inputs ab. CLI importiert via
4
- // dynamic-import.
2
+ // App schema for the `kumiko-schema` CLI (generate | validate). Composes the
3
+ // app features once and derives both CLI inputs. The CLI imports via
4
+ // dynamic import.
5
5
  //
6
- // **Convention:** dieses File exportiert
7
- // - `ENTITY_METAS: readonly EntityTableMeta[]` (Pflicht — generate + validate)
8
- // - `FEATURES: readonly FeatureDefinition[]` (optional — aktiviert
9
- // `kumiko-schema validate`s validateBoot-Layer; ohne wird er übersprungen)
6
+ // **Convention:** this file exports
7
+ // - `ENTITY_METAS: readonly EntityTableMeta[]` (required — generate + validate)
8
+ // - `FEATURES: readonly FeatureDefinition[]` (optional — activates
9
+ // the validateBoot layer of `kumiko-schema validate`; without it that layer is skipped)
10
10
 
11
11
  import { collectTableMetas, type EntityTableMeta } from "@cosmicdrift/kumiko-framework/db";
12
12
  import type { FeatureDefinition } from "@cosmicdrift/kumiko-framework/engine";
@@ -1,17 +1,17 @@
1
1
  #!/usr/bin/env bun
2
- // biome-ignore-all lint/suspicious/noConsole: CLI-Script, console ist Feature.
2
+ // biome-ignore-all lint/suspicious/noConsole: CLI script, console is the feature.
3
3
  //
4
- // Coverage lint. Diffs die Top-Level-Exports von bundled-features gegen die Features, die
5
- // use-all-bundled mountet (FEATURES aus kumiko/schema.ts: APP_FEATURES plus die von
6
- // composeFeatures ergänzten Auth-/Signup-Features). Fängt drei Drifts:
4
+ // Coverage lint. Diffs the top-level exports of bundled-features against the features that
5
+ // use-all-bundled mounts (FEATURES from kumiko/schema.ts: APP_FEATURES plus the auth/signup
6
+ // features added by composeFeatures). Catches three drifts:
7
7
  //
8
- // 1. Ein Export, der weder gemountet ist noch in EXPECTED_HELD_BACK steht
9
- // 2. Ein EXPECTED_HELD_BACK-Eintrag, der kein Export mehr ist
10
- // 3. Ein EXPECTED_HELD_BACK-Eintrag, der inzwischen gemountet ist (stale)
8
+ // 1. An export that is neither mounted nor listed in EXPECTED_HELD_BACK
9
+ // 2. An EXPECTED_HELD_BACK entry that is no longer an export
10
+ // 3. An EXPECTED_HELD_BACK entry that has since been mounted (stale)
11
11
  //
12
12
  // Usage:
13
13
  // bun samples/apps/use-all-bundled/scripts/check-coverage.ts
14
- // Exit 0 wenn alles in Sync, exit 1 wenn Drift.
14
+ // Exit 0 if everything is in sync, exit 1 on drift.
15
15
 
16
16
  import { readFileSync } from "node:fs";
17
17
  import { resolve } from "node:path";
@@ -20,11 +20,11 @@ import { FEATURES } from "../kumiko/schema";
20
20
  const ROOT = resolve(import.meta.dir, "..");
21
21
  const FRAMEWORK_ROOT = resolve(ROOT, "..", "..", "..");
22
22
 
23
- // Held-back exports: existieren in bundled-features, sind aber nicht
24
- // als runtime-feature in use-all-bundled mountbar.
23
+ // Held-back exports: exist in bundled-features, but cannot be mounted
24
+ // as a runtime feature in use-all-bundled.
25
25
  //
26
- // Nur Utilities ohne defineFeature (foundation-shared ist pure helpers)
27
- // und scaffold-only Features. Gemountete Features gehören nicht hierher.
26
+ // Only utilities without defineFeature (foundation-shared is pure helpers)
27
+ // and scaffold-only features. Mounted features do not belong here.
28
28
  const EXPECTED_HELD_BACK = new Set([
29
29
  "foundation-shared", // utilities (requireDefined/requireNonEmpty), kein feature
30
30
  "page-render", // shared HTML render/cache helpers (legal/managed-pages), kein defineFeature
@@ -32,16 +32,16 @@ const EXPECTED_HELD_BACK = new Set([
32
32
  ]);
33
33
 
34
34
  // Sub-paths in bundled-features's package.json exports (./tenant/seeding,
35
- // ./auth-email-password/web, …) sind keine Features, nur utilities.
36
- // Wir interessieren uns nur für top-level-paths. Nicht-Feature-Exports
37
- // (Root ".", package.json, Asset-Endungen) sind ausgeschlossen, damit ein
38
- // künftiger ./styles.css o.ä. nicht fälschlich als unreferenced Feature
39
- // einen CI-Fail auslöst.
35
+ // ./auth-email-password/web, …) are not features, only utilities.
36
+ // We only care about top-level paths. Non-feature exports
37
+ // (root ".", package.json, asset extensions) are excluded so that a
38
+ // future ./styles.css or similar does not wrongly trigger a CI fail as an
39
+ // unreferenced feature.
40
40
  function isFeatureExportPath(path: string): boolean {
41
41
  if (!path.startsWith("./")) return false;
42
42
  const segment = path.slice(2);
43
43
  if (segment.length === 0) return false;
44
- // Top-level only: kein weiterer "/" nach dem ersten Segment.
44
+ // Top-level only: no further "/" after the first segment.
45
45
  if (segment.includes("/")) return false;
46
46
  // Feature-Export-Pfade sind extension-frei (./audit, ./feature-toggles).
47
47
  // Asset-/Manifest-Exports (./package.json, ./styles.css) haben einen "."
@@ -68,8 +68,8 @@ const mounted = new Set<string>(FEATURES.map((feature) => feature.name));
68
68
  const unreferenced = [...exports].filter(
69
69
  (name) => !mounted.has(name) && !EXPECTED_HELD_BACK.has(name),
70
70
  );
71
- // Ohne diese Prüfungen hätte der Guard in seiner eigenen Konfig einen Blind-Spot: ein
72
- // umbenanntes oder entferntes Feature bliebe still in der Liste, ein gemountetes ebenso.
71
+ // Without these checks the guard would have a blind spot in its own config: a
72
+ // renamed or removed feature would silently stay in the list, as would a mounted one.
73
73
  const staleHeldBack = [...EXPECTED_HELD_BACK].filter((name) => !exports.has(name));
74
74
  const mountedHeldBack = [...EXPECTED_HELD_BACK].filter((name) => mounted.has(name));
75
75
 
@@ -1,7 +1,7 @@
1
- // Reine Konstanten — kein Framework-Import, damit der E2E-Login-Helper
2
- // (Playwright-Worker) sie lesen kann, ohne die vitest-Importchain aus
3
- // `@cosmicdrift/kumiko-testing/helpers` mitzuziehen (kollidiert mit
4
- // Playwrights expect über Object.prototype-Symbole).
1
+ // Pure constants — no framework import, so the E2E login helper
2
+ // (Playwright worker) can read them without pulling in the vitest import chain from
3
+ // `@cosmicdrift/kumiko-testing/helpers` (collides with
4
+ // Playwright's expect via Object.prototype symbols).
5
5
 
6
6
  export const ADMIN_EMAIL = "admin@kumiko.dev";
7
7
  export const ADMIN_PASSWORD = "kumiko-admin";
@@ -6,10 +6,10 @@
6
6
  // APP_TRANSLATIONS overrides here.
7
7
  //
8
8
  // APP_TRANSLATIONS: die entity-backed Admin-Screens (tenant/user/managed-pages)
9
- // haben kein /web-Plugin, das ihre Spalten-Labels mitbrächte. Sie nutzen die
10
- // `<feature>:entity:<e>:field:*`-Convention und überlassen die Labels bewusst
11
- // der App (siehe user/screens.ts). use-all-bundled IST diese App.
12
- //
9
+ // APP_TRANSLATIONS: the entity-backed admin screens (tenant/user/managed-pages)
10
+ // have no /web plugin that would bring their column labels. They use the
11
+ // `<feature>:entity:<e>:field:*` convention and deliberately leave the labels
12
+ // to the app (see user/screens.ts). use-all-bundled IS that app.
13
13
  // adminShellClient + tenant/audit/jobs/config: WorkspaceShell needs the
14
14
  // admin-shell overview components and Settings-Hub i18n; mirror admin-console
15
15
  // client registration for features that run-config already mounts server-side.
@@ -1,14 +1,14 @@
1
1
  // @runtime dev
2
2
  //
3
- // Dev-/Screenshot-Server für das use-all-bundled Sample. runDevApp mischt im
4
- // auth-Mode die 4 Standard-Features (config/user/tenant/auth) automatisch via
5
- // composeFeatures(includeBundled) dazu — exakt die 4, die APP_FEATURES bewusst
6
- // auslässt — und seedet den Admin im onAfterSetup. So rendert EINE App alle
7
- // bundled-feature-Screens für die Doku-Previews.
3
+ // Dev/screenshot server for the use-all-bundled sample. In auth mode runDevApp
4
+ // mixes in the 4 standard features (config/user/tenant/auth) automatically via
5
+ // composeFeatures(includeBundled) — exactly the 4 that APP_FEATURES deliberately
6
+ // omits — and seeds the admin in onAfterSetup. That way ONE app renders all
7
+ // bundled feature screens for the docs previews.
8
8
  //
9
- // globalRoles SystemAdmin: schaltet die Plattform-Screens frei (tenant-list,
10
- // user-list, tier-admin, GDPR-Inspector). anonymousAccess + extraContext.text
11
- // Content: die öffentlichen legal-pages-Routen (/legal/*).
9
+ // globalRoles SystemAdmin: unlocks the platform screens (tenant-list,
10
+ // user-list, tier-admin, GDPR inspector). anonymousAccess + extraContext.text
11
+ // content: the public legal-pages routes (/legal/*).
12
12
 
13
13
  import { createTemplateResolverApi } from "@cosmicdrift/kumiko-bundled-features/template-resolver";
14
14
  import type { RunDevAppOptions } from "@cosmicdrift/kumiko-dev-server";
@@ -21,10 +21,10 @@ import { notesFeature } from "./notes-feature";
21
21
  import { appScreensFeature } from "./screens-feature";
22
22
  import { seedScreenshotData } from "./seed";
23
23
 
24
- // tier-admin rendert mit dem bare createTierEngineFeature() (aus APP_FEATURES) den
25
- // ehrlichen Empty-State ("no TierMap configured"). Eine konfigurierte tierMap
26
- // würde den Resolver auf jedem Write aktivieren (inkl. Login) und braucht
27
- // per-Tenant Tier-Assignments — disproportional für einen Preview-Screenshot.
24
+ // tier-admin renders the honest empty state ("no TierMap configured") with the
25
+ // bare createTierEngineFeature() (from APP_FEATURES). A configured tierMap
26
+ // would activate the resolver on every write (incl. login) and needs
27
+ // per-tenant tier assignments — disproportionate for a preview screenshot.
28
28
 
29
29
  export const devAppOptions = {
30
30
  // notesFeature is dev-only (app-owned tag host) — see notes-feature.ts for why
@@ -1,9 +1,9 @@
1
1
  // @runtime dev
2
- // Seed-Daten für die Feature-Reference-Screenshots: damit die entity-Listen
3
- // nicht leer rendern und die öffentlichen Legal-Routes Inhalt haben. Läuft
4
- // nach dem Admin (runDevApp seeds-Hook), idempotent über die seed-Helper.
5
- // Dev-only (importiert die dev-only notes-feature) — wird nur von server.ts
6
- // (runDevApp) als Screenshot-Seed genutzt, nie im Prod-Boot.
2
+ // Seed data for the feature reference screenshots: so the entity lists
3
+ // do not render empty and the public legal routes have content. Runs
4
+ // after the admin (runDevApp seeds hook), idempotent via the seed helpers.
5
+ // Dev-only (imports the dev-only notes feature) — used only by server.ts
6
+ // (runDevApp) as the screenshot seed, never in the prod boot.
7
7
 
8
8
  import { seedPage } from "@cosmicdrift/kumiko-bundled-features/managed-pages/seeding";
9
9
  import { NotesHistoryHandlers } from "@cosmicdrift/kumiko-bundled-features/notes-history";
@@ -329,7 +329,7 @@ export const seedScreenshotData: SeedFn = async (stack) => {
329
329
  const devTenant = DEV_TENANT_ID as TenantId;
330
330
  const betaTenant = BETA_TENANT_ID as TenantId;
331
331
 
332
- // managed-pages — zwei Seiten im Dev-Tenant für den page-list Screen.
332
+ // managed-pages — two pages in the dev tenant for the page-list screen.
333
333
  await seedPage(stack.db, {
334
334
  tenantId: devTenant,
335
335
  slug: "about",
@@ -1,29 +1,29 @@
1
1
  // kumiko-feature-version: 1
2
2
  //
3
- // todos — Demo-Domain-Feature, das ueber EXT_USER_DATA in user-data-
4
- // rights integriert. Ein App-Author registriert pro Domain-Entity einen
5
- // (export, delete)-Hook — das war es. Forget-Cron, Export-Bundle und
6
- // DSGVO-Endpoints kommen vollstaendig aus user-data-rights.
3
+ // todos — demo domain feature that integrates into user-data-
4
+ // rights via EXT_USER_DATA. An app author registers one
5
+ // (export, delete) hook per domain entity — that's it. Forget cron, export bundle and
6
+ // GDPR endpoints come entirely from user-data-rights.
7
7
  //
8
- // Dieses Feature zeigt:
9
- // - r.entity({ name: "todo", ...todoEntity }) → Drizzle-Tabelle wird gebaut
10
- // - r.writeHandler({ name: "create", … }) → User legt Todo an
11
- // - r.queryHandler({ name: "list", … }) → User sieht seine Todos
12
- // - r.useExtension(EXT_USER_DATA, "todo") → Forget + Export integration
8
+ // This feature shows:
9
+ // - r.entity({ name: "todo", ...todoEntity }) → Drizzle table is built
10
+ // - r.writeHandler({ name: "create", … }) → user creates a todo
11
+ // - r.queryHandler({ name: "list", … }) → user sees their todos
12
+ // - r.useExtension(EXT_USER_DATA, "todo") → forget + export integration
13
13
  //
14
- // Was passiert wenn der User dann request-export aufruft:
15
- // 1. user-data-rights.request-export.write triggert einen Job
16
- // 2. Worker iteriert alle EXT_USER_DATA-Provider (user, fileRef, todo)
17
- // 3. todoExportHook liest alle Rows mit author_id = userId aus
18
- // ALLEN Tenants des Users
19
- // 4. Bundle wird als ZIP an einen signed-Magic-Link gepackt + per
20
- // Email verschickt
14
+ // What happens when the user then calls request-export:
15
+ // 1. user-data-rights.request-export.write triggers a job
16
+ // 2. The worker iterates all EXT_USER_DATA providers (user, fileRef, todo)
17
+ // 3. todoExportHook reads all rows with author_id = userId from
18
+ // ALL of the user's tenants
19
+ // 4. The bundle is packed as a ZIP behind a signed magic link and sent by
20
+ // email
21
21
  //
22
- // Was passiert wenn der User request-deletion aufruft:
23
- // 1. user-data-rights setzt status=DeletionRequested + grace
24
- // 2. Nach Ablauf laeuft der run-forget-cleanup-Cron
25
- // 3. todoDeleteHook DELETEt alle Rows mit author_id = userId
26
- // 4. user wird anonymisiert (display_name="(deleted)", email=null)
22
+ // What happens when the user calls request-deletion:
23
+ // 1. user-data-rights sets status=DeletionRequested + grace
24
+ // 2. After it expires the run-forget-cleanup cron runs
25
+ // 3. todoDeleteHook DELETEs all rows with author_id = userId
26
+ // 4. The user is anonymized (display_name="(deleted)", email=null)
27
27
 
28
28
  import { UserDataRightsScreens } from "@cosmicdrift/kumiko-bundled-features/user-data-rights";
29
29
  import {
@@ -51,8 +51,8 @@ export const todoEntity = createEntity({
51
51
  table: "store_todos",
52
52
  idType: "uuid",
53
53
  fields: {
54
- // nullable: bei DSGVO-anonymize wird authorId auf null gesetzt
55
- // (Row bleibt, Personenbezug raus). Pattern matched fileRef.
54
+ // nullable: on GDPR anonymize authorId is set to null
55
+ // (row stays, personal reference removed). Pattern matches fileRef.
56
56
  authorId: createTextField({ personal: "ref" }),
57
57
  title: createTextField({
58
58
  personal: false,
@@ -1,9 +1,9 @@
1
1
  // @runtime test
2
- // Playwright-Config für den workspaces-Sample. Startet den echten dev-
3
- // server als webServer-Fixture (Port aus samples/e2e/e2e-ports.ts).
4
- // Pattern ist 1:1 wie der ui-
5
- // walkthrough-Sample — wenn das Pattern nochmal gebraucht wird, lohnt
6
- // sich eine Extraktion.
2
+ // Playwright config for the workspaces sample. Starts the real dev
3
+ // server as a webServer fixture (port from samples/e2e/e2e-ports.ts).
4
+ // The pattern is 1:1 as in the ui-
5
+ // walkthrough sample — if the pattern is needed again, an extraction
6
+ // is worthwhile.
7
7
 
8
8
  import { defineAppE2eConfig, PLAYWRIGHT_DEMO_ENV } from "@cosmicdrift/kumiko-testing/e2e";
9
9
  import { E2E_PORTS } from "../../e2e/e2e-ports";
@@ -1,6 +1,6 @@
1
- // Reine Konstanten — kein Framework-Import. seed.ts braucht sie auf
2
- // dem Server, client.tsx kann sie ggf. zur Anzeige verwenden, später
3
- // ggf. auch ein E2E-Helper (siehe ui-walkthrough/auth-constants.ts).
1
+ // Pure constants — no framework import. seed.ts needs them on
2
+ // the server, client.tsx may use them for display, later possibly
3
+ // also an E2E helper (see ui-walkthrough/auth-constants.ts).
4
4
 
5
5
  export const ADMIN_EMAIL = "admin@kumiko.dev";
6
6
  export const ADMIN_PASSWORD = "kumiko-admin";
@@ -1,15 +1,15 @@
1
1
  // @runtime dev
2
2
  //
3
- // Dev-server für den workspaces Sample. Auth-Mode aktiv: der Client
4
- // muss sich über den Login-Screen anmelden. WorkspaceShell filtert dann
5
- // nach `user.roles` aus der Session — Admin sieht alle drei, andere
6
- // Rollen nur ihre.
3
+ // Dev server for the workspaces sample. Auth mode active: the client
4
+ // must log in via the login screen. WorkspaceShell then filters
5
+ // by `user.roles` from the session — admin sees all three, other
6
+ // roles only theirs.
7
7
  //
8
- // runDevApp mischt die Standard-Features (config/user/tenant/auth)
9
- // automatisch dazu wenn `auth` gesetzt ist und ruft seedAdmin im
10
- // onAfterSetup. Der Client lädt das Schema selbst über die
11
- // authentifizierte GET /api/schema, kein hand-geschriebener
12
- // clientSchema-Spiegel.
8
+ // runDevApp mixes in the standard features (config/user/tenant/auth)
9
+ // automatically when `auth` is set and calls seedAdmin in
10
+ // onAfterSetup. The client loads the schema itself via the
11
+ // authenticated GET /api/schema, no hand-written
12
+ // clientSchema mirror.
13
13
 
14
14
  import { runDevApp } from "@cosmicdrift/kumiko-dev-server";
15
15
  import type { TenantId } from "@cosmicdrift/kumiko-framework/engine";
@@ -17,17 +17,17 @@ import { demoFeature } from "../features/demo";
17
17
  import { driverFeature } from "../features/demo-driver";
18
18
  import { ADMIN_EMAIL, ADMIN_PASSWORD } from "./auth-constants";
19
19
 
20
- // Ein Dev-Tenant reicht — die Workspaces filtern nach `roles` (nicht
21
- // nach Tenant). Wer Tenant-Switching demoen will, schaut in den
22
- // ui-walkthrough-Sample.
20
+ // One dev tenant is enough — the workspaces filter by `roles` (not
21
+ // by tenant). To demo tenant switching, look at the
22
+ // ui-walkthrough sample.
23
23
  const DEV_TENANT_ID = "00000000-0000-4000-8000-000000000010" as TenantId;
24
24
 
25
25
  import { localeDe } from "@cosmicdrift/kumiko-locale-de";
26
26
 
27
27
  await runDevApp({
28
28
  features: [localeDe(), demoFeature, driverFeature],
29
- // PORT env-var override für Playwright-e2e-Runs (config zeigt auf 4175);
30
- // sonst lokal-Default 4174.
29
+ // PORT env var override for Playwright e2e runs (config points to 4175);
30
+ // otherwise local default 4174.
31
31
  port: Number.parseInt(process.env["PORT"] ?? "4174", 10),
32
32
  clientEntry: "./src/app/client.tsx",
33
33
  htmlPath: "./public/index.html",
@@ -37,8 +37,8 @@ await runDevApp({
37
37
  email: ADMIN_EMAIL,
38
38
  password: ADMIN_PASSWORD,
39
39
  displayName: "Admin",
40
- // Admin-Rolle gibt Zugriff auf alle drei Workspaces (admin/dispatch/
41
- // driver). Realwelt-Apps würden weitere Rollen + User definieren.
40
+ // The admin role grants access to all three workspaces (admin/dispatch/
41
+ // driver). Real-world apps would define further roles + users.
42
42
  memberships: [
43
43
  { tenantId: DEV_TENANT_ID, tenantKey: "dev", tenantName: "Dev Tenant", roles: ["Admin"] },
44
44
  ],
@@ -1,5 +1,5 @@
1
- // AppShell mit WorkspaceShell statt DefaultAppShell — Multi-Persona-
2
- // App mit URL-driven Workspace-Switch und Nav-Tree-Filter pro Rolle.
1
+ // AppShell with WorkspaceShell instead of DefaultAppShell — multi-persona
2
+ // app with URL-driven workspace switch and nav tree filter per role.
3
3
 
4
4
  import {
5
5
  DefaultTopbarActions,
@@ -1,7 +1,7 @@
1
- // Entity + Screen-Definitionen. WIRD vom Server in feature.ts via
2
- // r.entity/r.screen registriert; die Browser-seitige Spiegelung
3
- // (clientSchema mit navs/workspaces) ist obsolet — der Client lädt das
4
- // aufgelöste AppSchema selbst über die authentifizierte GET /api/schema.
1
+ // Entity + screen definitions. IS registered by the server in feature.ts via
2
+ // r.entity/r.screen; the browser-side mirror
3
+ // (clientSchema with navs/workspaces) is obsolete — the client loads the
4
+ // resolved AppSchema itself via the authenticated GET /api/schema.
5
5
 
6
6
  import type {
7
7
  EntityDefinition,
@@ -1,8 +1,8 @@
1
- // Cross-Feature-Demo: demo-driver registriert eine Nav die sich
2
- // selber zur driver-Workspace von `demo` zuweist. Beweist dass
3
- // r.nav.workspaces QNs über Feature-Grenzen hinweg auflöst — nützlich
4
- // für Teams die pro Persona ein eigenes Package haben und die an einen
5
- // gemeinsamen Core anflanschen.
1
+ // Cross-feature demo: demo-driver registers a nav that assigns
2
+ // itself to the driver workspace of `demo`. Proves that
3
+ // r.nav.workspaces resolves QNs across feature boundaries — useful
4
+ // for teams that have their own package per persona and dock onto a
5
+ // shared core.
6
6
 
7
7
  import { defineFeature, type FeatureDefinition } from "@cosmicdrift/kumiko-framework/engine";
8
8
  import type { TranslationValue } from "@cosmicdrift/kumiko-framework/ui-types";