@cosmicdrift/kumiko-framework 0.146.4 → 0.147.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 (68) hide show
  1. package/package.json +6 -2
  2. package/src/api/auth-routes.ts +32 -67
  3. package/src/api/routes.ts +1 -3
  4. package/src/bun-db/__tests__/PATTERN.md +0 -1
  5. package/src/db/__tests__/assert-no-unreachable-live-rows.integration.test.ts +29 -3
  6. package/src/db/__tests__/schema-inspection.test.ts +7 -0
  7. package/src/db/event-store-executor-context.ts +398 -0
  8. package/src/db/event-store-executor-read.ts +276 -0
  9. package/src/db/event-store-executor-write.ts +615 -0
  10. package/src/db/event-store-executor.ts +29 -1166
  11. package/src/db/queries/shadow-swap.ts +3 -1
  12. package/src/db/schema-inspection.ts +1 -1
  13. package/src/engine/__tests__/boot-validator-dashboard.test.ts +10 -0
  14. package/src/engine/__tests__/engine.test.ts +45 -1
  15. package/src/engine/__tests__/event-migration-declarative.test.ts +6 -0
  16. package/src/engine/__tests__/membership-roles.test.ts +4 -10
  17. package/src/engine/__tests__/screen.test.ts +26 -0
  18. package/src/engine/boot-validator/entity-list-screens.ts +1 -1
  19. package/src/engine/boot-validator/gdpr-storage.ts +1 -1
  20. package/src/engine/boot-validator/index.ts +12 -8
  21. package/src/engine/boot-validator/nav.ts +120 -0
  22. package/src/engine/boot-validator/{screens-nav.ts → screens.ts} +12 -190
  23. package/src/engine/boot-validator/workspaces.ts +68 -0
  24. package/src/engine/define-feature.ts +77 -1011
  25. package/src/engine/feature-ast/__tests__/fixtures/cross-file-registrar/feature.ts +9 -0
  26. package/src/engine/feature-ast/__tests__/fixtures/cross-file-registrar/screens.ts +4 -0
  27. package/src/engine/feature-ast/__tests__/parse-real-features.test.ts +18 -8
  28. package/src/engine/feature-ast/__tests__/parse.test.ts +291 -2
  29. package/src/engine/feature-ast/__tests__/patcher.test.ts +1 -1
  30. package/src/engine/feature-ast/__tests__/render-roundtrip.test.ts +73 -0
  31. package/src/engine/feature-ast/extractors/round1.ts +3 -3
  32. package/src/engine/feature-ast/extractors/round4.ts +45 -10
  33. package/src/engine/feature-ast/extractors/shared.ts +64 -6
  34. package/src/engine/feature-ast/parse.ts +123 -10
  35. package/src/engine/feature-ast/patterns.ts +14 -7
  36. package/src/engine/feature-ast/render.ts +28 -7
  37. package/src/engine/feature-builder-state.ts +165 -0
  38. package/src/engine/feature-config-events-jobs.ts +303 -0
  39. package/src/engine/feature-entity-handlers.ts +161 -0
  40. package/src/engine/feature-ui-extensions.ts +413 -0
  41. package/src/engine/index.ts +0 -2
  42. package/src/engine/pattern-library/library.ts +44 -1131
  43. package/src/engine/pattern-library/mixed-schemas.ts +484 -0
  44. package/src/engine/pattern-library/opaque-schemas.ts +124 -0
  45. package/src/engine/pattern-library/shared-fields.ts +80 -0
  46. package/src/engine/pattern-library/static-schemas.ts +456 -0
  47. package/src/engine/registry-facade.ts +349 -0
  48. package/src/engine/registry-ingest.ts +473 -0
  49. package/src/engine/registry-state.ts +388 -0
  50. package/src/engine/registry-validate.ts +660 -0
  51. package/src/engine/registry.ts +79 -1695
  52. package/src/engine/types/screen.ts +4 -2
  53. package/src/engine/types/tree-node.ts +2 -5
  54. package/src/event-store/snapshot.ts +7 -7
  55. package/src/i18n/required-surface-keys.ts +2 -0
  56. package/src/jobs/job-runner.ts +1 -1
  57. package/src/observability/standard-metrics.ts +4 -3
  58. package/src/pipeline/dispatch-batch.ts +3 -3
  59. package/src/pipeline/dispatch-shared.ts +17 -10
  60. package/src/pipeline/event-dispatcher-admin.ts +289 -0
  61. package/src/pipeline/event-dispatcher-delivery.ts +264 -0
  62. package/src/pipeline/event-dispatcher.ts +28 -540
  63. package/src/pipeline/projection-rebuild.ts +1 -1
  64. package/src/stack/__tests__/setup-test-stack-jobs.integration.test.ts +95 -0
  65. package/src/stack/test-stack.ts +46 -1
  66. package/src/testing/boot-validator-fixture.ts +1 -2
  67. package/src/engine/__tests__/deep-link.test.ts +0 -35
  68. package/src/engine/deep-link.ts +0 -23
@@ -14,1137 +14,50 @@
14
14
  // the Designer/LLM relies on.
15
15
 
16
16
  import type { FeaturePatternKind } from "../feature-ast/patterns";
