@cosmicdrift/kumiko-framework 0.146.3 → 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
@@ -0,0 +1,484 @@
1
+ // Mixed pattern schemas (header form + opaque body source).
2
+
3
+ import {
4
+ accessRuleField,
5
+ ENTITY_HOOK_TYPE_OPTIONS,
6
+ HOOK_TYPE_OPTIONS,
7
+ HTTP_METHOD_OPTIONS,
8
+ } from "./shared-fields";
9
+ import type { PatternFormSchema } from "./types";
10
+
11
+ // --- Mixed patterns (header form + opaque body source) --------------------
12
+
13
+ export const screenSchema: PatternFormSchema = {
14
+ kind: "screen",
15
+ label: { en: "Screen", de: "Bildschirm" },
16
+ summary: { en: "List / edit / detail / custom UI surface." },
17
+ category: "ui",
18
+ editability: "mixed",
19
+ fields: [
20
+ {
21
+ path: "definition.id",
22
+ label: { en: "ID", de: "ID" },
23
+ input: "text",
24
+ required: true,
25
+ },
26
+ {
27
+ path: "definition.type",
28
+ label: { en: "Type", de: "Typ" },
29
+ input: "select",
30
+ options: [
31
+ { value: "entityList", label: { en: "Entity list" } },
32
+ { value: "entityEdit", label: { en: "Entity edit" } },
33
+ { value: "actionForm", label: { en: "Action form" } },
34
+ { value: "custom", label: { en: "Custom" } },
35
+ ],
36
+ required: true,
37
+ },
38
+ {
39
+ path: "definition",
40
+ label: { en: "Definition", de: "Definition" },
41
+ hint: { en: "Full ScreenDefinition — closures show as code-blocks." },
42
+ input: "json-readonly",
43
+ },
44
+ {
45
+ path: "opaqueProps",
46
+ label: { en: "Closure paths", de: "Closure-Pfade" },
47
+ hint: { en: "JSON-paths whose values are inline closures." },
48
+ input: "json-readonly",
49
+ readOnly: true,
50
+ },
51
+ ],
52
+ };
53
+
54
+ export const writeHandlerSchema: PatternFormSchema = {
55
+ kind: "writeHandler",
56
+ label: { en: "Write handler", de: "Write-Handler" },
57
+ summary: { en: "Mutation endpoint with Zod schema + closure body." },
58
+ category: "behaviour",
59
+ editability: "mixed",
60
+ fields: [
61
+ {
62
+ path: "handlerName",
63
+ label: { en: "Name", de: "Name" },
64
+ input: "text",
65
+ required: true,
66
+ placeholder: "task:create",
67
+ },
68
+ {
69
+ path: "schemaSource",
70
+ label: { en: "Zod schema (source)", de: "Zod-Schema (Source)" },
71
+ input: "code-block",
72
+ language: "zod",
73
+ readOnly: true,
74
+ },
75
+ {
76
+ path: "handlerBody",
77
+ label: { en: "Handler body (source)", de: "Handler-Body (Source)" },
78
+ input: "code-block",
79
+ language: "typescript",
80
+ readOnly: true,
81
+ },
82
+ accessRuleField,
83
+ {
84
+ path: "rateLimit",
85
+ label: { en: "Rate limit", de: "Rate-Limit" },
86
+ input: "json-readonly",
87
+ },
88
+ {
89
+ path: "unsafeSkipTransitionGuard",
90
+ label: { en: "Skip transition guard", de: "Übergangs-Guard überspringen" },
91
+ input: "boolean",
92
+ },
93
+ ],
94
+ };
95
+
96
+ export const queryHandlerSchema: PatternFormSchema = {
97
+ kind: "queryHandler",
98
+ label: { en: "Query handler", de: "Query-Handler" },
99
+ summary: { en: "Read endpoint with Zod schema + closure body." },
100
+ category: "behaviour",
101
+ editability: "mixed",
102
+ fields: [
103
+ {
104
+ path: "handlerName",
105
+ label: { en: "Name", de: "Name" },
106
+ input: "text",
107
+ required: true,
108
+ placeholder: "task:list",
109
+ },
110
+ {
111
+ path: "schemaSource",
112
+ label: { en: "Zod schema (source)", de: "Zod-Schema (Source)" },
113
+ input: "code-block",
114
+ language: "zod",
115
+ readOnly: true,
116
+ },
117
+ {
118
+ path: "handlerBody",
119
+ label: { en: "Handler body (source)", de: "Handler-Body (Source)" },
120
+ input: "code-block",
121
+ language: "typescript",
122
+ readOnly: true,
123
+ },
124
+ accessRuleField,
125
+ {
126
+ path: "rateLimit",
127
+ label: { en: "Rate limit", de: "Rate-Limit" },
128
+ input: "json-readonly",
129
+ },
130
+ ],
131
+ };
132
+
133
+ export const hookSchema: PatternFormSchema = {
134
+ kind: "hook",
135
+ label: { en: "Lifecycle hook", de: "Lifecycle-Hook" },
136
+ summary: { en: "Pre-/post-save/delete/query hook on one or more entities." },
137
+ category: "behaviour",
138
+ editability: "mixed",
139
+ fields: [
140
+ {
141
+ path: "hookType",
142
+ label: { en: "Type", de: "Typ" },
143
+ input: "select",
144
+ options: HOOK_TYPE_OPTIONS,
145
+ required: true,
146
+ },
147
+ {
148
+ path: "target",
149
+ label: { en: "Target entity (or list)", de: "Ziel-Entität (oder Liste)" },
150
+ input: "json-readonly",
151
+ required: true,
152
+ },
153
+ {
154
+ path: "fnBody",
155
+ label: { en: "Hook body (source)", de: "Hook-Body (Source)" },
156
+ input: "code-block",
157
+ language: "typescript",
158
+ readOnly: true,
159
+ },
160
+ {
161
+ path: "phase",
162
+ label: { en: "Phase", de: "Phase" },
163
+ input: "select",
164
+ options: [
165
+ { value: "inTransaction", label: { en: "In transaction" } },
166
+ { value: "afterCommit", label: { en: "After commit" } },
167
+ ],
168
+ },
169
+ ],
170
+ };
171
+
172
+ export const entityHookSchema: PatternFormSchema = {
173
+ kind: "entityHook",
174
+ label: { en: "Entity hook", de: "Entity-Hook" },
175
+ summary: { en: "Hook scoped to a single entity (no cross-entity targets)." },
176
+ category: "behaviour",
177
+ editability: "mixed",
178
+ fields: [
179
+ {
180
+ path: "hookType",
181
+ label: { en: "Type", de: "Typ" },
182
+ input: "select",
183
+ options: ENTITY_HOOK_TYPE_OPTIONS,
184
+ required: true,
185
+ },
186
+ {
187
+ path: "entityName",
188
+ label: { en: "Entity", de: "Entität" },
189
+ input: "entity-ref",
190
+ required: true,
191
+ },
192
+ {
193
+ path: "fnBody",
194
+ label: { en: "Hook body (source)", de: "Hook-Body (Source)" },
195
+ input: "code-block",
196
+ language: "typescript",
197
+ readOnly: true,
198
+ },
199
+ {
200
+ path: "phase",
201
+ label: { en: "Phase", de: "Phase" },
202
+ input: "select",
203
+ options: [
204
+ { value: "inTransaction", label: { en: "In transaction" } },
205
+ { value: "afterCommit", label: { en: "After commit" } },
206
+ ],
207
+ },
208
+ ],
209
+ };
210
+
211
+ export const jobSchema: PatternFormSchema = {
212
+ kind: "job",
213
+ label: { en: "Job", de: "Job" },
214
+ summary: { en: "Scheduled background job (cron / interval)." },
215
+ category: "background",
216
+ editability: "mixed",
217
+ fields: [
218
+ {
219
+ path: "jobName",
220
+ label: { en: "Name", de: "Name" },
221
+ input: "text",
222
+ required: true,
223
+ },
224
+ {
225
+ path: "options",
226
+ label: { en: "Options", de: "Optionen" },
227
+ hint: { en: "Schedule, idempotencyKey, runIn, …" },
228
+ input: "json-readonly",
229
+ },
230
+ {
231
+ path: "handlerBody",
232
+ label: { en: "Handler body (source)", de: "Handler-Body (Source)" },
233
+ input: "code-block",
234
+ language: "typescript",
235
+ readOnly: true,
236
+ },
237
+ ],
238
+ };
239
+
240
+ export const notificationSchema: PatternFormSchema = {
241
+ kind: "notification",
242
+ label: { en: "Notification", de: "Benachrichtigung" },
243
+ summary: { en: "Trigger → recipient/data/template pipeline." },
244
+ category: "cross-cutting",
245
+ editability: "mixed",
246
+ fields: [
247
+ {
248
+ path: "notificationName",
249
+ label: { en: "Name", de: "Name" },
250
+ input: "text",
251
+ required: true,
252
+ },
253
+ {
254
+ path: "trigger.on",
255
+ label: { en: "Trigger on entity", de: "Trigger-Entität" },
256
+ input: "entity-ref",
257
+ required: true,
258
+ },
259
+ {
260
+ path: "recipientBody",
261
+ label: { en: "Recipient (source)", de: "Empfänger (Source)" },
262
+ input: "code-block",
263
+ language: "typescript",
264
+ readOnly: true,
265
+ },
266
+ {
267
+ path: "dataBody",
268
+ label: { en: "Data (source)", de: "Daten (Source)" },
269
+ input: "code-block",
270
+ language: "typescript",
271
+ readOnly: true,
272
+ },
273
+ {
274
+ path: "templates",
275
+ label: { en: "Templates per channel", de: "Templates pro Kanal" },
276
+ input: "key-value-map",
277
+ keyPlaceholder: "email",
278
+ valueInput: "code-block",
279
+ },
280
+ ],
281
+ };
282
+
283
+ export const authClaimsSchema: PatternFormSchema = {
284
+ kind: "authClaims",
285
+ label: { en: "Auth claims hook", de: "Auth-Claims-Hook" },
286
+ summary: { en: "Contributes claims into SessionUser at login." },
287
+ category: "cross-cutting",
288
+ editability: "opaque",
289
+ singleton: true,
290
+ fields: [
291
+ {
292
+ path: "fnBody",
293
+ label: { en: "Handler body (source)", de: "Handler-Body (Source)" },
294
+ input: "code-block",
295
+ language: "typescript",
296
+ readOnly: true,
297
+ },
298
+ ],
299
+ };
300
+
301
+ export const httpRouteSchema: PatternFormSchema = {
302
+ kind: "httpRoute",
303
+ label: { en: "HTTP route", de: "HTTP-Route" },
304
+ summary: { en: "Custom HTTP endpoint outside the dispatcher." },
305
+ category: "cross-cutting",
306
+ editability: "mixed",
307
+ fields: [
308
+ {
309
+ path: "method",
310
+ label: { en: "Method", de: "Methode" },
311
+ input: "select",
312
+ options: HTTP_METHOD_OPTIONS,
313
+ required: true,
314
+ },
315
+ {
316
+ path: "path",
317
+ label: { en: "Path", de: "Pfad" },
318
+ input: "text",
319
+ required: true,
320
+ placeholder: "/health",
321
+ },
322
+ {
323
+ path: "anonymous",
324
+ label: { en: "Anonymous (no auth)", de: "Anonym (keine Auth)" },
325
+ input: "boolean",
326
+ },
327
+ {
328
+ path: "handlerBody",
329
+ label: { en: "Handler body (source)", de: "Handler-Body (Source)" },
330
+ input: "code-block",
331
+ language: "typescript",
332
+ readOnly: true,
333
+ },
334
+ ],
335
+ };
336
+
337
+ export const projectionSchema: PatternFormSchema = {
338
+ kind: "projection",
339
+ label: { en: "Projection", de: "Projection" },
340
+ summary: { en: "Single-stream read-model in-TX with the source aggregate." },
341
+ category: "background",
342
+ editability: "mixed",
343
+ fields: [
344
+ {
345
+ path: "name",
346
+ label: { en: "Name", de: "Name" },
347
+ input: "text",
348
+ required: true,
349
+ },
350
+ {
351
+ path: "sourceEntity",
352
+ label: { en: "Source entity (or list)", de: "Quell-Entität (oder Liste)" },
353
+ input: "json-readonly",
354
+ required: true,
355
+ },
356
+ {
357
+ path: "applyBodies",
358
+ label: { en: "Apply per event-type", de: "Apply pro Event-Type" },
359
+ input: "key-value-map",
360
+ keyPlaceholder: "feature:event:type",
361
+ valueInput: "code-block",
362
+ required: true,
363
+ },
364
+ ],
365
+ };
366
+
367
+ export const multiStreamProjectionSchema: PatternFormSchema = {
368
+ kind: "multiStreamProjection",
369
+ label: { en: "Multi-stream projection", de: "Multi-Stream-Projection" },
370
+ summary: { en: "Async cross-aggregate read-model." },
371
+ category: "background",
372
+ editability: "mixed",
373
+ fields: [
374
+ {
375
+ path: "name",
376
+ label: { en: "Name", de: "Name" },
377
+ input: "text",
378
+ required: true,
379
+ },
380
+ {
381
+ path: "applyBodies",
382
+ label: { en: "Apply per event-type", de: "Apply pro Event-Type" },
383
+ input: "key-value-map",
384
+ keyPlaceholder: "feature:event:type",
385
+ valueInput: "code-block",
386
+ required: true,
387
+ },
388
+ {
389
+ path: "errorMode",
390
+ label: { en: "Error mode", de: "Fehler-Modus" },
391
+ input: "select",
392
+ options: [
393
+ { value: "skip", label: { en: "Skip" } },
394
+ { value: "halt", label: { en: "Halt" } },
395
+ { value: "dead-letter", label: { en: "Dead-letter" } },
396
+ ],
397
+ },
398
+ {
399
+ path: "runIn",
400
+ label: { en: "Run in", de: "Läuft in" },
401
+ input: "select",
402
+ options: [
403
+ { value: "tenant", label: { en: "Tenant scope" } },
404
+ { value: "system", label: { en: "System scope" } },
405
+ ],
406
+ },
407
+ {
408
+ path: "delivery",
409
+ label: { en: "Delivery", de: "Auslieferung" },
410
+ input: "select",
411
+ options: [
412
+ { value: "shared", label: { en: "Shared" } },
413
+ { value: "per-instance", label: { en: "Per instance" } },
414
+ ],
415
+ },
416
+ ],
417
+ };
418
+
419
+ export const defineEventSchema: PatternFormSchema = {
420
+ kind: "defineEvent",
421
+ label: { en: "Define event", de: "Event definieren" },
422
+ summary: { en: "Register an event payload shape with version." },
423
+ category: "data",
424
+ editability: "mixed",
425
+ fields: [
426
+ {
427
+ path: "eventName",
428
+ label: { en: "Name", de: "Name" },
429
+ input: "text",
430
+ required: true,
431
+ placeholder: "taskCompleted",
432
+ },
433
+ {
434
+ path: "schemaSource",
435
+ label: { en: "Zod schema (source)", de: "Zod-Schema (Source)" },
436
+ input: "code-block",
437
+ language: "zod",
438
+ readOnly: true,
439
+ },
440
+ {
441
+ path: "version",
442
+ label: { en: "Version", de: "Version" },
443
+ input: "number",
444
+ min: 1,
445
+ },
446
+ ],
447
+ };
448
+
449
+ export const eventMigrationSchema: PatternFormSchema = {
450
+ kind: "eventMigration",
451
+ label: { en: "Event migration", de: "Event-Migration" },
452
+ summary: { en: "Step-wise transform between event versions." },
453
+ category: "data",
454
+ editability: "mixed",
455
+ fields: [
456
+ {
457
+ path: "eventName",
458
+ label: { en: "Event", de: "Event" },
459
+ input: "text",
460
+ required: true,
461
+ },
462
+ {
463
+ path: "fromVersion",
464
+ label: { en: "From version", de: "Von Version" },
465
+ input: "number",
466
+ min: 1,
467
+ required: true,
468
+ },
469
+ {
470
+ path: "toVersion",
471
+ label: { en: "To version", de: "Auf Version" },
472
+ input: "number",
473
+ min: 2,
474
+ required: true,
475
+ },
476
+ {
477
+ path: "transformBody",
478
+ label: { en: "Transform (source)", de: "Transform (Source)" },
479
+ input: "code-block",
480
+ language: "typescript",
481
+ readOnly: true,
482
+ },
483
+ ],
484
+ };
@@ -0,0 +1,124 @@
1
+ import type { PatternFormSchema } from "./types";
2
+
3
+ // --- Opaque patterns (entire pattern is read-only code) -------------------
4
+
5
+ export const extendsRegistrarSchema: PatternFormSchema = {
6
+ kind: "extendsRegistrar",
7
+ label: { en: "Registrar extension", de: "Registrar-Erweiterung" },
8
+ summary: { en: "Meta-programming surface — the Designer treats it as code." },
9
+ category: "advanced",
10
+ editability: "opaque",
11
+ fields: [
12
+ {
13
+ path: "extensionName",
14
+ label: { en: "Name", de: "Name" },
15
+ input: "text",
16
+ required: true,
17
+ readOnly: true,
18
+ },
19
+ {
20
+ path: "defBody",
21
+ label: { en: "Definition body (source)", de: "Definitions-Body (Source)" },
22
+ input: "code-block",
23
+ language: "typescript",
24
+ readOnly: true,
25
+ },
26
+ ],
27
+ };
28
+
29
+ export const usesApiSchema: PatternFormSchema = {
30
+ kind: "usesApi",
31
+ label: { en: "Uses API", de: "Nutzt API" },
32
+ summary: {
33
+ en: "Cross-feature handler-ID dependency. Boot fails if no other feature exposes it.",
34
+ },
35
+ category: "advanced",
36
+ editability: "static",
37
+ fields: [
38
+ {
39
+ path: "apiName",
40
+ label: { en: "API name", de: "API-Name" },
41
+ input: "text",
42
+ required: true,
43
+ },
44
+ ],
45
+ };
46
+
47
+ export const exposesApiSchema: PatternFormSchema = {
48
+ kind: "exposesApi",
49
+ label: { en: "Exposes API", de: "Stellt API bereit" },
50
+ summary: { en: "Declares this feature provides a handler matching the cross-feature contract." },
51
+ category: "advanced",
52
+ editability: "static",
53
+ fields: [
54
+ {
55
+ path: "apiName",
56
+ label: { en: "API name", de: "API-Name" },
57
+ input: "text",
58
+ required: true,
59
+ },
60
+ ],
61
+ };
62
+
63
+ // Visual-Tree pattern schemas. treeActions is a static map (Designer
64
+ // renders the action-name → ActionDef pairs as a nested form), tree is
65
+ // closure-only (Designer shows the provider body as read-only code).
66
+ export const treeActionsSchema: PatternFormSchema = {
67
+ kind: "treeActions",
68
+ label: { en: "Tree actions", de: "Tree-Actions" },
69
+ summary: { en: "Action verbs the Visual-Tree dispatches via buildTarget." },
70
+ category: "ui",
71
+ editability: "static",
72
+ singleton: true,
73
+ fields: [
74
+ {
75
+ path: "definitions",
76
+ label: { en: "Action definitions", de: "Action-Definitionen" },
77
+ input: "json-readonly",
78
+ readOnly: true,
79
+ },
80
+ ],
81
+ };
82
+
83
+ export const envSchemaSchema: PatternFormSchema = {
84
+ kind: "envSchema",
85
+ label: { en: "Env schema", de: "Env-Schema" },
86
+ summary: {
87
+ en: "Zod-object declaring this feature's required env-vars. Apps merge it via composeEnvSchema for boot-validation.",
88
+ },
89
+ category: "advanced",
90
+ editability: "opaque",
91
+ fields: [
92
+ {
93
+ path: "schemaBody",
94
+ label: { en: "Schema", de: "Schema" },
95
+ input: "json-readonly",
96
+ readOnly: true,
97
+ },
98
+ ],
99
+ };
100
+
101
+ export const unknownSchema: PatternFormSchema = {
102
+ kind: "unknown",
103
+ label: { en: "Unknown call", de: "Unbekannter Call" },
104
+ summary: { en: "Parser doesn't recognise this r.* method — read-only." },
105
+ category: "advanced",
106
+ editability: "opaque",
107
+ fields: [
108
+ {
109
+ path: "methodName",
110
+ label: { en: "Method", de: "Methode" },
111
+ input: "text",
112
+ readOnly: true,
113
+ },
114
+ {
115
+ path: "source",
116
+ label: { en: "Source", de: "Source" },
117
+ input: "json-readonly",
118
+ readOnly: true,
119
+ },
120
+ ],
121
+ };
122
+
123
+ // =============================================================================
124
+ // Catalogue — exhaustive map keyed by FeaturePatternKind
@@ -0,0 +1,80 @@
1
+ import type { FormFieldSpec } from "./types";
2
+
3
+ // =============================================================================
4
+ // Reusable field building blocks
5
+ // =============================================================================
6
+
7
+ export const HOOK_TYPE_OPTIONS = [
8
+ { value: "validation", label: { en: "Validation", de: "Validierung" } },
9
+ { value: "preSave", label: { en: "Pre-Save", de: "Vor Speichern" } },
10
+ { value: "postSave", label: { en: "Post-Save", de: "Nach Speichern" } },
11
+ { value: "preDelete", label: { en: "Pre-Delete", de: "Vor Löschen" } },
12
+ { value: "postDelete", label: { en: "Post-Delete", de: "Nach Löschen" } },
13
+ { value: "preQuery", label: { en: "Pre-Query", de: "Vor Abfrage" } },
14
+ ] as const;
15
+
16
+ export const ENTITY_HOOK_TYPE_OPTIONS = [
17
+ { value: "postSave", label: { en: "Post-Save", de: "Nach Speichern" } },
18
+ { value: "preDelete", label: { en: "Pre-Delete", de: "Vor Löschen" } },
19
+ { value: "postDelete", label: { en: "Post-Delete", de: "Nach Löschen" } },
20
+ ] as const;
21
+
22
+ export const HTTP_METHOD_OPTIONS = [
23
+ { value: "GET", label: { en: "GET" } },
24
+ { value: "POST", label: { en: "POST" } },
25
+ { value: "PUT", label: { en: "PUT" } },
26
+ { value: "PATCH", label: { en: "PATCH" } },
27
+ { value: "DELETE", label: { en: "DELETE" } },
28
+ { value: "HEAD", label: { en: "HEAD" } },
29
+ { value: "OPTIONS", label: { en: "OPTIONS" } },
30
+ ] as const;
31
+
32
+ export const CLAIM_KEY_TYPE_OPTIONS = [
33
+ { value: "string", label: { en: "string" } },
34
+ { value: "number", label: { en: "number" } },
35
+ { value: "boolean", label: { en: "boolean" } },
36
+ { value: "string[]", label: { en: "string[]" } },
37
+ { value: "object", label: { en: "object" } },
38
+ ] as const;
39
+
40
+ export const ID_TYPE_OPTIONS = [
41
+ { value: "uuid", label: { en: "UUID (default)", de: "UUID (Standard)" } },
42
+ { value: "serial", label: { en: "Serial integer", de: "Serial Integer" } },
43
+ ] as const;
44
+
45
+ export const accessRuleField: FormFieldSpec = {
46
+ path: "access",
47
+ label: { en: "Access", de: "Zugriff" },
48
+ hint: { en: "Either a list of role names or `openToAll`." },
49
+ input: "discriminated-union",
50
+ discriminator: "type",
51
+ variants: [
52
+ {
53
+ tag: "roles",
54
+ label: { en: "Role-based", de: "Rollen-basiert" },
55
+ fields: [
56
+ {
57
+ path: "access.roles",
58
+ label: { en: "Roles", de: "Rollen" },
59
+ input: "string-list",
60
+ itemPlaceholder: "Admin",
61
+ },
62
+ ],
63
+ },
64
+ {
65
+ tag: "openToAll",
66
+ label: { en: "Open to all (auth still required)", de: "Für alle (Auth nötig)" },
67
+ fields: [
68
+ {
69
+ path: "access.openToAll",
70
+ label: { en: "Open to all", de: "Offen für alle" },
71
+ input: "boolean",
72
+ },
73
+ ],
74
+ },
75
+ ],
76
+ };
77
+
78
+ // =============================================================================
79
+ // Pattern schemas
80
+ // =============================================================================