@cosmicdrift/kumiko-samples 0.365.1 → 0.366.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/package.json +1 -1
- package/packages/bundled-features/package.json +1 -1
- package/packages/bundled-features/src/admin-shell/feature.ts +9 -9
- package/packages/bundled-features/src/agent-tools/changes.json +6 -0
- package/packages/bundled-features/src/agent-tools/tool-dispatch.ts +87 -5
- package/packages/bundled-features/src/audit/feature.ts +1 -1
- package/packages/bundled-features/src/auth-email-password/changes.json +13 -0
- package/packages/bundled-features/src/auth-email-password/constants.ts +0 -2
- package/packages/bundled-features/src/auth-email-password/feature.ts +3 -3
- package/packages/bundled-features/src/auth-email-password/handlers/invite-create.write.ts +1 -0
- package/packages/bundled-features/src/auth-mfa/feature.ts +4 -4
- package/packages/bundled-features/src/auth-mfa-user-data/index.ts +1 -1
- package/packages/bundled-features/src/billing-foundation/consumer-protection/register-consent.ts +1 -1
- package/packages/bundled-features/src/billing-foundation/feature.ts +1 -1
- package/packages/bundled-features/src/cap-counter/cap-reservation.ts +89 -28
- package/packages/bundled-features/src/cap-counter/changes.json +7 -0
- package/packages/bundled-features/src/cap-counter/constants.ts +3 -0
- package/packages/bundled-features/src/cap-counter/feature.ts +2 -2
- package/packages/bundled-features/src/cap-counter/tables.ts +5 -2
- package/packages/bundled-features/src/cap-overview/feature.ts +3 -3
- package/packages/bundled-features/src/channel-discord/feature.ts +2 -2
- package/packages/bundled-features/src/channel-email/feature.ts +1 -1
- package/packages/bundled-features/src/channel-in-app/feature.ts +1 -1
- package/packages/bundled-features/src/channel-push/feature.ts +1 -1
- package/packages/bundled-features/src/channel-slack/feature.ts +2 -2
- package/packages/bundled-features/src/channel-teams/feature.ts +2 -2
- package/packages/bundled-features/src/channel-telegram/feature.ts +2 -2
- package/packages/bundled-features/src/compliance-profiles/feature.ts +1 -1
- package/packages/bundled-features/src/compliance-profiles-ops/index.ts +3 -3
- package/packages/bundled-features/src/config/feature.ts +1 -1
- package/packages/bundled-features/src/crypto-shredding/changes.json +6 -0
- package/packages/bundled-features/src/custom-fields/feature.ts +2 -2
- package/packages/bundled-features/src/custom-fields/wire-for-entity.ts +17 -13
- package/packages/bundled-features/src/data-retention/feature.ts +1 -1
- package/packages/bundled-features/src/data-retention/presets.ts +4 -4
- package/packages/bundled-features/src/delivery/changes.json +6 -0
- package/packages/bundled-features/src/delivery/db/queries/address-opt-outs.ts +6 -2
- package/packages/bundled-features/src/delivery/db/queries/preferences.ts +4 -4
- package/packages/bundled-features/src/delivery/delivery-service.ts +17 -2
- package/packages/bundled-features/src/delivery/feature.ts +9 -5
- package/packages/bundled-features/src/delivery/handlers/resubscribe-address.write.ts +12 -8
- package/packages/bundled-features/src/delivery/handlers/resubscribe-user.write.ts +3 -1
- package/packages/bundled-features/src/delivery/handlers/set-preference.write.ts +3 -1
- package/packages/bundled-features/src/delivery/handlers/unsubscribe-address.write.ts +3 -1
- package/packages/bundled-features/src/delivery/handlers/unsubscribe-user.write.ts +3 -1
- package/packages/bundled-features/src/delivery/notification-type-aliases.ts +13 -0
- package/packages/bundled-features/src/derivatives-sharp/feature.ts +1 -1
- package/packages/bundled-features/src/document-ingest-foundation/feature.ts +2 -2
- package/packages/bundled-features/src/file-derivatives/feature.ts +3 -3
- package/packages/bundled-features/src/file-foundation/feature.ts +1 -1
- package/packages/bundled-features/src/file-provider-inmemory/feature.ts +2 -2
- package/packages/bundled-features/src/file-provider-s3/feature.ts +3 -3
- package/packages/bundled-features/src/file-provider-s3-env/feature.ts +2 -2
- package/packages/bundled-features/src/files/README.md +1 -1
- package/packages/bundled-features/src/files-tenant-data/index.ts +1 -1
- package/packages/bundled-features/src/folders/feature.ts +2 -2
- package/packages/bundled-features/src/folders-user-data/index.ts +1 -1
- package/packages/bundled-features/src/form-draft/feature.ts +2 -2
- package/packages/bundled-features/src/form-draft-user-data/index.ts +1 -1
- package/packages/bundled-features/src/inbound-mail-foundation/changes.json +13 -0
- package/packages/bundled-features/src/inbound-mail-foundation/connect-routes.ts +9 -1
- package/packages/bundled-features/src/inbound-mail-foundation/feature.ts +1 -1
- package/packages/bundled-features/src/inbound-mail-foundation/types.ts +19 -7
- package/packages/bundled-features/src/inbound-mail-foundation/watch-supervisor.ts +21 -2
- package/packages/bundled-features/src/inbound-provider-imap/feature.ts +2 -2
- package/packages/bundled-features/src/inbound-provider-inmemory/feature.ts +1 -1
- package/packages/bundled-features/src/ledger/feature.ts +5 -5
- package/packages/bundled-features/src/legal-pages/feature.ts +1 -1
- package/packages/bundled-features/src/mail-foundation/feature.ts +1 -1
- package/packages/bundled-features/src/mail-transport-inmemory/feature.ts +2 -2
- package/packages/bundled-features/src/mail-transport-smtp/feature.ts +3 -3
- package/packages/bundled-features/src/managed-pages/css-gate.ts +1 -1
- package/packages/bundled-features/src/managed-pages/feature.ts +2 -2
- package/packages/bundled-features/src/metrics/feature.ts +2 -2
- package/packages/bundled-features/src/notes-history/feature.ts +2 -2
- package/packages/bundled-features/src/notes-history-user-data/index.ts +1 -1
- package/packages/bundled-features/src/personal-access-tokens/feature.ts +15 -11
- package/packages/bundled-features/src/readiness/feature.ts +2 -2
- package/packages/bundled-features/src/renderer-foundation/README.md +1 -1
- package/packages/bundled-features/src/renderer-foundation/feature.ts +1 -1
- package/packages/bundled-features/src/renderer-pdf/feature.ts +1 -1
- package/packages/bundled-features/src/secrets/feature.ts +1 -1
- package/packages/bundled-features/src/seo/feature.ts +1 -1
- package/packages/bundled-features/src/sessions/feature.ts +17 -13
- package/packages/bundled-features/src/step-dispatcher/feature.ts +1 -1
- package/packages/bundled-features/src/subscription-mollie/feature.ts +1 -1
- package/packages/bundled-features/src/subscription-stripe/feature.ts +3 -3
- package/packages/bundled-features/src/tags/feature.ts +2 -2
- package/packages/bundled-features/src/template-resolver/feature.ts +2 -2
- package/packages/bundled-features/src/template-resolver-user-data/index.ts +1 -1
- package/packages/bundled-features/src/tenant/feature.ts +4 -4
- package/packages/bundled-features/src/tenant/seeding.ts +1 -1
- package/packages/bundled-features/src/tenant-lifecycle/feature.ts +2 -3
- package/packages/bundled-features/src/tenant-settings/feature.ts +1 -1
- package/packages/bundled-features/src/tier-engine/feature.ts +36 -27
- package/packages/bundled-features/src/user/feature.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/README.md +1 -1
- package/packages/bundled-features/src/user-data-rights/feature.ts +4 -4
- package/packages/bundled-features/src/user-data-rights-defaults/feature.ts +2 -2
- package/packages/bundled-features/src/user-profile/feature.ts +4 -4
- package/packages/bundled-features/src/waitlist/feature.ts +2 -2
- package/samples/apps/cap-billing-demo/README.md +1 -1
- package/samples/apps/cap-billing-demo/src/feature.ts +4 -4
- package/samples/apps/cap-billing-demo/src/run-config.ts +1 -1
- package/samples/apps/config-demo/src/features/demo/feature.ts +2 -2
- package/samples/apps/styleguide/src/features/ai-settings/feature.ts +43 -28
- package/samples/apps/styleguide/src/features/demo/feature.ts +18 -18
- package/samples/apps/styleguide/src/features/widgets/ai-text-demo.ts +10 -12
- package/samples/apps/styleguide/src/features/widgets/feature.ts +90 -108
- package/samples/apps/ui-walkthrough/src/features/rental/feature.ts +36 -36
- package/samples/apps/ui-walkthrough/src/features/vehicles/feature.ts +6 -6
- package/samples/apps/use-all-bundled/e2e-billing-gallery/gallery-features.ts +2 -2
- package/samples/apps/use-all-bundled/scripts/gen-sample-index.ts +7 -7
- package/samples/apps/use-all-bundled/src/app/notes-feature.ts +3 -3
- package/samples/apps/use-all-bundled/src/app/screens-feature.ts +1 -1
- package/samples/apps/user-data-rights-demo/src/feature.ts +5 -5
- package/samples/recipes/access-control/src/feature.ts +21 -25
- package/samples/recipes/anonymous-access/src/feature.ts +36 -43
- package/samples/recipes/anonymous-access-multitenant/src/feature.ts +14 -16
- package/samples/recipes/auth-foundation-providers/src/feature.ts +1 -1
- package/samples/recipes/basic-entity/README.md +1 -1
- package/samples/recipes/boot-check/README.md +1 -1
- package/samples/recipes/boot-check/src/feature.ts +2 -2
- package/samples/recipes/chat-channels/src/feature.ts +1 -1
- package/samples/recipes/cross-feature-events/src/feature.ts +7 -7
- package/samples/recipes/currencies-global/src/feature.ts +3 -3
- package/samples/recipes/currencies-per-tenant/src/feature.ts +2 -2
- package/samples/recipes/custom-fields-basic/README.md +1 -1
- package/samples/recipes/custom-fields-basic/src/feature.ts +3 -2
- package/samples/recipes/custom-handlers/README.md +3 -3
- package/samples/recipes/custom-handlers/src/feature.ts +37 -41
- package/samples/recipes/dashboard-stats/src/feature.ts +6 -6
- package/samples/recipes/delivery-notifications/README.md +1 -1
- package/samples/recipes/delivery-notifications/src/feature.ts +8 -8
- package/samples/recipes/designer-demo/src/feature.ts +2 -1
- package/samples/recipes/embedded/src/feature.ts +1 -1
- package/samples/recipes/embedded-entity-form/src/feature.ts +13 -13
- package/samples/recipes/encrypted-tenant-config/src/feature.ts +1 -1
- package/samples/recipes/error-contract/src/feature.ts +25 -25
- package/samples/recipes/event-sourcing/src/feature.ts +70 -76
- package/samples/recipes/feature-toggles/src/feature.ts +28 -20
- package/samples/recipes/files-post-processing/src/feature.ts +22 -26
- package/samples/recipes/idempotency/src/feature.ts +16 -18
- package/samples/recipes/inbound-mail-triage/src/feature.ts +1 -1
- package/samples/recipes/lane-routing/src/feature.ts +10 -12
- package/samples/recipes/legal-pages/src/feature.ts +1 -1
- package/samples/recipes/lifecycle-hooks/README.md +4 -4
- package/samples/recipes/lifecycle-hooks/src/feature.ts +55 -35
- package/samples/recipes/managed-config/src/feature.ts +8 -8
- package/samples/recipes/managed-pages/src/feature.ts +1 -1
- package/samples/recipes/notes-history-basic/README.md +1 -1
- package/samples/recipes/notes-history-basic/src/feature.ts +2 -2
- package/samples/recipes/pdf-audit-export/src/feature.ts +7 -7
- package/samples/recipes/pipeline-basics/README.md +1 -1
- package/samples/recipes/pipeline-basics/src/feature.ts +7 -7
- package/samples/recipes/public-share-token/src/feature.ts +1 -1
- package/samples/recipes/rate-limiting/src/feature.ts +7 -9
- package/samples/recipes/record-detail-layout/src/feature.ts +18 -18
- package/samples/recipes/reference-data/src/feature.ts +1 -1
- package/samples/recipes/relations/src/feature.ts +2 -2
- package/samples/recipes/screens-nav/src/feature.ts +1 -1
- package/samples/recipes/search/src/feature.ts +17 -19
- package/samples/recipes/security-baseline/README.md +1 -1
- package/samples/recipes/state-machine/src/feature.ts +1 -1
- package/samples/recipes/tags-basic/README.md +1 -1
- package/samples/recipes/tags-basic/src/feature.ts +2 -2
- package/samples/recipes/tenant-settings/README.md +1 -1
- package/samples/recipes/tenant-settings/src/feature.ts +1 -1
- package/samples/recipes/timezones/src/feature.ts +12 -12
- package/samples/recipes/user-data-rights/README.md +2 -2
- package/samples/recipes/user-data-rights/src/feature.ts +1 -1
- package/samples/recipes/user-profile/src/feature.ts +1 -1
- package/samples/recipes/webhook-step/src/feature.ts +1 -1
- package/samples/recipes/writeform-section/src/feature.ts +12 -12
|
@@ -206,10 +206,10 @@ export function createTierEngineFeature<
|
|
|
206
206
|
category: "billing",
|
|
207
207
|
recommended: false,
|
|
208
208
|
});
|
|
209
|
-
r.requires("config");
|
|
210
|
-
r.requires("tenant");
|
|
209
|
+
r.requires({ features: ["config"] });
|
|
210
|
+
r.requires({ features: ["tenant"] });
|
|
211
211
|
|
|
212
|
-
r.entity("tier-assignment", tierAssignmentEntity);
|
|
212
|
+
r.entity({ name: "tier-assignment", ...tierAssignmentEntity });
|
|
213
213
|
|
|
214
214
|
// Standard-CRUD via Helper.
|
|
215
215
|
r.writeHandler(
|
|
@@ -369,25 +369,33 @@ export function createTierEngineFeature<
|
|
|
369
369
|
onTierAssigned.fn = applyAssignment;
|
|
370
370
|
|
|
371
371
|
// Invalidation: tier-assignment CRUD events (e.g. the billing webhook) update the cache.
|
|
372
|
-
r.hook(
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
372
|
+
r.hook({
|
|
373
|
+
type: "postSave",
|
|
374
|
+
target: { allOf: "tier-assignment" },
|
|
375
|
+
handler: async (result) => {
|
|
376
|
+
// result.data has tenantId + tier (after entity-update merge)
|
|
377
|
+
const data = result.data as { tenantId?: unknown; tier?: unknown }; // @cast-boundary engine-payload
|
|
378
|
+
// skip: defensive guard on the payload shape. Correct entity events
|
|
379
|
+
// always carry both fields as strings; a malformed payload (a custom
|
|
380
|
+
// handler bug) skips the cache update instead of throwing, because a
|
|
381
|
+
// hook error must not block the lifecycle pipeline (afterCommit,
|
|
382
|
+
// best-effort side effect).
|
|
383
|
+
if (typeof data.tenantId !== "string" || typeof data.tier !== "string") return;
|
|
384
|
+
applyAssignment(data.tenantId as TenantId, data.tier);
|
|
385
|
+
},
|
|
382
386
|
});
|
|
383
|
-
r.hook(
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
387
|
+
r.hook({
|
|
388
|
+
type: "postDelete",
|
|
389
|
+
target: { allOf: "tier-assignment" },
|
|
390
|
+
handler: async (payload) => {
|
|
391
|
+
const data = payload.data as { tenantId?: unknown }; // @cast-boundary engine-payload
|
|
392
|
+
// skip: gleiche type-guard semantik wie postSave-hook oben.
|
|
393
|
+
if (typeof data.tenantId !== "string") return;
|
|
394
|
+
const tenantId = data.tenantId as TenantId;
|
|
395
|
+
takeTicket(tenantId);
|
|
396
|
+
cache.delete(tenantId);
|
|
397
|
+
publishAssignmentChange(tenantId);
|
|
398
|
+
},
|
|
391
399
|
});
|
|
392
400
|
|
|
393
401
|
// Auto-default-tier-on-tenant-signup: hook fires inTransaction (atomic
|
|
@@ -406,10 +414,10 @@ export function createTierEngineFeature<
|
|
|
406
414
|
const defaultTier = opts.defaultTier;
|
|
407
415
|
const autoDefaultTierHookReason =
|
|
408
416
|
"seeds the default tier-assignment stream of the tenant just created";
|
|
409
|
-
r.hook(
|
|
410
|
-
"postSave",
|
|
411
|
-
{ allOf: "tenant" },
|
|
412
|
-
async (result, ctx) => {
|
|
417
|
+
r.hook({
|
|
418
|
+
type: "postSave",
|
|
419
|
+
target: { allOf: "tenant" },
|
|
420
|
+
handler: async (result, ctx) => {
|
|
413
421
|
// result-shape: kumiko-framework's SaveContext mit isNew + data
|
|
414
422
|
const saveResult = result as { isNew?: unknown; data?: unknown }; // @cast-boundary engine-payload
|
|
415
423
|
// skip: nur bei tenant-create (initial) — tenant-updates feuern
|
|
@@ -462,8 +470,9 @@ export function createTierEngineFeature<
|
|
|
462
470
|
tdb,
|
|
463
471
|
);
|
|
464
472
|
},
|
|
465
|
-
|
|
466
|
-
|
|
473
|
+
phase: HookPhases.inTransaction,
|
|
474
|
+
escapeHatch: { reason: autoDefaultTierHookReason },
|
|
475
|
+
});
|
|
467
476
|
}
|
|
468
477
|
|
|
469
478
|
// Extension-point declaration + self-registration. Pattern analog
|
|
@@ -28,7 +28,7 @@ export function createUserFeature(): FeatureDefinition {
|
|
|
28
28
|
recommended: true,
|
|
29
29
|
});
|
|
30
30
|
r.systemScope();
|
|
31
|
-
r.entity("user", userEntity);
|
|
31
|
+
r.entity({ name: "user", ...userEntity });
|
|
32
32
|
|
|
33
33
|
// Self-extension: `user` declares AND fulfils principalStatus (precedent:
|
|
34
34
|
// tier-engine/feature.ts) — wires the blocked-principal check into every stack that mounts `user`.
|
|
@@ -14,7 +14,7 @@ an. user-data-rights orchestriert Export- und Forget-Pipeline:
|
|
|
14
14
|
|
|
15
15
|
```ts
|
|
16
16
|
defineFeature("tasks", (r) => {
|
|
17
|
-
r.requires("user-data-rights");
|
|
17
|
+
r.requires({ features: ["user-data-rights"] });
|
|
18
18
|
r.useExtension(EXT_USER_DATA, "task", {
|
|
19
19
|
export: async (ctx) => ({
|
|
20
20
|
entity: "task",
|
|
@@ -197,7 +197,7 @@ export function createUserDataRightsFeature(opts: UserDataRightsOptions = {}): F
|
|
|
197
197
|
category: "compliance",
|
|
198
198
|
recommended: false,
|
|
199
199
|
});
|
|
200
|
-
r.requires("user", "data-retention", "compliance-profiles", "sessions");
|
|
200
|
+
r.requires({ features: ["user", "data-retention", "compliance-profiles", "sessions"] });
|
|
201
201
|
r.usesApi("compliance.forTenant");
|
|
202
202
|
r.usesApi("retention.policyFor");
|
|
203
203
|
// S2.U6 — restrict-account ruft sessions.revokeAllForUser cross-feature.
|
|
@@ -242,15 +242,15 @@ export function createUserDataRightsFeature(opts: UserDataRightsOptions = {}): F
|
|
|
242
242
|
);
|
|
243
243
|
|
|
244
244
|
// S2.U3 Atom 1b — ExportJob-Lifecycle-Entity.
|
|
245
|
-
r.entity("export-job", exportJobEntity);
|
|
245
|
+
r.entity({ name: "export-job", ...exportJobEntity });
|
|
246
246
|
|
|
247
247
|
// S2.U3 Atom 4a — Download-Token-Entity. Worker generiert Token beim
|
|
248
248
|
// Flip auf done (siehe run-export-jobs.ts). Hash in DB, plain im
|
|
249
249
|
// RunExportJobsResult fuer Atom 5 (Notification per Email).
|
|
250
|
-
r.entity("export-download-token", exportDownloadTokenEntity);
|
|
250
|
+
r.entity({ name: "export-download-token", ...exportDownloadTokenEntity });
|
|
251
251
|
|
|
252
252
|
// S2.U7 — Audit-Trail invalid Download-Attempts (DPO Brute-Force-Detection).
|
|
253
|
-
r.entity("download-attempt", downloadAttemptEntity);
|
|
253
|
+
r.entity({ name: "download-attempt", ...downloadAttemptEntity });
|
|
254
254
|
|
|
255
255
|
// S2.U6 — DSGVO Art. 18 Account-Freeze (Verarbeitungs-Pause).
|
|
256
256
|
// Endpoints fuer Restrict + Lift. Login-Block fuer Restricted Users
|
|
@@ -40,7 +40,7 @@ import {
|
|
|
40
40
|
// `user` (S2.H1) und `fileRef` (S2.H2).
|
|
41
41
|
//
|
|
42
42
|
// Architektur-Entscheidung (S2.H1+H2): user-data-rights selbst kann
|
|
43
|
-
// nicht r.requires("user", "files") + r.useExtension(EXT_USER_DATA, ...)
|
|
43
|
+
// nicht r.requires({ features: ["user", "files"] }) + r.useExtension(EXT_USER_DATA, ...)
|
|
44
44
|
// machen weil es selbst Provider von EXT_USER_DATA ist (Boot-Validator
|
|
45
45
|
// lehnt self-extension ab). Lösung: drittes optional-mountbares Feature
|
|
46
46
|
// das requires beide Sources + die useExtension-Calls macht.
|
|
@@ -68,7 +68,7 @@ export function createUserDataRightsDefaultsFeature(): FeatureDefinition {
|
|
|
68
68
|
category: "compliance",
|
|
69
69
|
recommended: false,
|
|
70
70
|
});
|
|
71
|
-
r.requires("user", "files", "user-data-rights");
|
|
71
|
+
r.requires({ features: ["user", "files", "user-data-rights"] });
|
|
72
72
|
// Optional sources: hooks are registered unconditionally but each one
|
|
73
73
|
// no-ops at runtime when its source feature isn't mounted (see
|
|
74
74
|
// hooks/feature-mounted.ts) — the export runner has no per-hook
|
|
@@ -41,10 +41,10 @@ export function createUserProfileFeature(options?: UserProfileFeatureOptions): F
|
|
|
41
41
|
category: "identity",
|
|
42
42
|
recommended: true,
|
|
43
43
|
});
|
|
44
|
-
r.requires("user");
|
|
45
|
-
r.requires("auth-email-password");
|
|
46
|
-
r.requires("user-data-rights");
|
|
47
|
-
r.requires("user-data-rights-defaults");
|
|
44
|
+
r.requires({ features: ["user"] });
|
|
45
|
+
r.requires({ features: ["auth-email-password"] });
|
|
46
|
+
r.requires({ features: ["user-data-rights"] });
|
|
47
|
+
r.requires({ features: ["user-data-rights-defaults"] });
|
|
48
48
|
|
|
49
49
|
const handlers = allowEmailChange ? { changeEmail: r.writeHandler(changeEmailWrite) } : {};
|
|
50
50
|
|
|
@@ -30,7 +30,7 @@ export function createWaitlistFeature(opts: WaitlistOptions = {}): FeatureDefini
|
|
|
30
30
|
);
|
|
31
31
|
r.uiHints({ displayLabel: "Waitlist", category: "data", recommended: false });
|
|
32
32
|
r.systemScope();
|
|
33
|
-
r.requires("tenant", "auth-email-password", "user-data-rights");
|
|
33
|
+
r.requires({ features: ["tenant", "auth-email-password", "user-data-rights"] });
|
|
34
34
|
r.translations({ keys: WAITLIST_I18N });
|
|
35
35
|
|
|
36
36
|
r.notification("confirmation", {
|
|
@@ -43,7 +43,7 @@ export function createWaitlistFeature(opts: WaitlistOptions = {}): FeatureDefini
|
|
|
43
43
|
localizedTemplate: waitlistAdminNoticeTemplate,
|
|
44
44
|
});
|
|
45
45
|
|
|
46
|
-
r.entity("waitlistEntry", waitlistEntryEntity);
|
|
46
|
+
r.entity({ name: "waitlistEntry", ...waitlistEntryEntity });
|
|
47
47
|
r.writeHandler(createSubmitHandler(opts));
|
|
48
48
|
r.writeHandler(createInviteHandler(opts.invite));
|
|
49
49
|
r.writeHandler(rejectHandler);
|
|
@@ -102,7 +102,7 @@ In the browser, use the Designer/Admin UI to set the config key
|
|
|
102
102
|
there's no HTTP endpoint for it because the sample shows the
|
|
103
103
|
architecture, not an inbox UI.
|
|
104
104
|
|
|
105
|
-
If you want clickable: write a small `r.queryHandler("inbox:list")`
|
|
105
|
+
If you want clickable: write a small `r.queryHandler({ name: "inbox:list", … })`
|
|
106
106
|
that returns `getInbox(ctx.user.tenantId)`. ~20 LOC, deliberately
|
|
107
107
|
omitted to keep the focus on cap+tier.
|
|
108
108
|
|
|
@@ -183,10 +183,10 @@ export const newsletterFeature = defineFeature(FEATURE_NAME, (r) => {
|
|
|
183
183
|
// Hard-deps: mail-foundation für Plugin-API + config für tier-Wahl +
|
|
184
184
|
// cap-counter (transitiv via withCapEnforcement, aber explicit ist
|
|
185
185
|
// klarer für boot-Validator-Errors).
|
|
186
|
-
r.requires("config");
|
|
187
|
-
r.requires("cap-counter");
|
|
188
|
-
r.requires(mailFoundationFeature.name);
|
|
189
|
-
r.requires(BILLING_FOUNDATION_FEATURE);
|
|
186
|
+
r.requires({ features: ["config"] });
|
|
187
|
+
r.requires({ features: ["cap-counter"] });
|
|
188
|
+
r.requires({ features: [mailFoundationFeature.name] });
|
|
189
|
+
r.requires({ features: [BILLING_FOUNDATION_FEATURE] });
|
|
190
190
|
|
|
191
191
|
// Tier-config-key. Tenant-Admin setzt's; default "free".
|
|
192
192
|
r.config(
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Single source of truth für die Feature-Komposition der Demo-App.
|
|
2
2
|
//
|
|
3
3
|
// **Mount-Order matters:** mail-foundation MUSS vor mail-transport-
|
|
4
|
-
// inmemory stehen (`r.requires("mail-foundation")` im Plugin); das
|
|
4
|
+
// inmemory stehen (`r.requires({ features: ["mail-foundation"] })` im Plugin); das
|
|
5
5
|
// newsletter-feature kommt danach (es nutzt mail-foundation +
|
|
6
6
|
// cap-counter). composeFeatures (auth-mode) ergänzt config + secrets +
|
|
7
7
|
// auth-email-password automatisch.
|
|
@@ -37,8 +37,8 @@ const REQUIRED_I18N = {
|
|
|
37
37
|
// mask.title ist der i18n-Key des Feld-Labels, mask.order die Reihenfolge.
|
|
38
38
|
export const configDemoFeature = defineFeature("config-demo", (r) => {
|
|
39
39
|
r.translations({ keys: REQUIRED_I18N });
|
|
40
|
-
r.requires("config");
|
|
41
|
-
r.requires("secrets");
|
|
40
|
+
r.requires({ features: ["config"] });
|
|
41
|
+
r.requires({ features: ["secrets"] });
|
|
42
42
|
|
|
43
43
|
r.config({
|
|
44
44
|
keys: {
|
|
@@ -53,14 +53,14 @@ function isProvider(value: unknown): value is Provider {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
export const aiSettingsFeature = defineFeature("ai-settings", (r) => {
|
|
56
|
-
r.requires("config");
|
|
56
|
+
r.requires({ features: ["config"] });
|
|
57
57
|
r.translations({ keys: AI_SETTINGS_I18N });
|
|
58
58
|
|
|
59
59
|
r.crud("connection", connectionEntity, { write: open, read: openRead });
|
|
60
|
-
r.queryHandler(
|
|
61
|
-
"connection-options",
|
|
62
|
-
noPayload,
|
|
63
|
-
async (query, ctx) => {
|
|
60
|
+
r.queryHandler({
|
|
61
|
+
name: "connection-options",
|
|
62
|
+
schema: noPayload,
|
|
63
|
+
handler: async (query, ctx) => {
|
|
64
64
|
const page = await executor.list({ limit: 100 }, query.user, ctx.db);
|
|
65
65
|
return {
|
|
66
66
|
rows: page.rows.map((row) => ({
|
|
@@ -69,28 +69,33 @@ export const aiSettingsFeature = defineFeature("ai-settings", (r) => {
|
|
|
69
69
|
})),
|
|
70
70
|
};
|
|
71
71
|
},
|
|
72
|
-
openRead,
|
|
73
|
-
);
|
|
72
|
+
...openRead,
|
|
73
|
+
});
|
|
74
74
|
|
|
75
|
-
r.queryHandler(
|
|
76
|
-
"model-options",
|
|
77
|
-
connectionIdPayload,
|
|
78
|
-
async (query, ctx) => {
|
|
75
|
+
r.queryHandler({
|
|
76
|
+
name: "model-options",
|
|
77
|
+
schema: connectionIdPayload,
|
|
78
|
+
handler: async (query, ctx) => {
|
|
79
79
|
const { connectionId } = query.payload;
|
|
80
80
|
if (connectionId === undefined) return { rows: [] };
|
|
81
81
|
const row = await executor.detail({ id: connectionId }, query.user, ctx.db);
|
|
82
82
|
const provider = row?.["provider"];
|
|
83
83
|
return { rows: isProvider(provider) ? MODELS_BY_PROVIDER[provider] : [] };
|
|
84
84
|
},
|
|
85
|
-
openRead,
|
|
86
|
-
);
|
|
85
|
+
...openRead,
|
|
86
|
+
});
|
|
87
87
|
|
|
88
|
-
r.queryHandler(
|
|
88
|
+
r.queryHandler({
|
|
89
|
+
name: "step-options",
|
|
90
|
+
schema: noPayload,
|
|
91
|
+
handler: async () => ({ rows: AI_STEPS }),
|
|
92
|
+
...openRead,
|
|
93
|
+
});
|
|
89
94
|
|
|
90
|
-
r.queryHandler(
|
|
91
|
-
"all-model-options",
|
|
92
|
-
noPayload,
|
|
93
|
-
async () => ({
|
|
95
|
+
r.queryHandler({
|
|
96
|
+
name: "all-model-options",
|
|
97
|
+
schema: noPayload,
|
|
98
|
+
handler: async () => ({
|
|
94
99
|
rows: PROVIDERS.flatMap((provider) =>
|
|
95
100
|
MODELS_BY_PROVIDER[provider].map((model) => ({
|
|
96
101
|
...model,
|
|
@@ -98,13 +103,13 @@ export const aiSettingsFeature = defineFeature("ai-settings", (r) => {
|
|
|
98
103
|
})),
|
|
99
104
|
),
|
|
100
105
|
}),
|
|
101
|
-
openRead,
|
|
102
|
-
);
|
|
106
|
+
...openRead,
|
|
107
|
+
});
|
|
103
108
|
|
|
104
|
-
r.queryHandler(
|
|
105
|
-
"connection:summary",
|
|
106
|
-
idPayload,
|
|
107
|
-
async (query, ctx) => {
|
|
109
|
+
r.queryHandler({
|
|
110
|
+
name: "connection:summary",
|
|
111
|
+
schema: idPayload,
|
|
112
|
+
handler: async (query, ctx) => {
|
|
108
113
|
const row = await executor.detail({ id: query.payload.id }, query.user, ctx.db);
|
|
109
114
|
return {
|
|
110
115
|
id: query.payload.id,
|
|
@@ -112,12 +117,22 @@ export const aiSettingsFeature = defineFeature("ai-settings", (r) => {
|
|
|
112
117
|
provider: String(row?.["provider"] ?? ""),
|
|
113
118
|
};
|
|
114
119
|
},
|
|
115
|
-
openRead,
|
|
116
|
-
);
|
|
120
|
+
...openRead,
|
|
121
|
+
});
|
|
117
122
|
|
|
118
123
|
const acknowledged = async () => ({ isSuccess: true as const, data: null });
|
|
119
|
-
r.writeHandler(
|
|
120
|
-
|
|
124
|
+
r.writeHandler({
|
|
125
|
+
name: "step:assign",
|
|
126
|
+
schema: z.object({}).passthrough(),
|
|
127
|
+
handler: acknowledged,
|
|
128
|
+
...open,
|
|
129
|
+
});
|
|
130
|
+
r.writeHandler({
|
|
131
|
+
name: "probe:run",
|
|
132
|
+
schema: z.object({}).passthrough(),
|
|
133
|
+
handler: acknowledged,
|
|
134
|
+
...open,
|
|
135
|
+
});
|
|
121
136
|
|
|
122
137
|
r.config({
|
|
123
138
|
keys: {
|
|
@@ -63,10 +63,10 @@ export const demoFeature = defineFeature("styleguide", (r) => {
|
|
|
63
63
|
// triggers shouldRenderActionsIconOnly) and need their own write handlers.
|
|
64
64
|
const { executor: itemExecutor } = createEntityExecutor("item", demoEntity);
|
|
65
65
|
|
|
66
|
-
r.writeHandler(
|
|
67
|
-
"item:archive",
|
|
68
|
-
z.object({ id: z.uuid() }),
|
|
69
|
-
async (event, ctx) =>
|
|
66
|
+
r.writeHandler({
|
|
67
|
+
name: "item:archive",
|
|
68
|
+
schema: z.object({ id: z.uuid() }),
|
|
69
|
+
handler: async (event, ctx) =>
|
|
70
70
|
// Admin-style status flip from a list row: last-writer-wins is fine,
|
|
71
71
|
// same as the tenant enable/disable toggle this mirrors.
|
|
72
72
|
itemExecutor.update(
|
|
@@ -75,13 +75,13 @@ export const demoFeature = defineFeature("styleguide", (r) => {
|
|
|
75
75
|
ctx.db,
|
|
76
76
|
{ skipOptimisticLock: true },
|
|
77
77
|
),
|
|
78
|
-
|
|
79
|
-
);
|
|
78
|
+
access: open.access,
|
|
79
|
+
});
|
|
80
80
|
|
|
81
|
-
r.writeHandler(
|
|
82
|
-
"item:publish",
|
|
83
|
-
z.object({ id: z.uuid() }),
|
|
84
|
-
async (event, ctx) =>
|
|
81
|
+
r.writeHandler({
|
|
82
|
+
name: "item:publish",
|
|
83
|
+
schema: z.object({ id: z.uuid() }),
|
|
84
|
+
handler: async (event, ctx) =>
|
|
85
85
|
itemExecutor.update(
|
|
86
86
|
{
|
|
87
87
|
id: event.payload.id,
|
|
@@ -91,13 +91,13 @@ export const demoFeature = defineFeature("styleguide", (r) => {
|
|
|
91
91
|
ctx.db,
|
|
92
92
|
{ skipOptimisticLock: true },
|
|
93
93
|
),
|
|
94
|
-
|
|
95
|
-
);
|
|
94
|
+
access: open.access,
|
|
95
|
+
});
|
|
96
96
|
|
|
97
|
-
r.writeHandler(
|
|
98
|
-
"item:duplicate",
|
|
99
|
-
z.object({ id: z.uuid() }),
|
|
100
|
-
async (event, ctx) => {
|
|
97
|
+
r.writeHandler({
|
|
98
|
+
name: "item:duplicate",
|
|
99
|
+
schema: z.object({ id: z.uuid() }),
|
|
100
|
+
handler: async (event, ctx) => {
|
|
101
101
|
const source = await itemExecutor.detail({ id: event.payload.id }, event.user, ctx.db);
|
|
102
102
|
if (!source) return failNotFound("item", event.payload.id);
|
|
103
103
|
return itemExecutor.create(
|
|
@@ -115,8 +115,8 @@ export const demoFeature = defineFeature("styleguide", (r) => {
|
|
|
115
115
|
ctx.db,
|
|
116
116
|
);
|
|
117
117
|
},
|
|
118
|
-
|
|
119
|
-
);
|
|
118
|
+
access: open.access,
|
|
119
|
+
});
|
|
120
120
|
|
|
121
121
|
r.screen({
|
|
122
122
|
id: "item-edit",
|
|
@@ -46,10 +46,10 @@ function cannedText(payload: RunInput): string {
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
export const aiTextDemoFeature = defineFeature("ai-text", (r) => {
|
|
49
|
-
r.queryHandler(
|
|
50
|
-
"run",
|
|
51
|
-
runInput,
|
|
52
|
-
async ({ payload }) => {
|
|
49
|
+
r.queryHandler({
|
|
50
|
+
name: "run",
|
|
51
|
+
schema: runInput,
|
|
52
|
+
handler: async ({ payload }) => {
|
|
53
53
|
const text = cannedText(payload);
|
|
54
54
|
return {
|
|
55
55
|
type: "text" as const,
|
|
@@ -57,14 +57,12 @@ export const aiTextDemoFeature = defineFeature("ai-text", (r) => {
|
|
|
57
57
|
usage: { inputTokens: payload.text.length, outputTokens: text.length },
|
|
58
58
|
};
|
|
59
59
|
},
|
|
60
|
-
{
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
"side effects or per-user data; any signed-in user may query it",
|
|
66
|
-
},
|
|
60
|
+
access: {
|
|
61
|
+
openToAll: {
|
|
62
|
+
reason:
|
|
63
|
+
"demo stand-in for a real LLM call: returns canned text transforms with no " +
|
|
64
|
+
"side effects or per-user data; any signed-in user may query it",
|
|
67
65
|
},
|
|
68
66
|
},
|
|
69
|
-
);
|
|
67
|
+
});
|
|
70
68
|
});
|