17
- import type { FormFieldSpec, PatternCategory, PatternFormSchema } from "./types";
18
-
19
- // =============================================================================
20
- // Reusable field building blocks
21
- // =============================================================================
22
-
23
- const HOOK_TYPE_OPTIONS = [
24
- { value: "validation", label: { en: "Validation", de: "Validierung" } },
25
- { value: "preSave", label: { en: "Pre-Save", de: "Vor Speichern" } },
26
- { value: "postSave", label: { en: "Post-Save", de: "Nach Speichern" } },
27
- { value: "preDelete", label: { en: "Pre-Delete", de: "Vor Löschen" } },
28
- { value: "postDelete", label: { en: "Post-Delete", de: "Nach Löschen" } },
29
- { value: "preQuery", label: { en: "Pre-Query", de: "Vor Abfrage" } },
30
- ] as const;
31
-
32
- const ENTITY_HOOK_TYPE_OPTIONS = [
33
- { value: "postSave", label: { en: "Post-Save", de: "Nach Speichern" } },
34
- { value: "preDelete", label: { en: "Pre-Delete", de: "Vor Löschen" } },
35
- { value: "postDelete", label: { en: "Post-Delete", de: "Nach Löschen" } },
36
- ] as const;
37
-
38
- const HTTP_METHOD_OPTIONS = [
39
- { value: "GET", label: { en: "GET" } },
40
- { value: "POST", label: { en: "POST" } },
41
- { value: "PUT", label: { en: "PUT" } },
42
- { value: "PATCH", label: { en: "PATCH" } },
43
- { value: "DELETE", label: { en: "DELETE" } },
44
- { value: "HEAD", label: { en: "HEAD" } },
45
- { value: "OPTIONS", label: { en: "OPTIONS" } },
46
- ] as const;
47
-
48
- const CLAIM_KEY_TYPE_OPTIONS = [
49
- { value: "string", label: { en: "string" } },
50
- { value: "number", label: { en: "number" } },
51
- { value: "boolean", label: { en: "boolean" } },
52
- { value: "string[]", label: { en: "string[]" } },
53
- { value: "object", label: { en: "object" } },
54
- ] as const;
55
-
56
- const ID_TYPE_OPTIONS = [
57
- { value: "uuid", label: { en: "UUID (default)", de: "UUID (Standard)" } },
58
- { value: "serial", label: { en: "Serial integer", de: "Serial Integer" } },
59
- ] as const;
60
-
61
- const accessRuleField: FormFieldSpec = {
62
- path: "access",
63
- label: { en: "Access", de: "Zugriff" },
64
- hint: { en: "Either a list of role names or `openToAll`." },
65
- input: "discriminated-union",
66
- discriminator: "type",
67
- variants: [
68
- {
69
- tag: "roles",
70
- label: { en: "Role-based", de: "Rollen-basiert" },
71
- fields: [
72
- {
73
- path: "access.roles",
74
- label: { en: "Roles", de: "Rollen" },
75
- input: "string-list",
76
- itemPlaceholder: "Admin",
77
- },
78
- ],
79
- },
80
- {
81
- tag: "openToAll",
82
- label: { en: "Open to all (auth still required)", de: "Für alle (Auth nötig)" },
83
- fields: [
84
- {
85
- path: "access.openToAll",
86
- label: { en: "Open to all", de: "Offen für alle" },
87
- input: "boolean",
88
- },
89
- ],
90
- },
91
- ],
92
- };
93
-
94
- // =============================================================================
95
- // Pattern schemas
96
- // =============================================================================
97
-
98
- // --- Static patterns (form-only, no closures) -----------------------------
99
-
100
- const requiresSchema: PatternFormSchema = {
101
- kind: "requires",
102
- label: { en: "Requires", de: "Benötigt" },
103
- summary: { en: "Hard dependency on other features." },
104
- category: "meta",
105
- editability: "static",
106
- singleton: true,
107
- fields: [
108
- {
109
- path: "featureNames",
110
- label: { en: "Feature names", de: "Feature-Namen" },
111
- input: "string-list",
112
- itemPlaceholder: "auth",
113
- required: true,
114
- },
115
- ],
116
- };
117
-
118
- const optionalRequiresSchema: PatternFormSchema = {
119
- kind: "optionalRequires",
120
- label: { en: "Optional requires", de: "Optional benötigt" },
121
- summary: { en: "Soft dependency — used if available, otherwise skipped." },
122
- category: "meta",
123
- editability: "static",
124
- singleton: true,
125
- fields: [
126
- {
127
- path: "featureNames",
128
- label: { en: "Feature names", de: "Feature-Namen" },
129
- input: "string-list",
130
- itemPlaceholder: "analytics",
131
- },
132
- ],
133
- };
134
-
135
- const readsConfigSchema: PatternFormSchema = {
136
- kind: "readsConfig",
137
- label: { en: "Reads config", de: "Liest Config" },
138
- summary: { en: "Declares which qualified config keys this feature reads." },
139
- category: "meta",
140
- editability: "static",
141
- singleton: true,
142
- fields: [
143
- {
144
- path: "qualifiedKeys",
145
- label: { en: "Qualified keys", de: "Qualifizierte Keys" },
146
- input: "string-list",
147
- itemPlaceholder: "billing:plan",
148
- },
149
- ],
150
- };
151
-
152
- const systemScopeSchema: PatternFormSchema = {
153
- kind: "systemScope",
154
- label: { en: "System scope", de: "System-Scope" },
155
- summary: { en: "Marks this feature as system-tenant only." },
156
- category: "meta",
157
- editability: "static",
158
- singleton: true,
159
- fields: [],
160
- };
161
-
162
- const toggleableSchema: PatternFormSchema = {
163
- kind: "toggleable",
164
- label: { en: "Toggleable", de: "Umschaltbar" },
165
- summary: { en: "Operator can switch this feature on/off per tenant." },
166
- category: "meta",
167
- editability: "static",
168
- singleton: true,
169
- fields: [
170
- {
171
- path: "default",
172
- label: { en: "Enabled by default", de: "Standardmäßig aktiviert" },
173
- input: "boolean",
174
- required: true,
175
- },
176
- ],
177
- };
178
-
179
- const describeSchema: PatternFormSchema = {
180
- kind: "describe",
181
- label: { en: "Description", de: "Beschreibung" },
182
- summary: { en: "One-to-three-sentence docs-lead: what the feature does + when you need it." },
183
- category: "meta",
184
- editability: "static",
185
- singleton: true,
186
- fields: [
187
- {
188
- path: "text",
189
- label: { en: "Text", de: "Text" },
190
- input: "textarea",
191
- required: true,
192
- placeholder: "Stores per-tenant widgets and exposes CRUD handlers for them.",
193
- },
194
- ],
195
- };
196
-
197
- const uiHintsSchema: PatternFormSchema = {
198
- kind: "uiHints",
199
- label: { en: "UI hints", de: "UI-Hinweise" },
200
- summary: { en: "Picker/scaffolder metadata. Opaque to the Designer; rendered as raw TS source." },
201
- category: "meta",
202
- editability: "opaque",
203
- singleton: true,
204
- fields: [],
205
- };
206
-
207
- const entitySchema: PatternFormSchema = {
208
- kind: "entity",
209
- label: { en: "Entity", de: "Entität" },
210
- summary: { en: "An aggregate stored as event-sourced read-model." },
211
- category: "data",
212
- editability: "static",
213
- fields: [
214
- {
215
- path: "entityName",
216
- label: { en: "Name", de: "Name" },
217
- input: "text",
218
- required: true,
219
- placeholder: "task",
220
- },
221
- {
222
- path: "definition.fields",
223
- label: { en: "Fields", de: "Felder" },
224
- input: "entity-fields-editor",
225
- required: true,
226
- },
227
- {
228
- path: "definition.idType",
229
- label: { en: "ID type", de: "ID-Typ" },
230
- input: "select",
231
- options: ID_TYPE_OPTIONS,
232
- },
233
- {
234
- path: "definition.softDelete",
235
- label: { en: "Soft delete", de: "Soft-Delete" },
236
- hint: { en: "Mark rows isDeleted=true instead of removing them." },
237
- input: "boolean",
238
- },
239
- {
240
- path: "definition.table",
241
- label: { en: "Table name (override)", de: "Tabellenname (Override)" },
242
- hint: { en: "Defaults to read_<plural-snake-case-name>." },
243
- input: "text",
244
- },
245
- ],
246
- };
247
-
248
- const relationSchema: PatternFormSchema = {
249
- kind: "relation",
250
- label: { en: "Relation", de: "Beziehung" },
251
- summary: { en: "Foreign-key relationship between entities." },
252
- category: "data",
253
- editability: "static",
254
- fields: [
255
- {
256
- path: "entityName",
257
- label: { en: "Owner entity", de: "Besitzende Entität" },
258
- input: "entity-ref",
259
- required: true,
260
- },
261
- {
262
- path: "relationName",
263
- label: { en: "Relation name", de: "Beziehungs-Name" },
264
- input: "text",
265
- required: true,
266
- placeholder: "owner",
267
- },
268
- {
269
- path: "definition",
270
- label: { en: "Type", de: "Typ" },
271
- input: "discriminated-union",
272
- discriminator: "type",
273
- variants: [
274
- {
275
- tag: "belongsTo",
276
- label: { en: "Belongs to", de: "Gehört zu" },
277
- fields: [
278
- {
279
- path: "definition.target",
280
- label: { en: "Target entity", de: "Ziel-Entität" },
281
- input: "entity-ref",
282
- required: true,
283
- },
284
- {
285
- path: "definition.foreignKey",
286
- label: { en: "Foreign key column", de: "FK-Spalte" },
287
- input: "text",
288
- required: true,
289
- },
290
- ],
291
- },
292
- {
293
- tag: "hasMany",
294
- label: { en: "Has many", de: "Hat viele" },
295
- fields: [
296
- {
297
- path: "definition.target",
298
- label: { en: "Target entity", de: "Ziel-Entität" },
299
- input: "entity-ref",
300
- required: true,
301
- },
302
- ],
303
- },
304
- {
305
- tag: "manyToMany",
306
- label: { en: "Many to many", de: "Viele zu viele" },
307
- fields: [
308
- {
309
- path: "definition.target",
310
- label: { en: "Target entity", de: "Ziel-Entität" },
311
- input: "entity-ref",
312
- required: true,
313
- },
314
- ],
315
- },
316
- ],
317
- },
318
- ],
319
- };
320
-
321
- const navSchema: PatternFormSchema = {
322
- kind: "nav",
323
- label: { en: "Navigation entry", de: "Navigations-Eintrag" },
324
- summary: { en: "Side-bar / menu link." },
325
- category: "ui",
326
- editability: "static",
327
- fields: [
328
- {
329
- path: "definition.id",
330
- label: { en: "ID", de: "ID" },
331
- input: "text",
332
- required: true,
333
- },
334
- {
335
- path: "definition.label",
336
- label: { en: "Label", de: "Beschriftung" },
337
- input: "text",
338
- },
339
- {
340
- path: "definition.screen",
341
- label: { en: "Screen QN", de: "Screen-QN" },
342
- hint: { en: "<feature>:screen:<id> qualified name." },
343
- input: "text",
344
- },
345
- {
346
- path: "definition.parent",
347
- label: { en: "Parent nav", de: "Eltern-Nav" },
348
- input: "text",
349
- },
350
- ],
351
- };
352
-
353
- const workspaceSchema: PatternFormSchema = {
354
- kind: "workspace",
355
- label: { en: "Workspace", de: "Arbeitsbereich" },
356
- summary: { en: "Persona-/role-scoped UI surface." },
357
- category: "ui",
358
- editability: "static",
359
- fields: [
360
- {
361
- path: "definition.id",
362
- label: { en: "ID", de: "ID" },
363
- input: "text",
364
- required: true,
365
- },
366
- {
367
- path: "definition.label",
368
- label: { en: "Label", de: "Beschriftung" },
369
- input: "text",
370
- },
371
- ],
372
- };
373
-
374
- const configSchema: PatternFormSchema = {
375
- kind: "config",
376
- label: { en: "Config keys", de: "Config-Keys" },
377
- summary: { en: "Typed runtime configuration." },
378
- category: "meta",
379
- editability: "static",
380
- singleton: true,
381
- fields: [
382
- {
383
- path: "keys",
384
- label: { en: "Keys", de: "Keys" },
385
- input: "key-value-map",
386
- keyPlaceholder: "maxItems",
387
- valueInput: "json-readonly",
388
- required: true,
389
- },
390
- ],
391
- };
392
-
393
- const translationsSchema: PatternFormSchema = {
394
- kind: "translations",
395
- label: { en: "Translations", de: "Übersetzungen" },
396
- summary: { en: "i18n strings keyed by locale." },
397
- category: "meta",
398
- editability: "static",
399
- singleton: true,
400
- fields: [
401
- {
402
- path: "keys",
403
- label: { en: "Locales", de: "Sprachen" },
404
- input: "key-value-map",
405
- keyPlaceholder: "en",
406
- valueInput: "json-readonly",
407
- required: true,
408
- },
409
- ],
410
- };
411
-
412
- const metricSchema: PatternFormSchema = {
413
- kind: "metric",
414
- label: { en: "Metric", de: "Metrik" },
415
- summary: { en: "Prometheus-style counter / gauge / histogram." },
416
- category: "meta",
417
- editability: "static",
418
- fields: [
419
- {
420
- path: "shortName",
421
- label: { en: "Short name", de: "Kurzname" },
422
- hint: { en: "Snake-case; auto-prefixed with kumiko_<feature>_." },
423
- input: "text",
424
- required: true,
425
- },
426
- {
427
- path: "options.type",
428
- label: { en: "Type", de: "Typ" },
429
- input: "select",
430
- options: [
431
- { value: "counter", label: { en: "Counter" } },
432
- { value: "gauge", label: { en: "Gauge" } },
433
- { value: "histogram", label: { en: "Histogram" } },
434
- ],
435
- required: true,
436
- },
437
- ],
438
- };
439
-
440
- const secretSchema: PatternFormSchema = {
441
- kind: "secret",
442
- label: { en: "Secret", de: "Secret" },
443
- summary: { en: "Tenant-scoped encrypted credential." },
444
- category: "meta",
445
- editability: "static",
446
- fields: [
447
- {
448
- path: "shortName",
449
- label: { en: "Short name", de: "Kurzname" },
450
- input: "text",
451
- required: true,
452
- },
453
- {
454
- path: "options.label",
455
- label: { en: "UI label (i18n)", de: "UI-Label (i18n)" },
456
- hint: { en: "{ en: 'API Key', de: 'API-Schlüssel' }" },
457
- input: "json-readonly",
458
- required: true,
459
- },
460
- {
461
- path: "options.scope",
462
- label: { en: "Scope", de: "Geltungsbereich" },
463
- input: "select",
464
- options: [
465
- { value: "tenant", label: { en: "Tenant" } },
466
- { value: "system", label: { en: "System" } },
467
- ],
468
- },
469
- ],
470
- };
471
-
472
- const claimKeySchema: PatternFormSchema = {
473
- kind: "claimKey",
474
- label: { en: "Claim key", de: "Claim-Key" },
475
- summary: { en: "Typed JWT claim contributed at login." },
476
- category: "meta",
477
- editability: "static",
478
- fields: [
479
- {
480
- path: "shortName",
481
- label: { en: "Short name", de: "Kurzname" },
482
- input: "text",
483
- required: true,
484
- },
485
- {
486
- path: "claimType",
487
- label: { en: "Type", de: "Typ" },
488
- input: "select",
489
- options: CLAIM_KEY_TYPE_OPTIONS,
490
- required: true,
491
- },
492
- ],
493
- };
494
-
495
- const referenceDataSchema: PatternFormSchema = {
496
- kind: "referenceData",
497
- label: { en: "Reference data", de: "Referenzdaten" },
498
- summary: { en: "Seed data for an entity (currencies, categories, …)." },
499
- category: "data",
500
- editability: "static",
501
- fields: [
502
- {
503
- path: "entityName",
504
- label: { en: "Entity", de: "Entität" },
505
- input: "entity-ref",
506
- required: true,
507
- },
508
- {
509
- path: "data",
510
- label: { en: "Rows", de: "Zeilen" },
511
- input: "json-readonly",
512
- required: true,
513
- },
514
- {
515
- path: "upsertKey",
516
- label: { en: "Upsert key", de: "Upsert-Key" },
517
- hint: { en: "Field name to deduplicate on." },
518
- input: "text",
519
- },
520
- ],
521
- };
522
-
523
- const useExtensionSchema: PatternFormSchema = {
524
- kind: "useExtension",
525
- label: { en: "Use extension", de: "Erweiterung nutzen" },
526
- summary: { en: "Apply a registered registrar-extension to an entity." },
527
- category: "advanced",
528
- editability: "static",
529
- fields: [
530
- {
531
- path: "extensionName",
532
- label: { en: "Extension name", de: "Erweiterungs-Name" },
533
- input: "text",
534
- required: true,
535
- },
536
- {
537
- path: "entityName",
538
- label: { en: "Entity", de: "Entität" },
539
- input: "entity-ref",
540
- required: true,
541
- },
542
- {
543
- path: "options",
544
- label: { en: "Options", de: "Optionen" },
545
- input: "json-readonly",
546
- },
547
- ],
548
- };
549
-
550
- // --- Mixed patterns (header form + opaque body source) --------------------
551
-
552
- const screenSchema: PatternFormSchema = {
553
- kind: "screen",
554
- label: { en: "Screen", de: "Bildschirm" },
555
- summary: { en: "List / edit / detail / custom UI surface." },
556
- category: "ui",
557
- editability: "mixed",
558
- fields: [
559
- {
560
- path: "definition.id",
561
- label: { en: "ID", de: "ID" },
562
- input: "text",
563
- required: true,
564
- },
565
- {
566
- path: "definition.type",
567
- label: { en: "Type", de: "Typ" },
568
- input: "select",
569
- options: [
570
- { value: "entityList", label: { en: "Entity list" } },
571
- { value: "entityEdit", label: { en: "Entity edit" } },
572
- { value: "actionForm", label: { en: "Action form" } },
573
- { value: "custom", label: { en: "Custom" } },
574
- ],
575
- required: true,
576
- },
577
- {
578
- path: "definition",
579
- label: { en: "Definition", de: "Definition" },
580
- hint: { en: "Full ScreenDefinition — closures show as code-blocks." },
581
- input: "json-readonly",
582
- },
583
- {
584
- path: "opaqueProps",
585
- label: { en: "Closure paths", de: "Closure-Pfade" },
586
- hint: { en: "JSON-paths whose values are inline closures." },
587
- input: "json-readonly",
588
- readOnly: true,
589
- },
590
- ],
591
- };
592
-
593
- const writeHandlerSchema: PatternFormSchema = {
594
- kind: "writeHandler",
595
- label: { en: "Write handler", de: "Write-Handler" },
596
- summary: { en: "Mutation endpoint with Zod schema + closure body." },
597
- category: "behaviour",
598
- editability: "mixed",
599
- fields: [
600
- {
601
- path: "handlerName",
602
- label: { en: "Name", de: "Name" },
603
- input: "text",
604
- required: true,
605
- placeholder: "task:create",
606
- },
607
- {
608
- path: "schemaSource",
609
- label: { en: "Zod schema (source)", de: "Zod-Schema (Source)" },
610
- input: "code-block",
611
- language: "zod",
612
- readOnly: true,
613
- },
614
- {
615
- path: "handlerBody",
616
- label: { en: "Handler body (source)", de: "Handler-Body (Source)" },
617
- input: "code-block",
618
- language: "typescript",
619
- readOnly: true,
620
- },
621
- accessRuleField,
622
- {
623
- path: "rateLimit",
624
- label: { en: "Rate limit", de: "Rate-Limit" },
625
- input: "json-readonly",
626
- },
627
- {
628
- path: "unsafeSkipTransitionGuard",
629
- label: { en: "Skip transition guard", de: "Übergangs-Guard überspringen" },
630
- input: "boolean",
631
- },
632
- ],
633
- };
634
-
635
- const queryHandlerSchema: PatternFormSchema = {
636
- kind: "queryHandler",
637
- label: { en: "Query handler", de: "Query-Handler" },
638
- summary: { en: "Read endpoint with Zod schema + closure body." },
639
- category: "behaviour",
640
- editability: "mixed",
641
- fields: [
642
- {
643
- path: "handlerName",
644
- label: { en: "Name", de: "Name" },
645
- input: "text",
646
- required: true,
647
- placeholder: "task:list",
648
- },
649
- {
650
- path: "schemaSource",
651
- label: { en: "Zod schema (source)", de: "Zod-Schema (Source)" },
652
- input: "code-block",
653
- language: "zod",
654
- readOnly: true,
655
- },
656
- {
657
- path: "handlerBody",
658
- label: { en: "Handler body (source)", de: "Handler-Body (Source)" },
659
- input: "code-block",
660
- language: "typescript",
661
- readOnly: true,
662
- },
663
- accessRuleField,
664
- {
665
- path: "rateLimit",
666
- label: { en: "Rate limit", de: "Rate-Limit" },
667
- input: "json-readonly",
668
- },
669
- ],
670
- };
671
-
672
- const hookSchema: PatternFormSchema = {
673
- kind: "hook",
674
- label: { en: "Lifecycle hook", de: "Lifecycle-Hook" },
675
- summary: { en: "Pre-/post-save/delete/query hook on one or more entities." },
676
- category: "behaviour",
677
- editability: "mixed",
678
- fields: [
679
- {
680
- path: "hookType",
681
- label: { en: "Type", de: "Typ" },
682
- input: "select",
683
- options: HOOK_TYPE_OPTIONS,
684
- required: true,
685
- },
686
- {
687
- path: "target",
688
- label: { en: "Target entity (or list)", de: "Ziel-Entität (oder Liste)" },
689
- input: "json-readonly",
690
- required: true,
691
- },
692
- {
693
- path: "fnBody",
694
- label: { en: "Hook body (source)", de: "Hook-Body (Source)" },
695
- input: "code-block",
696
- language: "typescript",
697
- readOnly: true,
698
- },
699
- {
700
- path: "phase",
701
- label: { en: "Phase", de: "Phase" },
702
- input: "select",
703
- options: [
704
- { value: "inTransaction", label: { en: "In transaction" } },
705
- { value: "afterCommit", label: { en: "After commit" } },
706
- ],
707
- },
708
- ],
709
- };
710
-
711
- const entityHookSchema: PatternFormSchema = {
712
- kind: "entityHook",
713
- label: { en: "Entity hook", de: "Entity-Hook" },
714
- summary: { en: "Hook scoped to a single entity (no cross-entity targets)." },
715
- category: "behaviour",
716
- editability: "mixed",
717
- fields: [
718
- {
719
- path: "hookType",
720
- label: { en: "Type", de: "Typ" },
721
- input: "select",
722
- options: ENTITY_HOOK_TYPE_OPTIONS,
723
- required: true,
724
- },
725
- {
726
- path: "entityName",
727
- label: { en: "Entity", de: "Entität" },
728
- input: "entity-ref",
729
- required: true,
730
- },
731
- {
732
- path: "fnBody",
733
- label: { en: "Hook body (source)", de: "Hook-Body (Source)" },
734
- input: "code-block",
735
- language: "typescript",
736
- readOnly: true,
737
- },
738
- {
739
- path: "phase",
740
- label: { en: "Phase", de: "Phase" },
741
- input: "select",
742
- options: [
743
- { value: "inTransaction", label: { en: "In transaction" } },
744
- { value: "afterCommit", label: { en: "After commit" } },
745
- ],
746
- },
747
- ],
748
- };
749
-
750
- const jobSchema: PatternFormSchema = {
751
- kind: "job",
752
- label: { en: "Job", de: "Job" },
753
- summary: { en: "Scheduled background job (cron / interval)." },
754
- category: "background",
755
- editability: "mixed",
756
- fields: [
757
- {
758
- path: "jobName",
759
- label: { en: "Name", de: "Name" },
760
- input: "text",
761
- required: true,
762
- },
763
- {
764
- path: "options",
765
- label: { en: "Options", de: "Optionen" },
766
- hint: { en: "Schedule, idempotencyKey, runIn, …" },
767
- input: "json-readonly",
768
- },
769
- {
770
- path: "handlerBody",
771
- label: { en: "Handler body (source)", de: "Handler-Body (Source)" },
772
- input: "code-block",
773
- language: "typescript",
774
- readOnly: true,
775
- },
776
- ],
777
- };
778
-
779
- const notificationSchema: PatternFormSchema = {
780
- kind: "notification",
781
- label: { en: "Notification", de: "Benachrichtigung" },
782
- summary: { en: "Trigger → recipient/data/template pipeline." },
783
- category: "cross-cutting",
784
- editability: "mixed",
785
- fields: [
786
- {
787
- path: "notificationName",
788
- label: { en: "Name", de: "Name" },
789
- input: "text",
790
- required: true,
791
- },
792
- {
793
- path: "trigger.on",
794
- label: { en: "Trigger on entity", de: "Trigger-Entität" },
795
- input: "entity-ref",
796
- required: true,
797
- },
798
- {
799
- path: "recipientBody",
800
- label: { en: "Recipient (source)", de: "Empfänger (Source)" },
801
- input: "code-block",
802
- language: "typescript",
803
- readOnly: true,
804
- },
805
- {
806
- path: "dataBody",
807
- label: { en: "Data (source)", de: "Daten (Source)" },
808
- input: "code-block",
809
- language: "typescript",
810
- readOnly: true,
811
- },
812
- {
813
- path: "templates",
814
- label: { en: "Templates per channel", de: "Templates pro Kanal" },
815
- input: "key-value-map",
816
- keyPlaceholder: "email",
817
- valueInput: "code-block",
818
- },
819
- ],
820
- };
821
-
822
- const authClaimsSchema: PatternFormSchema = {
823
- kind: "authClaims",
824
- label: { en: "Auth claims hook", de: "Auth-Claims-Hook" },
825
- summary: { en: "Contributes claims into SessionUser at login." },
826
- category: "cross-cutting",
827
- editability: "opaque",
828
- singleton: true,
829
- fields: [
830
- {
831
- path: "fnBody",
832
- label: { en: "Handler body (source)", de: "Handler-Body (Source)" },
833
- input: "code-block",
834
- language: "typescript",
835
- readOnly: true,
836
- },
837
- ],
838
- };
839
-
840
- const httpRouteSchema: PatternFormSchema = {
841
- kind: "httpRoute",
842
- label: { en: "HTTP route", de: "HTTP-Route" },
843
- summary: { en: "Custom HTTP endpoint outside the dispatcher." },
844
- category: "cross-cutting",
845
- editability: "mixed",
846
- fields: [
847
- {
848
- path: "method",
849
- label: { en: "Method", de: "Methode" },
850
- input: "select",
851
- options: HTTP_METHOD_OPTIONS,
852
- required: true,
853
- },
854
- {
855
- path: "path",
856
- label: { en: "Path", de: "Pfad" },
857
- input: "text",
858
- required: true,
859
- placeholder: "/health",
860
- },
861
- {
862
- path: "anonymous",
863
- label: { en: "Anonymous (no auth)", de: "Anonym (keine Auth)" },
864
- input: "boolean",
865
- },
866
- {
867
- path: "handlerBody",
868
- label: { en: "Handler body (source)", de: "Handler-Body (Source)" },
869
- input: "code-block",
870
- language: "typescript",
871
- readOnly: true,
872
- },
873
- ],
874
- };
875
-
876
- const projectionSchema: PatternFormSchema = {
877
- kind: "projection",
878
- label: { en: "Projection", de: "Projection" },
879
- summary: { en: "Single-stream read-model in-TX with the source aggregate." },
880
- category: "background",
881
- editability: "mixed",
882
- fields: [
883
- {
884
- path: "name",
885
- label: { en: "Name", de: "Name" },
886
- input: "text",
887
- required: true,
888
- },
889
- {
890
- path: "sourceEntity",
891
- label: { en: "Source entity (or list)", de: "Quell-Entität (oder Liste)" },
892
- input: "json-readonly",
893
- required: true,
894
- },
895
- {
896
- path: "applyBodies",
897
- label: { en: "Apply per event-type", de: "Apply pro Event-Type" },
898
- input: "key-value-map",
899
- keyPlaceholder: "feature:event:type",
900
- valueInput: "code-block",
901
- required: true,
902
- },
903
- ],
904
- };
905
-
906
- const multiStreamProjectionSchema: PatternFormSchema = {
907
- kind: "multiStreamProjection",
908
- label: { en: "Multi-stream projection", de: "Multi-Stream-Projection" },
909
- summary: { en: "Async cross-aggregate read-model." },
910
- category: "background",
911
- editability: "mixed",
912
- fields: [
913
- {
914
- path: "name",
915
- label: { en: "Name", de: "Name" },
916
- input: "text",
917
- required: true,
918
- },
919
- {
920
- path: "applyBodies",
921
- label: { en: "Apply per event-type", de: "Apply pro Event-Type" },
922
- input: "key-value-map",
923
- keyPlaceholder: "feature:event:type",
924
- valueInput: "code-block",
925
- required: true,
926
- },
927
- {
928
- path: "errorMode",
929
- label: { en: "Error mode", de: "Fehler-Modus" },
930
- input: "select",
931
- options: [
932
- { value: "skip", label: { en: "Skip" } },
933
- { value: "halt", label: { en: "Halt" } },
934
- { value: "dead-letter", label: { en: "Dead-letter" } },
935
- ],
936
- },
937
- {
938
- path: "runIn",
939
- label: { en: "Run in", de: "Läuft in" },
940
- input: "select",
941
- options: [
942
- { value: "tenant", label: { en: "Tenant scope" } },
943
- { value: "system", label: { en: "System scope" } },
944
- ],
945
- },
946
- {
947
- path: "delivery",
948
- label: { en: "Delivery", de: "Auslieferung" },
949
- input: "select",
950
- options: [
951
- { value: "shared", label: { en: "Shared" } },
952
- { value: "per-instance", label: { en: "Per instance" } },
953
- ],
954
- },
955
- ],
956
- };
957
-
958
- const defineEventSchema: PatternFormSchema = {
959
- kind: "defineEvent",
960
- label: { en: "Define event", de: "Event definieren" },
961
- summary: { en: "Register an event payload shape with version." },
962
- category: "data",
963
- editability: "mixed",
964
- fields: [
965
- {
966
- path: "eventName",
967
- label: { en: "Name", de: "Name" },
968
- input: "text",
969
- required: true,
970
- placeholder: "taskCompleted",
971
- },
972
- {
973
- path: "schemaSource",
974
- label: { en: "Zod schema (source)", de: "Zod-Schema (Source)" },
975
- input: "code-block",
976
- language: "zod",
977
- readOnly: true,
978
- },
979
- {
980
- path: "version",
981
- label: { en: "Version", de: "Version" },
982
- input: "number",
983
- min: 1,
984
- },
985
- ],
986
- };
987
-
988
- const eventMigrationSchema: PatternFormSchema = {
989
- kind: "eventMigration",
990
- label: { en: "Event migration", de: "Event-Migration" },
991
- summary: { en: "Step-wise transform between event versions." },
992
- category: "data",
993
- editability: "mixed",
994
- fields: [
995
- {
996
- path: "eventName",
997
- label: { en: "Event", de: "Event" },
998
- input: "text",
999
- required: true,
1000
- },
1001
- {
1002
- path: "fromVersion",
1003
- label: { en: "From version", de: "Von Version" },
1004
- input: "number",
1005
- min: 1,
1006
- required: true,
1007
- },
1008
- {
1009
- path: "toVersion",
1010
- label: { en: "To version", de: "Auf Version" },
1011
- input: "number",
1012
- min: 2,
1013
- required: true,
1014
- },
1015
- {
1016
- path: "transformBody",
1017
- label: { en: "Transform (source)", de: "Transform (Source)" },
1018
- input: "code-block",
1019
- language: "typescript",
1020
- readOnly: true,
1021
- },
1022
- ],
1023
- };
1024
-
1025
- // --- Opaque patterns (entire pattern is read-only code) -------------------
1026
-
1027
- const extendsRegistrarSchema: PatternFormSchema = {
1028
- kind: "extendsRegistrar",
1029
- label: { en: "Registrar extension", de: "Registrar-Erweiterung" },
1030
- summary: { en: "Meta-programming surface — the Designer treats it as code." },
1031
- category: "advanced",
1032
- editability: "opaque",
1033
- fields: [
1034
- {
1035
- path: "extensionName",
1036
- label: { en: "Name", de: "Name" },
1037
- input: "text",
1038
- required: true,
1039
- readOnly: true,
1040
- },
1041
- {
1042
- path: "defBody",
1043
- label: { en: "Definition body (source)", de: "Definitions-Body (Source)" },
1044
- input: "code-block",
1045
- language: "typescript",
1046
- readOnly: true,
1047
- },
1048
- ],
1049
- };
1050
-
1051
- const usesApiSchema: PatternFormSchema = {
1052
- kind: "usesApi",
1053
- label: { en: "Uses API", de: "Nutzt API" },
1054
- summary: {
1055
- en: "Cross-feature handler-ID dependency. Boot fails if no other feature exposes it.",
1056
- },
1057
- category: "advanced",
1058
- editability: "static",
1059
- fields: [
1060
- {
1061
- path: "apiName",
1062
- label: { en: "API name", de: "API-Name" },
1063
- input: "text",
1064
- required: true,
1065
- },
1066
- ],
1067
- };
1068
-
1069
- const exposesApiSchema: PatternFormSchema = {
1070
- kind: "exposesApi",
1071
- label: { en: "Exposes API", de: "Stellt API bereit" },
1072
- summary: { en: "Declares this feature provides a handler matching the cross-feature contract." },
1073
- category: "advanced",
1074
- editability: "static",
1075
- fields: [
1076
- {
1077
- path: "apiName",
1078
- label: { en: "API name", de: "API-Name" },
1079
- input: "text",
1080
- required: true,
1081
- },
1082
- ],
1083
- };
1084
-
1085
- // Visual-Tree pattern schemas. treeActions is a static map (Designer
1086
- // renders the action-name → ActionDef pairs as a nested form), tree is
1087
- // closure-only (Designer shows the provider body as read-only code).
1088
- const treeActionsSchema: PatternFormSchema = {
1089
- kind: "treeActions",
1090
- label: { en: "Tree actions", de: "Tree-Actions" },
1091
- summary: { en: "Action verbs the Visual-Tree dispatches via buildTarget." },
1092
- category: "ui",
1093
- editability: "static",
1094
- singleton: true,
1095
- fields: [
1096
- {
1097
- path: "definitions",
1098
- label: { en: "Action definitions", de: "Action-Definitionen" },
1099
- input: "json-readonly",
1100
- readOnly: true,
1101
- },
1102
- ],
1103
- };
1104
-
1105
- const envSchemaSchema: PatternFormSchema = {
1106
- kind: "envSchema",
1107
- label: { en: "Env schema", de: "Env-Schema" },
1108
- summary: {
1109
- en: "Zod-object declaring this feature's required env-vars. Apps merge it via composeEnvSchema for boot-validation.",
1110
- },
1111
- category: "advanced",
1112
- editability: "opaque",
1113
- fields: [
1114
- {
1115
- path: "schemaBody",
1116
- label: { en: "Schema", de: "Schema" },
1117
- input: "json-readonly",
1118
- readOnly: true,
1119
- },
1120
- ],
1121
- };
1122
-
1123
- const unknownSchema: PatternFormSchema = {
1124
- kind: "unknown",
1125
- label: { en: "Unknown call", de: "Unbekannter Call" },
1126
- summary: { en: "Parser doesn't recognise this r.* method — read-only." },
1127
- category: "advanced",
1128
- editability: "opaque",
1129
- fields: [
1130
- {
1131
- path: "methodName",
1132
- label: { en: "Method", de: "Methode" },
1133
- input: "text",
1134
- readOnly: true,
1135
- },
1136
- {
1137
- path: "source",
1138
- label: { en: "Source", de: "Source" },
1139
- input: "json-readonly",
1140
- readOnly: true,
1141
- },
1142
- ],
1143
- };
1144
-
1145
- // =============================================================================
1146
- // Catalogue — exhaustive map keyed by FeaturePatternKind
1147
- // =============================================================================
17
+ import {
18
+ authClaimsSchema,
19
+ defineEventSchema,
20
+ entityHookSchema,
21
+ eventMigrationSchema,
22
+ hookSchema,
23
+ httpRouteSchema,
24
+ jobSchema,
25
+ multiStreamProjectionSchema,
26
+ notificationSchema,
27
+ projectionSchema,
28
+ queryHandlerSchema,
29
+ screenSchema,
30
+ writeHandlerSchema,
31
+ } from "./mixed-schemas";
32
+ import {
33
+ envSchemaSchema,
34
+ exposesApiSchema,
35
+ extendsRegistrarSchema,
36
+ treeActionsSchema,
37
+ unknownSchema,
38
+ usesApiSchema,
39
+ } from "./opaque-schemas";
40
+ import {
41
+ claimKeySchema,
42
+ configSchema,
43
+ describeSchema,
44
+ entitySchema,
45
+ metricSchema,
46
+ navSchema,
47
+ optionalRequiresSchema,
48
+ readsConfigSchema,
49
+ referenceDataSchema,
50
+ relationSchema,
51
+ requiresSchema,
52
+ secretSchema,
53
+ systemScopeSchema,
54
+ toggleableSchema,
55
+ translationsSchema,
56
+ uiHintsSchema,
57
+ useExtensionSchema,
58
+ workspaceSchema,
59
+ } from "./static-schemas";
60
+ import type { PatternCategory, PatternFormSchema } from "./types";
1148
61
 
1149
62
  export const PATTERN_LIBRARY: Readonly<Record<FeaturePatternKind, PatternFormSchema>> = {
1150
63
  requires: requiresSchema,