@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.
Files changed (174) 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/feature.ts +9 -9
  4. package/packages/bundled-features/src/agent-tools/changes.json +6 -0
  5. package/packages/bundled-features/src/agent-tools/tool-dispatch.ts +87 -5
  6. package/packages/bundled-features/src/audit/feature.ts +1 -1
  7. package/packages/bundled-features/src/auth-email-password/changes.json +13 -0
  8. package/packages/bundled-features/src/auth-email-password/constants.ts +0 -2
  9. package/packages/bundled-features/src/auth-email-password/feature.ts +3 -3
  10. package/packages/bundled-features/src/auth-email-password/handlers/invite-create.write.ts +1 -0
  11. package/packages/bundled-features/src/auth-mfa/feature.ts +4 -4
  12. package/packages/bundled-features/src/auth-mfa-user-data/index.ts +1 -1
  13. package/packages/bundled-features/src/billing-foundation/consumer-protection/register-consent.ts +1 -1
  14. package/packages/bundled-features/src/billing-foundation/feature.ts +1 -1
  15. package/packages/bundled-features/src/cap-counter/cap-reservation.ts +89 -28
  16. package/packages/bundled-features/src/cap-counter/changes.json +7 -0
  17. package/packages/bundled-features/src/cap-counter/constants.ts +3 -0
  18. package/packages/bundled-features/src/cap-counter/feature.ts +2 -2
  19. package/packages/bundled-features/src/cap-counter/tables.ts +5 -2
  20. package/packages/bundled-features/src/cap-overview/feature.ts +3 -3
  21. package/packages/bundled-features/src/channel-discord/feature.ts +2 -2
  22. package/packages/bundled-features/src/channel-email/feature.ts +1 -1
  23. package/packages/bundled-features/src/channel-in-app/feature.ts +1 -1
  24. package/packages/bundled-features/src/channel-push/feature.ts +1 -1
  25. package/packages/bundled-features/src/channel-slack/feature.ts +2 -2
  26. package/packages/bundled-features/src/channel-teams/feature.ts +2 -2
  27. package/packages/bundled-features/src/channel-telegram/feature.ts +2 -2
  28. package/packages/bundled-features/src/compliance-profiles/feature.ts +1 -1
  29. package/packages/bundled-features/src/compliance-profiles-ops/index.ts +3 -3
  30. package/packages/bundled-features/src/config/feature.ts +1 -1
  31. package/packages/bundled-features/src/crypto-shredding/changes.json +6 -0
  32. package/packages/bundled-features/src/custom-fields/feature.ts +2 -2
  33. package/packages/bundled-features/src/custom-fields/wire-for-entity.ts +17 -13
  34. package/packages/bundled-features/src/data-retention/feature.ts +1 -1
  35. package/packages/bundled-features/src/data-retention/presets.ts +4 -4
  36. package/packages/bundled-features/src/delivery/changes.json +6 -0
  37. package/packages/bundled-features/src/delivery/db/queries/address-opt-outs.ts +6 -2
  38. package/packages/bundled-features/src/delivery/db/queries/preferences.ts +4 -4
  39. package/packages/bundled-features/src/delivery/delivery-service.ts +17 -2
  40. package/packages/bundled-features/src/delivery/feature.ts +9 -5
  41. package/packages/bundled-features/src/delivery/handlers/resubscribe-address.write.ts +12 -8
  42. package/packages/bundled-features/src/delivery/handlers/resubscribe-user.write.ts +3 -1
  43. package/packages/bundled-features/src/delivery/handlers/set-preference.write.ts +3 -1
  44. package/packages/bundled-features/src/delivery/handlers/unsubscribe-address.write.ts +3 -1
  45. package/packages/bundled-features/src/delivery/handlers/unsubscribe-user.write.ts +3 -1
  46. package/packages/bundled-features/src/delivery/notification-type-aliases.ts +13 -0
  47. package/packages/bundled-features/src/derivatives-sharp/feature.ts +1 -1
  48. package/packages/bundled-features/src/document-ingest-foundation/feature.ts +2 -2
  49. package/packages/bundled-features/src/file-derivatives/feature.ts +3 -3
  50. package/packages/bundled-features/src/file-foundation/feature.ts +1 -1
  51. package/packages/bundled-features/src/file-provider-inmemory/feature.ts +2 -2
  52. package/packages/bundled-features/src/file-provider-s3/feature.ts +3 -3
  53. package/packages/bundled-features/src/file-provider-s3-env/feature.ts +2 -2
  54. package/packages/bundled-features/src/files/README.md +1 -1
  55. package/packages/bundled-features/src/files-tenant-data/index.ts +1 -1
  56. package/packages/bundled-features/src/folders/feature.ts +2 -2
  57. package/packages/bundled-features/src/folders-user-data/index.ts +1 -1
  58. package/packages/bundled-features/src/form-draft/feature.ts +2 -2
  59. package/packages/bundled-features/src/form-draft-user-data/index.ts +1 -1
  60. package/packages/bundled-features/src/inbound-mail-foundation/changes.json +13 -0
  61. package/packages/bundled-features/src/inbound-mail-foundation/connect-routes.ts +9 -1
  62. package/packages/bundled-features/src/inbound-mail-foundation/feature.ts +1 -1
  63. package/packages/bundled-features/src/inbound-mail-foundation/types.ts +19 -7
  64. package/packages/bundled-features/src/inbound-mail-foundation/watch-supervisor.ts +21 -2
  65. package/packages/bundled-features/src/inbound-provider-imap/feature.ts +2 -2
  66. package/packages/bundled-features/src/inbound-provider-inmemory/feature.ts +1 -1
  67. package/packages/bundled-features/src/ledger/feature.ts +5 -5
  68. package/packages/bundled-features/src/legal-pages/feature.ts +1 -1
  69. package/packages/bundled-features/src/mail-foundation/feature.ts +1 -1
  70. package/packages/bundled-features/src/mail-transport-inmemory/feature.ts +2 -2
  71. package/packages/bundled-features/src/mail-transport-smtp/feature.ts +3 -3
  72. package/packages/bundled-features/src/managed-pages/css-gate.ts +1 -1
  73. package/packages/bundled-features/src/managed-pages/feature.ts +2 -2
  74. package/packages/bundled-features/src/metrics/feature.ts +2 -2
  75. package/packages/bundled-features/src/notes-history/feature.ts +2 -2
  76. package/packages/bundled-features/src/notes-history-user-data/index.ts +1 -1
  77. package/packages/bundled-features/src/personal-access-tokens/feature.ts +15 -11
  78. package/packages/bundled-features/src/readiness/feature.ts +2 -2
  79. package/packages/bundled-features/src/renderer-foundation/README.md +1 -1
  80. package/packages/bundled-features/src/renderer-foundation/feature.ts +1 -1
  81. package/packages/bundled-features/src/renderer-pdf/feature.ts +1 -1
  82. package/packages/bundled-features/src/secrets/feature.ts +1 -1
  83. package/packages/bundled-features/src/seo/feature.ts +1 -1
  84. package/packages/bundled-features/src/sessions/feature.ts +17 -13
  85. package/packages/bundled-features/src/step-dispatcher/feature.ts +1 -1
  86. package/packages/bundled-features/src/subscription-mollie/feature.ts +1 -1
  87. package/packages/bundled-features/src/subscription-stripe/feature.ts +3 -3
  88. package/packages/bundled-features/src/tags/feature.ts +2 -2
  89. package/packages/bundled-features/src/template-resolver/feature.ts +2 -2
  90. package/packages/bundled-features/src/template-resolver-user-data/index.ts +1 -1
  91. package/packages/bundled-features/src/tenant/feature.ts +4 -4
  92. package/packages/bundled-features/src/tenant/seeding.ts +1 -1
  93. package/packages/bundled-features/src/tenant-lifecycle/feature.ts +2 -3
  94. package/packages/bundled-features/src/tenant-settings/feature.ts +1 -1
  95. package/packages/bundled-features/src/tier-engine/feature.ts +36 -27
  96. package/packages/bundled-features/src/user/feature.ts +1 -1
  97. package/packages/bundled-features/src/user-data-rights/README.md +1 -1
  98. package/packages/bundled-features/src/user-data-rights/feature.ts +4 -4
  99. package/packages/bundled-features/src/user-data-rights-defaults/feature.ts +2 -2
  100. package/packages/bundled-features/src/user-profile/feature.ts +4 -4
  101. package/packages/bundled-features/src/waitlist/feature.ts +2 -2
  102. package/samples/apps/cap-billing-demo/README.md +1 -1
  103. package/samples/apps/cap-billing-demo/src/feature.ts +4 -4
  104. package/samples/apps/cap-billing-demo/src/run-config.ts +1 -1
  105. package/samples/apps/config-demo/src/features/demo/feature.ts +2 -2
  106. package/samples/apps/styleguide/src/features/ai-settings/feature.ts +43 -28
  107. package/samples/apps/styleguide/src/features/demo/feature.ts +18 -18
  108. package/samples/apps/styleguide/src/features/widgets/ai-text-demo.ts +10 -12
  109. package/samples/apps/styleguide/src/features/widgets/feature.ts +90 -108
  110. package/samples/apps/ui-walkthrough/src/features/rental/feature.ts +36 -36
  111. package/samples/apps/ui-walkthrough/src/features/vehicles/feature.ts +6 -6
  112. package/samples/apps/use-all-bundled/e2e-billing-gallery/gallery-features.ts +2 -2
  113. package/samples/apps/use-all-bundled/scripts/gen-sample-index.ts +7 -7
  114. package/samples/apps/use-all-bundled/src/app/notes-feature.ts +3 -3
  115. package/samples/apps/use-all-bundled/src/app/screens-feature.ts +1 -1
  116. package/samples/apps/user-data-rights-demo/src/feature.ts +5 -5
  117. package/samples/recipes/access-control/src/feature.ts +21 -25
  118. package/samples/recipes/anonymous-access/src/feature.ts +36 -43
  119. package/samples/recipes/anonymous-access-multitenant/src/feature.ts +14 -16
  120. package/samples/recipes/auth-foundation-providers/src/feature.ts +1 -1
  121. package/samples/recipes/basic-entity/README.md +1 -1
  122. package/samples/recipes/boot-check/README.md +1 -1
  123. package/samples/recipes/boot-check/src/feature.ts +2 -2
  124. package/samples/recipes/chat-channels/src/feature.ts +1 -1
  125. package/samples/recipes/cross-feature-events/src/feature.ts +7 -7
  126. package/samples/recipes/currencies-global/src/feature.ts +3 -3
  127. package/samples/recipes/currencies-per-tenant/src/feature.ts +2 -2
  128. package/samples/recipes/custom-fields-basic/README.md +1 -1
  129. package/samples/recipes/custom-fields-basic/src/feature.ts +3 -2
  130. package/samples/recipes/custom-handlers/README.md +3 -3
  131. package/samples/recipes/custom-handlers/src/feature.ts +37 -41
  132. package/samples/recipes/dashboard-stats/src/feature.ts +6 -6
  133. package/samples/recipes/delivery-notifications/README.md +1 -1
  134. package/samples/recipes/delivery-notifications/src/feature.ts +8 -8
  135. package/samples/recipes/designer-demo/src/feature.ts +2 -1
  136. package/samples/recipes/embedded/src/feature.ts +1 -1
  137. package/samples/recipes/embedded-entity-form/src/feature.ts +13 -13
  138. package/samples/recipes/encrypted-tenant-config/src/feature.ts +1 -1
  139. package/samples/recipes/error-contract/src/feature.ts +25 -25
  140. package/samples/recipes/event-sourcing/src/feature.ts +70 -76
  141. package/samples/recipes/feature-toggles/src/feature.ts +28 -20
  142. package/samples/recipes/files-post-processing/src/feature.ts +22 -26
  143. package/samples/recipes/idempotency/src/feature.ts +16 -18
  144. package/samples/recipes/inbound-mail-triage/src/feature.ts +1 -1
  145. package/samples/recipes/lane-routing/src/feature.ts +10 -12
  146. package/samples/recipes/legal-pages/src/feature.ts +1 -1
  147. package/samples/recipes/lifecycle-hooks/README.md +4 -4
  148. package/samples/recipes/lifecycle-hooks/src/feature.ts +55 -35
  149. package/samples/recipes/managed-config/src/feature.ts +8 -8
  150. package/samples/recipes/managed-pages/src/feature.ts +1 -1
  151. package/samples/recipes/notes-history-basic/README.md +1 -1
  152. package/samples/recipes/notes-history-basic/src/feature.ts +2 -2
  153. package/samples/recipes/pdf-audit-export/src/feature.ts +7 -7
  154. package/samples/recipes/pipeline-basics/README.md +1 -1
  155. package/samples/recipes/pipeline-basics/src/feature.ts +7 -7
  156. package/samples/recipes/public-share-token/src/feature.ts +1 -1
  157. package/samples/recipes/rate-limiting/src/feature.ts +7 -9
  158. package/samples/recipes/record-detail-layout/src/feature.ts +18 -18
  159. package/samples/recipes/reference-data/src/feature.ts +1 -1
  160. package/samples/recipes/relations/src/feature.ts +2 -2
  161. package/samples/recipes/screens-nav/src/feature.ts +1 -1
  162. package/samples/recipes/search/src/feature.ts +17 -19
  163. package/samples/recipes/security-baseline/README.md +1 -1
  164. package/samples/recipes/state-machine/src/feature.ts +1 -1
  165. package/samples/recipes/tags-basic/README.md +1 -1
  166. package/samples/recipes/tags-basic/src/feature.ts +2 -2
  167. package/samples/recipes/tenant-settings/README.md +1 -1
  168. package/samples/recipes/tenant-settings/src/feature.ts +1 -1
  169. package/samples/recipes/timezones/src/feature.ts +12 -12
  170. package/samples/recipes/user-data-rights/README.md +2 -2
  171. package/samples/recipes/user-data-rights/src/feature.ts +1 -1
  172. package/samples/recipes/user-profile/src/feature.ts +1 -1
  173. package/samples/recipes/webhook-step/src/feature.ts +1 -1
  174. package/samples/recipes/writeform-section/src/feature.ts +12 -12
@@ -198,10 +198,10 @@ export const widgetsFeature = defineFeature("widgets", (r) => {
198
198
  ],
199
199
  });
200
200
 
201
- r.queryHandler(
202
- "metrics:portfolio-stat",
203
- z.object({ region: z.string().optional() }),
204
- async ({ payload: { region } }, ctx) => ({
201
+ r.queryHandler({
202
+ name: "metrics:portfolio-stat",
203
+ schema: z.object({ region: z.string().optional() }),
204
+ handler: async ({ payload: { region } }, ctx) => ({
205
205
  value:
206
206
  region === "us"
207
207
  ? demoMoney(38120, "USD", ctx.locale)
@@ -212,86 +212,76 @@ export const widgetsFeature = defineFeature("widgets", (r) => {
212
212
  deltaDirection: "up",
213
213
  deltaTone: "positive",
214
214
  }),
215
- {
216
- access: {
217
- openToAll: {
218
- reason:
219
- "demo dashboard widget: returns static canned data with no per-user or " +
220
- "tenant scoping; any signed-in user may query it",
221
- },
215
+ access: {
216
+ openToAll: {
217
+ reason:
218
+ "demo dashboard widget: returns static canned data with no per-user or " +
219
+ "tenant scoping; any signed-in user may query it",
222
220
  },
223
221
  },
224
- );
225
- r.queryHandler(
226
- "metrics:net-worth-assets",
227
- z.object({ region: z.string().optional() }),
228
- async (_query, ctx) => ({ value: demoMoney(120000, "EUR", ctx.locale) }),
229
- {
230
- access: {
231
- openToAll: {
232
- reason:
233
- "demo dashboard widget: returns static canned data with no per-user or " +
234
- "tenant scoping; any signed-in user may query it",
235
- },
222
+ });
223
+ r.queryHandler({
224
+ name: "metrics:net-worth-assets",
225
+ schema: z.object({ region: z.string().optional() }),
226
+ handler: async (_query, ctx) => ({ value: demoMoney(120000, "EUR", ctx.locale) }),
227
+ access: {
228
+ openToAll: {
229
+ reason:
230
+ "demo dashboard widget: returns static canned data with no per-user or " +
231
+ "tenant scoping; any signed-in user may query it",
236
232
  },
237
233
  },
238
- );
239
- r.queryHandler(
240
- "metrics:net-worth-debts",
241
- z.object({ region: z.string().optional() }),
242
- async (_query, ctx) => ({ value: demoMoney(65370, "EUR", ctx.locale) }),
243
- {
244
- access: {
245
- openToAll: {
246
- reason:
247
- "demo dashboard widget: returns static canned data with no per-user or " +
248
- "tenant scoping; any signed-in user may query it",
249
- },
234
+ });
235
+ r.queryHandler({
236
+ name: "metrics:net-worth-debts",
237
+ schema: z.object({ region: z.string().optional() }),
238
+ handler: async (_query, ctx) => ({ value: demoMoney(65370, "EUR", ctx.locale) }),
239
+ access: {
240
+ openToAll: {
241
+ reason:
242
+ "demo dashboard widget: returns static canned data with no per-user or " +
243
+ "tenant scoping; any signed-in user may query it",
250
244
  },
251
245
  },
252
- );
253
- r.queryHandler(
254
- "metrics:response-times",
255
- z.object({}),
256
- async () => ({
246
+ });
247
+ r.queryHandler({
248
+ name: "metrics:response-times",
249
+ schema: z.object({}),
250
+ handler: async () => ({
257
251
  points: RESPONSE_POINTS,
258
252
  windowStartMs: 0,
259
253
  windowEndMs: 24 * 60 * 60 * 1000,
260
254
  }),
261
- {
262
- access: {
263
- openToAll: {
264
- reason:
265
- "demo dashboard widget: returns static canned data with no per-user or " +
266
- "tenant scoping; any signed-in user may query it",
267
- },
255
+ access: {
256
+ openToAll: {
257
+ reason:
258
+ "demo dashboard widget: returns static canned data with no per-user or " +
259
+ "tenant scoping; any signed-in user may query it",
268
260
  },
269
261
  },
270
- );
271
- r.queryHandler(
272
- "metrics:loan",
273
- z.object({}),
274
- async () => ({
262
+ });
263
+ r.queryHandler({
264
+ name: "metrics:loan",
265
+ schema: z.object({}),
266
+ handler: async () => ({
275
267
  series: LOAN_SERIES,
276
268
  markers: LOAN_MARKERS,
277
269
  windowStartMs: LOAN_START_MS,
278
270
  windowEndMs: loanMonthMs(LOAN_MONTHS),
279
271
  todayMs: LOAN_TODAY_MS,
280
272
  }),
281
- {
282
- access: {
283
- openToAll: {
284
- reason:
285
- "demo dashboard widget: returns static canned data with no per-user or " +
286
- "tenant scoping; any signed-in user may query it",
287
- },
273
+ access: {
274
+ openToAll: {
275
+ reason:
276
+ "demo dashboard widget: returns static canned data with no per-user or " +
277
+ "tenant scoping; any signed-in user may query it",
288
278
  },
289
279
  },
290
- );
291
- r.queryHandler(
292
- "metrics:latest-items",
293
- z.object({}),
294
- async (_query, ctx) => ({
280
+ });
281
+ r.queryHandler({
282
+ name: "metrics:latest-items",
283
+ schema: z.object({}),
284
+ handler: async (_query, ctx) => ({
295
285
  rows: [
296
286
  {
297
287
  id: "i1",
@@ -306,25 +296,23 @@ export const widgetsFeature = defineFeature("widgets", (r) => {
306
296
  ],
307
297
  nextCursor: null,
308
298
  }),
309
- {
310
- access: {
311
- openToAll: {
312
- reason:
313
- "demo dashboard widget: returns static canned data with no per-user or " +
314
- "tenant scoping; any signed-in user may query it",
315
- },
299
+ access: {
300
+ openToAll: {
301
+ reason:
302
+ "demo dashboard widget: returns static canned data with no per-user or " +
303
+ "tenant scoping; any signed-in user may query it",
316
304
  },
317
305
  },
318
- );
319
- r.queryHandler(
320
- "metrics:inbox-messages",
321
- z.object({
306
+ });
307
+ r.queryHandler({
308
+ name: "metrics:inbox-messages",
309
+ schema: z.object({
322
310
  cursor: z.coerce.number().int().min(0).optional(),
323
311
  limit: z.number().int().min(1).max(100).optional(),
324
312
  unreadOnly: z.boolean().optional(),
325
313
  search: z.string().optional(),
326
314
  }),
327
- async ({ payload: { cursor, limit, unreadOnly, search } }) => {
315
+ handler: async ({ payload: { cursor, limit, unreadOnly, search } }) => {
328
316
  const term = search?.trim().toLowerCase() ?? "";
329
317
  const filtered = INBOX_MESSAGES.filter(
330
318
  (m) =>
@@ -339,20 +327,18 @@ export const widgetsFeature = defineFeature("widgets", (r) => {
339
327
  const nextCursor = start + pageSize < filtered.length ? String(start + pageSize) : null;
340
328
  return { rows, nextCursor };
341
329
  },
342
- {
343
- access: {
344
- openToAll: {
345
- reason:
346
- "demo dashboard widget: returns static canned data with no per-user or " +
347
- "tenant scoping; any signed-in user may query it",
348
- },
330
+ access: {
331
+ openToAll: {
332
+ reason:
333
+ "demo dashboard widget: returns static canned data with no per-user or " +
334
+ "tenant scoping; any signed-in user may query it",
349
335
  },
350
336
  },
351
- );
352
- r.queryHandler(
353
- "metrics:upcoming-events",
354
- z.object({}),
355
- async (_query, ctx) => ({
337
+ });
338
+ r.queryHandler({
339
+ name: "metrics:upcoming-events",
340
+ schema: z.object({}),
341
+ handler: async (_query, ctx) => ({
356
342
  rows: [
357
343
  {
358
344
  primary: demoText("widgets:dashboard:demo-event-rate-adjustment", ctx.locale),
@@ -364,20 +350,18 @@ export const widgetsFeature = defineFeature("widgets", (r) => {
364
350
  },
365
351
  ],
366
352
  }),
367
- {
368
- access: {
369
- openToAll: {
370
- reason:
371
- "demo dashboard widget: returns static canned data with no per-user or " +
372
- "tenant scoping; any signed-in user may query it",
373
- },
353
+ access: {
354
+ openToAll: {
355
+ reason:
356
+ "demo dashboard widget: returns static canned data with no per-user or " +
357
+ "tenant scoping; any signed-in user may query it",
374
358
  },
375
359
  },
376
- );
377
- r.queryHandler(
378
- "metrics:goal-progress",
379
- z.object({}),
380
- async (_query, ctx) => ({
360
+ });
361
+ r.queryHandler({
362
+ name: "metrics:goal-progress",
363
+ schema: z.object({}),
364
+ handler: async (_query, ctx) => ({
381
365
  rows: [
382
366
  {
383
367
  label: demoText("widgets:dashboard:demo-goal-mortgage", ctx.locale),
@@ -391,16 +375,14 @@ export const widgetsFeature = defineFeature("widgets", (r) => {
391
375
  },
392
376
  ],
393
377
  }),
394
- {
395
- access: {
396
- openToAll: {
397
- reason:
398
- "demo dashboard widget: returns static canned data with no per-user or " +
399
- "tenant scoping; any signed-in user may query it",
400
- },
378
+ access: {
379
+ openToAll: {
380
+ reason:
381
+ "demo dashboard widget: returns static canned data with no per-user or " +
382
+ "tenant scoping; any signed-in user may query it",
401
383
  },
402
384
  },
403
- );
385
+ });
404
386
 
405
387
  r.translations({ keys: WIDGETS_I18N });
406
388
 
@@ -71,10 +71,10 @@ export const rentalFeature = defineFeature("rental", (r) => {
71
71
  r.crud("leasePosition", leasePositionEntity, { write: open, read: open });
72
72
  r.crud("leaseParty", leasePartyEntity, { write: open, read: open });
73
73
 
74
- r.queryHandler(
75
- "lease:akte",
76
- idPayloadSchema,
77
- async (query, ctx) => {
74
+ r.queryHandler({
75
+ name: "lease:akte",
76
+ schema: idPayloadSchema,
77
+ handler: async (query, ctx) => {
78
78
  const row = await leaseExecutor.detail({ id: query.payload.id }, query.user, ctx.db);
79
79
  if (!row) return failNotFound("lease", query.payload.id);
80
80
  const lease = parseLeaseRow(row);
@@ -104,13 +104,13 @@ export const rentalFeature = defineFeature("rental", (r) => {
104
104
  positionCount: await countChildren(positionExecutor),
105
105
  };
106
106
  },
107
- open,
108
- );
107
+ ...open,
108
+ });
109
109
 
110
- r.queryHandler(
111
- "lease:positions",
112
- idPayloadSchema,
113
- async (query, ctx) => {
110
+ r.queryHandler({
111
+ name: "lease:positions",
112
+ schema: idPayloadSchema,
113
+ handler: async (query, ctx) => {
114
114
  const page = await positionExecutor.list(
115
115
  { filter: { field: "lease", op: "eq", value: query.payload.id }, limit: 100 },
116
116
  query.user,
@@ -132,13 +132,13 @@ export const rentalFeature = defineFeature("rental", (r) => {
132
132
  }),
133
133
  };
134
134
  },
135
- open,
136
- );
135
+ ...open,
136
+ });
137
137
 
138
- r.queryHandler(
139
- "lease:kennzahlen",
140
- z.object({}),
141
- async () => ({
138
+ r.queryHandler({
139
+ name: "lease:kennzahlen",
140
+ schema: z.object({}),
141
+ handler: async () => ({
142
142
  debt: "123.456 €",
143
143
  debtSub: "nach 10 Jahren",
144
144
  debtDelta: "15 %",
@@ -146,13 +146,13 @@ export const rentalFeature = defineFeature("rental", (r) => {
146
146
  rent: "48.200 €",
147
147
  rentSub: "319 Verträge",
148
148
  }),
149
- open,
150
- );
149
+ ...open,
150
+ });
151
151
 
152
- r.writeHandler(
153
- "lease:terminate",
154
- idPayloadSchema,
155
- async (event, ctx) =>
152
+ r.writeHandler({
153
+ name: "lease:terminate",
154
+ schema: idPayloadSchema,
155
+ handler: async (event, ctx) =>
156
156
  leaseExecutor.update(
157
157
  {
158
158
  id: event.payload.id,
@@ -165,13 +165,13 @@ export const rentalFeature = defineFeature("rental", (r) => {
165
165
  ctx.db,
166
166
  { skipOptimisticLock: true },
167
167
  ),
168
- open,
169
- );
168
+ ...open,
169
+ });
170
170
 
171
- r.writeHandler(
172
- "lease:record-positions",
173
- recordPositionsPayloadSchema,
174
- async (event, ctx) => {
171
+ r.writeHandler({
172
+ name: "lease:record-positions",
173
+ schema: recordPositionsPayloadSchema,
174
+ handler: async (event, ctx) => {
175
175
  let created: Awaited<ReturnType<typeof positionExecutor.create>> | undefined;
176
176
  const unitLabels = new Map<string, string | null | undefined>();
177
177
  for (const unitId of new Set(event.payload.items.map((item) => item.unit))) {
@@ -189,13 +189,13 @@ export const rentalFeature = defineFeature("rental", (r) => {
189
189
  }
190
190
  return created ?? failNotFound("leasePosition", event.payload.lease);
191
191
  },
192
- open,
193
- );
192
+ ...open,
193
+ });
194
194
 
195
- r.writeHandler(
196
- "rent:adjust",
197
- rentAdjustPayloadSchema,
198
- async (event, ctx) => {
195
+ r.writeHandler({
196
+ name: "rent:adjust",
197
+ schema: rentAdjustPayloadSchema,
198
+ handler: async (event, ctx) => {
199
199
  const { positionId, wirksamAb, neuerBetrag } = event.payload;
200
200
  const current = await positionExecutor.detail({ id: positionId }, event.user, ctx.db);
201
201
  if (!current) return failNotFound("leasePosition", positionId);
@@ -229,8 +229,8 @@ export const rentalFeature = defineFeature("rental", (r) => {
229
229
  { skipOptimisticLock: true },
230
230
  );
231
231
  },
232
- open,
233
- );
232
+ ...open,
233
+ });
234
234
 
235
235
  r.screen(leaseListScreen);
236
236
  r.screen(leaseListShortScreen);
@@ -32,10 +32,10 @@ export const vehiclesFeature = defineFeature("vehicles", (r) => {
32
32
  r.crud("campaign", campaignEntity, { write: open, read: open });
33
33
  r.crud("campaignPost", campaignPostEntity, { write: open, read: open });
34
34
 
35
- r.writeHandler(
36
- "campaign-post:mark-posted",
37
- markPostedPayloadSchema,
38
- async (event, ctx) => {
35
+ r.writeHandler({
36
+ name: "campaign-post:mark-posted",
37
+ schema: markPostedPayloadSchema,
38
+ handler: async (event, ctx) => {
39
39
  const current = await campaignPostExecutor.detail(
40
40
  { id: event.payload.id },
41
41
  event.user,
@@ -62,8 +62,8 @@ export const vehiclesFeature = defineFeature("vehicles", (r) => {
62
62
  { skipOptimisticLock: true },
63
63
  );
64
64
  },
65
- open,
66
- );
65
+ ...open,
66
+ });
67
67
 
68
68
  r.screen(vehicleListScreen);
69
69
  r.screen(vehicleEditScreen);
@@ -117,14 +117,14 @@ export const galleryProviderFeature: FeatureDefinition = defineFeature(
117
117
  "gallery-mock-provider",
118
118
  (r) => {
119
119
  r.describe("E2E-only mock subscription provider for the consent/termination gallery.");
120
- r.requires("billing-foundation");
120
+ r.requires({ features: ["billing-foundation"] });
121
121
  r.useExtension("subscriptionProvider", GALLERY_PROVIDER, galleryProviderPlugin);
122
122
  },
123
123
  );
124
124
 
125
125
  export const galleryScreensFeature: FeatureDefinition = defineFeature("gallery-screens", (r) => {
126
126
  r.describe("E2E-only nav entry for the billing plans screen plus plan labels.");
127
- r.requires("billing-foundation", "admin-shell");
127
+ r.requires({ features: ["billing-foundation", "admin-shell"] });
128
128
  r.nav({
129
129
  id: "billing",
130
130
  label: "gallery:nav.billing",
@@ -89,13 +89,13 @@ export function extractFeaturesFromText(rawText: string): Set<string> {
89
89
  const name = m[1];
90
90
  if (name) feats.add(name);
91
91
  }
92
- for (const m of text.matchAll(/r\.requires\(\s*["']([a-z0-9-]+)["']/g)) {
93
- const name = m[1];
94
- if (name) feats.add(name);
95
- }
96
- for (const m of text.matchAll(/r\.optionalRequires\(\s*["']([a-z0-9-]+)["']/g)) {
97
- const name = m[1];
98
- if (name) feats.add(name);
92
+ for (const m of text.matchAll(
93
+ /r\.(?:requires|optionalRequires)\(\s*\{\s*features:\s*\[([^\]]*)\]/g,
94
+ )) {
95
+ for (const quoted of (m[1] ?? "").matchAll(/["']([a-z0-9-]+)["']/g)) {
96
+ const name = quoted[1];
97
+ if (name) feats.add(name);
98
+ }
99
99
  }
100
100
  return feats;
101
101
  }
@@ -120,9 +120,9 @@ export const notesFeature: FeatureDefinition = defineFeature("notes-demo", (r) =
120
120
  r.describe(
121
121
  "Dev-only host entity demonstrating tags, custom-fields, and folders on a real entityEdit screen.",
122
122
  );
123
- r.requires("tags");
124
- r.requires("custom-fields");
125
- r.requires("folders");
123
+ r.requires({ features: ["tags"] });
124
+ r.requires({ features: ["custom-fields"] });
125
+ r.requires({ features: ["folders"] });
126
126
 
127
127
  wireCustomFieldsFor(r, "note", noteTable);
128
128
 
@@ -9,7 +9,7 @@ import { defineFeature, type FeatureDefinition } from "@cosmicdrift/kumiko-frame
9
9
 
10
10
  export const appScreensFeature: FeatureDefinition = defineFeature("app-screens", (r) => {
11
11
  r.describe("App-side nav placement for the user-profile self-service page.");
12
- r.requires("user-profile");
12
+ r.requires({ features: ["user-profile"] });
13
13
  r.nav({
14
14
  id: "profile",
15
15
  label: "screen:profile.title",
@@ -6,10 +6,10 @@
6
6
  // DSGVO-Endpoints kommen vollstaendig aus user-data-rights.
7
7
  //
8
8
  // Dieses Feature zeigt:
9
- // - r.entity("todo", todoEntity) → Drizzle-Tabelle wird gebaut
10
- // - r.writeHandler("create") → User legt Todo an
11
- // - r.queryHandler("list") → User sieht seine Todos
12
- // - r.useExtension(EXT_USER_DATA, "todo") → Forget + Export integration
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
13
13
  //
14
14
  // Was passiert wenn der User dann request-export aufruft:
15
15
  // 1. user-data-rights.request-export.write triggert einen Job
@@ -122,7 +122,7 @@ const listTodosHandler = defineQueryHandler({
122
122
  });
123
123
 
124
124
  export const todosFeature = defineFeature(FEATURE_NAME, (r) => {
125
- r.requires("user-data-rights");
125
+ r.requires({ features: ["user-data-rights"] });
126
126
 
127
127
  // store_todos is a direct-write store: the create handler `insertOne`s and
128
128
  // the forget hook `updateMany`/`deleteMany`s rows WITHOUT emitting lifecycle
@@ -64,8 +64,8 @@ export const taskTable = buildEntityTable("task", taskEntity, {
64
64
  });
65
65
 
66
66
  export const accessControlFeature = defineFeature("access-control", (r) => {
67
- r.entity("project", projectEntity);
68
- r.entity("task", taskEntity);
67
+ r.entity({ name: "project", ...projectEntity });
68
+ r.entity({ name: "task", ...taskEntity });
69
69
  r.relation("task", "project", taskRelations.project);
70
70
 
71
71
  // Admin-only: standard create — no business logic, helper handles schema +
@@ -80,14 +80,14 @@ export const accessControlFeature = defineFeature("access-control", (r) => {
80
80
  // hand-write this handler instead of using the helper. The framework helpers
81
81
  // are deliberately scope-limited — opt out the moment you need defaults.
82
82
  const taskExecutor = createEventStoreExecutor(taskTable, taskEntity, { entityName: "task" });
83
- r.writeHandler(
84
- "task:create",
85
- z.object({
83
+ r.writeHandler({
84
+ name: "task:create",
85
+ schema: z.object({
86
86
  title: z.string().min(1),
87
87
  projectId: z.uuid(),
88
88
  assigneeId: z.uuid().optional(),
89
89
  }),
90
- async (event, ctx) =>
90
+ handler: async (event, ctx) =>
91
91
  taskExecutor.create(
92
92
  {
93
93
  ...event.payload,
@@ -96,22 +96,20 @@ export const accessControlFeature = defineFeature("access-control", (r) => {
96
96
  event.user,
97
97
  ctx.db,
98
98
  ),
99
- {
100
- access: {
101
- openToAll: {
102
- reason:
103
- "any signed-in tenant user may create a task; assigneeId defaults to the caller when omitted",
104
- },
99
+ access: {
100
+ openToAll: {
101
+ reason:
102
+ "any signed-in tenant user may create a task; assigneeId defaults to the caller when omitted",
105
103
  },
106
104
  },
107
- );
105
+ });
108
106
 
109
107
  // Update requires version — optimistic locking is the default and this
110
108
  // narrowed schema (only title + assigneeId mutable) is stricter than the
111
109
  // helper's generic update schema, so we keep it hand-written.
112
- r.writeHandler(
113
- "task:update",
114
- z.object({
110
+ r.writeHandler({
111
+ name: "task:update",
112
+ schema: z.object({
115
113
  id: z.uuid(),
116
114
  version: z.number(),
117
115
  changes: z.object({
@@ -119,17 +117,15 @@ export const accessControlFeature = defineFeature("access-control", (r) => {
119
117
  assigneeId: z.uuid().optional(),
120
118
  }),
121
119
  }),
122
- async (event, ctx) => taskExecutor.update(event.payload, event.user, ctx.db),
123
- {
124
- access: {
125
- openToAll: {
126
- reason:
127
- "any signed-in tenant user may update a task; optimistic locking (version) " +
128
- "guards against concurrent overwrites, not caller identity",
129
- },
120
+ handler: async (event, ctx) => taskExecutor.update(event.payload, event.user, ctx.db),
121
+ access: {
122
+ openToAll: {
123
+ reason:
124
+ "any signed-in tenant user may update a task; optimistic locking (version) " +
125
+ "guards against concurrent overwrites, not caller identity",
130
126
  },
131
127
  },
132
- );
128
+ });
133
129
 
134
130
  // Self-service list — no custom logic.
135
131
  r.queryHandler(