@stonecrop/schema 0.13.14 → 0.15.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 (141) hide show
  1. package/README.md +38 -92
  2. package/dist/cli.js +6 -6
  3. package/dist/index-rozX0Luh.js +458 -0
  4. package/dist/index-rozX0Luh.js.map +1 -0
  5. package/dist/index.js +70 -31
  6. package/dist/index.js.map +1 -1
  7. package/dist/schema.d.ts +324 -88
  8. package/dist/src/column-schema.d.ts +17 -8
  9. package/dist/src/column-schema.d.ts.map +1 -1
  10. package/dist/src/component-meta.d.ts +96 -0
  11. package/dist/src/component-meta.d.ts.map +1 -0
  12. package/dist/src/component-meta.js +85 -0
  13. package/dist/src/converter/heuristics.d.ts.map +1 -1
  14. package/dist/src/converter/heuristics.js +6 -13
  15. package/dist/src/converter/index.d.ts +1 -1
  16. package/dist/src/converter/index.d.ts.map +1 -1
  17. package/dist/src/converter/index.js +9 -8
  18. package/dist/src/converter/scalars.d.ts +1 -1
  19. package/dist/src/converter/scalars.d.ts.map +1 -1
  20. package/dist/src/converter/scalars.js +23 -26
  21. package/dist/src/converter/types.d.ts +17 -8
  22. package/dist/src/converter/types.d.ts.map +1 -1
  23. package/dist/src/doctype.d.ts +127 -9
  24. package/dist/src/doctype.d.ts.map +1 -1
  25. package/dist/src/doctype.js +80 -7
  26. package/dist/src/field.d.ts +69 -12
  27. package/dist/src/field.d.ts.map +1 -1
  28. package/dist/src/field.js +45 -7
  29. package/dist/src/index.d.ts +4 -3
  30. package/dist/src/index.d.ts.map +1 -1
  31. package/dist/src/index.js +7 -3
  32. package/package.json +14 -9
  33. package/dist/cli.cjs +0 -41
  34. package/dist/cli.cjs.map +0 -1
  35. package/dist/converter/heuristics.js +0 -258
  36. package/dist/converter/index.js +0 -181
  37. package/dist/converter/scalars.js +0 -86
  38. package/dist/converter/types.js +0 -5
  39. package/dist/converter-CYNFRwPg.js +0 -512
  40. package/dist/converter-CYNFRwPg.js.map +0 -1
  41. package/dist/converter-DNwpowpq.js +0 -459
  42. package/dist/converter-DNwpowpq.js.map +0 -1
  43. package/dist/doctype.js +0 -152
  44. package/dist/field.js +0 -110
  45. package/dist/fieldtype.js +0 -97
  46. package/dist/index--rNo7Kel.js +0 -494
  47. package/dist/index--rNo7Kel.js.map +0 -1
  48. package/dist/index-0qWNlDQ5.js +0 -450
  49. package/dist/index-0qWNlDQ5.js.map +0 -1
  50. package/dist/index-2UVTfbcY.js +0 -448
  51. package/dist/index-2UVTfbcY.js.map +0 -1
  52. package/dist/index-9QjWZ_PC.js +0 -449
  53. package/dist/index-9QjWZ_PC.js.map +0 -1
  54. package/dist/index-BCADNO5M.js +0 -449
  55. package/dist/index-BCADNO5M.js.map +0 -1
  56. package/dist/index-BJjSlCSo.js +0 -496
  57. package/dist/index-BJjSlCSo.js.map +0 -1
  58. package/dist/index-BRQJGVFR.js +0 -453
  59. package/dist/index-BRQJGVFR.js.map +0 -1
  60. package/dist/index-BatnoC-J.js +0 -429
  61. package/dist/index-BatnoC-J.js.map +0 -1
  62. package/dist/index-BbG7Mzvg.js +0 -495
  63. package/dist/index-BbG7Mzvg.js.map +0 -1
  64. package/dist/index-BdCmYHg0.js +0 -493
  65. package/dist/index-BdCmYHg0.js.map +0 -1
  66. package/dist/index-BhMd2_xl.js +0 -454
  67. package/dist/index-BhMd2_xl.js.map +0 -1
  68. package/dist/index-C5ANE_rn.js +0 -501
  69. package/dist/index-C5ANE_rn.js.map +0 -1
  70. package/dist/index-CLc5mUMQ.js +0 -478
  71. package/dist/index-CLc5mUMQ.js.map +0 -1
  72. package/dist/index-COrltkHl.js +0 -401
  73. package/dist/index-COrltkHl.js.map +0 -1
  74. package/dist/index-Cu-6609R.js +0 -493
  75. package/dist/index-Cu-6609R.js.map +0 -1
  76. package/dist/index-CvN9xK1B.js +0 -453
  77. package/dist/index-CvN9xK1B.js.map +0 -1
  78. package/dist/index-CzoRIy1-.js +0 -408
  79. package/dist/index-CzoRIy1-.js.map +0 -1
  80. package/dist/index-D66L9JNw.js +0 -506
  81. package/dist/index-D66L9JNw.js.map +0 -1
  82. package/dist/index-D68mWfHm.js +0 -510
  83. package/dist/index-D68mWfHm.js.map +0 -1
  84. package/dist/index-D6Up-BP5.js +0 -435
  85. package/dist/index-D6Up-BP5.js.map +0 -1
  86. package/dist/index-D9qPYlSk.js +0 -491
  87. package/dist/index-D9qPYlSk.js.map +0 -1
  88. package/dist/index-DEkR-1RC.js +0 -496
  89. package/dist/index-DEkR-1RC.js.map +0 -1
  90. package/dist/index-DIY_FLR2.js +0 -516
  91. package/dist/index-DIY_FLR2.js.map +0 -1
  92. package/dist/index-DIb_Z0wI.js +0 -476
  93. package/dist/index-DIb_Z0wI.js.map +0 -1
  94. package/dist/index-DNROIEMe.js +0 -449
  95. package/dist/index-DNROIEMe.js.map +0 -1
  96. package/dist/index-DT-NJBvW.js +0 -510
  97. package/dist/index-DT-NJBvW.js.map +0 -1
  98. package/dist/index-DUFcQC-H.js +0 -449
  99. package/dist/index-DUFcQC-H.js.map +0 -1
  100. package/dist/index-DVVTsJNb.js +0 -495
  101. package/dist/index-DVVTsJNb.js.map +0 -1
  102. package/dist/index-DYy5E1cU.js +0 -508
  103. package/dist/index-DYy5E1cU.js.map +0 -1
  104. package/dist/index-DdC5tpt-.js +0 -488
  105. package/dist/index-DdC5tpt-.js.map +0 -1
  106. package/dist/index-Dk593rMz.js +0 -512
  107. package/dist/index-Dk593rMz.js.map +0 -1
  108. package/dist/index-DmD8vmuB.js +0 -449
  109. package/dist/index-DmD8vmuB.js.map +0 -1
  110. package/dist/index-DpCFgELB.js +0 -514
  111. package/dist/index-DpCFgELB.js.map +0 -1
  112. package/dist/index-DqxTn6jG.js +0 -488
  113. package/dist/index-DqxTn6jG.js.map +0 -1
  114. package/dist/index-DyK5-0kh.js +0 -514
  115. package/dist/index-DyK5-0kh.js.map +0 -1
  116. package/dist/index-XSSAHj8g.js +0 -495
  117. package/dist/index-XSSAHj8g.js.map +0 -1
  118. package/dist/index-XZZbfFxT.js +0 -434
  119. package/dist/index-XZZbfFxT.js.map +0 -1
  120. package/dist/index-_3q9Bzka.js +0 -512
  121. package/dist/index-_3q9Bzka.js.map +0 -1
  122. package/dist/index-aeXXzPET.cjs +0 -2
  123. package/dist/index-aeXXzPET.cjs.map +0 -1
  124. package/dist/index-neVkjlgB.js +0 -446
  125. package/dist/index-neVkjlgB.js.map +0 -1
  126. package/dist/index-xdlVWldg.js +0 -408
  127. package/dist/index-xdlVWldg.js.map +0 -1
  128. package/dist/index.cjs +0 -2
  129. package/dist/index.cjs.map +0 -1
  130. package/dist/jsonschema.js +0 -225
  131. package/dist/naming.js +0 -106
  132. package/dist/src/fieldtype.d.ts +0 -63
  133. package/dist/src/fieldtype.d.ts.map +0 -1
  134. package/dist/src/fieldtype.js +0 -105
  135. package/dist/src/jsonschema.d.ts +0 -95
  136. package/dist/src/jsonschema.d.ts.map +0 -1
  137. package/dist/src/schema-field.d.ts +0 -66
  138. package/dist/src/schema-field.d.ts.map +0 -1
  139. package/dist/src/schema-field.js +0 -0
  140. package/dist/src/tsdoc-metadata.json +0 -11
  141. package/dist/validation.js +0 -60
package/dist/schema.d.ts CHANGED
@@ -9,11 +9,12 @@ import { z } from 'zod';
9
9
  */
10
10
  export declare const ActionDefinition: z.ZodObject<{
11
11
  label: z.ZodString;
12
- handler: z.ZodString;
13
12
  requiredFields: z.ZodOptional<z.ZodArray<z.ZodString>>;
14
13
  allowedStates: z.ZodOptional<z.ZodArray<z.ZodString>>;
15
- confirm: z.ZodOptional<z.ZodBoolean>;
16
- args: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
14
+ nextState: z.ZodOptional<z.ZodString>;
15
+ stateless: z.ZodOptional<z.ZodBoolean>;
16
+ selfTransition: z.ZodOptional<z.ZodBoolean>;
17
+ clientHandler: z.ZodOptional<z.ZodString>;
17
18
  }, z.core.$strip>;
18
19
 
19
20
  /**
@@ -32,20 +33,6 @@ export declare type ActionDefinition = z.infer<typeof ActionDefinition>;
32
33
  */
33
34
  export declare function buildScalarMap(customScalars?: Record<string, Partial<FieldTemplate>>): Record<string, FieldTemplate>;
34
35
 
35
- /**
36
- * The complete list of field types built into Stonecrop.
37
- * User apps can use any string as a fieldtype; this const is the exhaustive set of types
38
- * that Stonecrop provides default components for.
39
- * @public
40
- */
41
- export declare const BUILTIN_FIELD_TYPES: readonly ["Data", "Text", "Int", "Float", "Decimal", "Check", "Date", "Time", "Datetime", "Duration", "DateRange", "JSON", "Code", "Link", "Attach", "Currency", "Quantity", "Select", "PrimaryKey", "Fieldset", "Display"];
42
-
43
- /**
44
- * Union of all builtin fieldtype string literals.
45
- * @public
46
- */
47
- export declare type BuiltinFieldType = (typeof BUILTIN_FIELD_TYPES)[number];
48
-
49
36
  /**
50
37
  * Converts camelCase to Title Case label
51
38
  * @param camelCase - Camel case string
@@ -72,6 +59,22 @@ export declare function camelToLabel(camelCase: string): string;
72
59
  */
73
60
  export declare function camelToSnake(camelCase: string): string;
74
61
 
62
+ /**
63
+ * Every component Stonecrop ships with that can render a value field, sorted by name.
64
+ *
65
+ * The union of the two maps above is the definition, not a copy of it: a shipped component either
66
+ * categorises a value ({@link COMPONENT_CATEGORY}) or is one of the link containers that has no
67
+ * value of its own ({@link COMPONENT_LINK_EXPANSION}'s `AForm`/`ATable`). `AFieldset` is absent by
68
+ * the same rule — it is a `kind: 'fieldset'` container, so it is never a value field's component.
69
+ *
70
+ * `component` is an **open** axis: any string is valid, and naming a custom component is how an app
71
+ * renders a field Stonecrop ships no widget for. This list is therefore the set to *suggest* to an
72
+ * author, and to check first-party data against — never a set to validate arbitrary input against.
73
+ *
74
+ * @public
75
+ */
76
+ export declare const CANONICAL_COMPONENTS: readonly string[];
77
+
75
78
  /**
76
79
  * Cardinality for relationship links.
77
80
  * @public
@@ -131,11 +134,19 @@ export declare interface ColumnSchema {
131
134
  /** Unique identifier for the field within its doctype. Maps to `name` on `TableColumn`. */
132
135
  fieldname: string;
133
136
  /**
134
- * Semantic field type (e.g. `'Data'`, `'Int'`, `'Date'`, `'Check'`). Fields without a
135
- * `fieldtype` are treated as non-scalar (nested table or fieldset) and excluded by
136
- * `schemaToColumns`.
137
+ * Rendering component (e.g. `'ATextInput'`, `'ANumericInput'`, `'ADate'`). Default cell
138
+ * formatting and filter widgets derive from its {@link ComponentCategory}.
139
+ *
140
+ * Optional here, unlike `ValueField.component`: absence is what marks an entry as non-scalar
141
+ * (a nested table or fieldset), which `schemaToColumns` excludes — it has no column equivalent.
142
+ */
143
+ component?: string;
144
+ /**
145
+ * Target doctype slug — marks this column as a link. When set and no `cellComponent` is given,
146
+ * `schemaToColumns` copies it to `TableColumn.linkDoctype`, which ACell uses to resolve a bare
147
+ * id to display text.
137
148
  */
138
- fieldtype?: string;
149
+ doctype?: string;
139
150
  /**
140
151
  * Human-readable column header. When absent, ATable assigns labels alphabetically
141
152
  * (A, B, C, …).
@@ -146,7 +157,7 @@ export declare interface ColumnSchema {
146
157
  /**
147
158
  * Horizontal text alignment for the column cell and header.
148
159
  *
149
- * @defaultValue 'left'
160
+ * @defaultValue 'center'
150
161
  */
151
162
  align?: 'left' | 'right' | 'center' | 'start' | 'end';
152
163
  /**
@@ -186,9 +197,10 @@ export declare interface ColumnSchema {
186
197
  */
187
198
  filterable?: boolean;
188
199
  /**
189
- * The type of filter control to render. When absent, a default is derived from `fieldtype`
190
- * (`Check` → `checkbox`, `Date` → `date`, `Datetime` → `dateRange`, `Select` → `select`,
191
- * numeric types → `number`, everything else → `text`).
200
+ * The type of filter control to render. When absent, a default is derived from the
201
+ * `component`'s {@link ComponentCategory} (`boolean` → `checkbox`, `date` → `date`,
202
+ * `datetime` → `dateRange`, `select` → `select`, `number` → `number`, everything else
203
+ * — including an unknown component — → `text`).
192
204
  */
193
205
  filterType?: 'text' | 'select' | 'number' | 'date' | 'dateRange' | 'checkbox' | 'component';
194
206
  /**
@@ -257,6 +269,47 @@ export declare interface ColumnSchema {
257
269
  colspan?: number;
258
270
  }
259
271
 
272
+ /**
273
+ * Canonical component → semantic category. Only the components Stonecrop ships with appear here;
274
+ * custom/unknown component names have no category and consumers fall back to their default.
275
+ * @public
276
+ */
277
+ export declare const COMPONENT_CATEGORY: Record<string, ComponentCategory>;
278
+
279
+ /**
280
+ * Canonical link component → expansion. Only components Stonecrop ships with appear here; an
281
+ * unmapped (custom) component has none, and callers treat that as `expand` — the behaviour that
282
+ * predates this map, so a custom component can never silently collapse a link to a picker.
283
+ * @public
284
+ */
285
+ export declare const COMPONENT_LINK_EXPANSION: Record<string, LinkExpansion>;
286
+
287
+ /**
288
+ * Semantic category for a rendering component.
289
+ *
290
+ * `component` is the primary field axis, so the runtime consumers that need to know what a field
291
+ * *means* (atable cell formatting / filter widgets, record-default init) derive it from here. This
292
+ * is the single source of "what kind of value does this component render", keyed by the canonical
293
+ * registered component names — each consumer maps the category to its own concern (filter widget,
294
+ * default value, …).
295
+ *
296
+ * @public
297
+ */
298
+ export declare type ComponentCategory = 'text' | 'number' | 'boolean' | 'date' | 'datetime' | 'select' | 'code' | 'link' | 'attach';
299
+
300
+ /**
301
+ * Resolve a component's semantic category, or `undefined` for an unknown (custom) component —
302
+ * callers treat that as "no opinion" and use their own default.
303
+ * @public
304
+ */
305
+ export declare function componentCategory(component?: string): ComponentCategory | undefined;
306
+
307
+ /**
308
+ * Resolve a component's link expansion, or `undefined` for an absent/unmapped component.
309
+ * @public
310
+ */
311
+ export declare function componentLinkExpansion(component?: string): LinkExpansion | undefined;
312
+
260
313
  /**
261
314
  * Output of GraphQL schema conversion — one per entity type.
262
315
  *
@@ -293,7 +346,7 @@ export declare interface ConvertedGraphQLDoctype extends Omit<DoctypeMeta, 'fiel
293
346
  * // With PostGraphile custom scalars
294
347
  * const doctypes = convertGraphQLSchema(introspection, {
295
348
  * customScalars: {
296
- * BigFloat: { component: 'ADecimalInput', fieldtype: 'Decimal' }
349
+ * BigFloat: { component: 'ANumericInput' }
297
350
  * }
298
351
  * })
299
352
  * ```
@@ -459,11 +512,35 @@ export declare const DoctypeMeta: z.ZodObject<{
459
512
  states: z.ZodOptional<z.ZodArray<z.ZodString>>;
460
513
  actions: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
461
514
  label: z.ZodString;
462
- handler: z.ZodString;
463
515
  requiredFields: z.ZodOptional<z.ZodArray<z.ZodString>>;
464
516
  allowedStates: z.ZodOptional<z.ZodArray<z.ZodString>>;
465
- confirm: z.ZodOptional<z.ZodBoolean>;
466
- args: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
517
+ nextState: z.ZodOptional<z.ZodString>;
518
+ stateless: z.ZodOptional<z.ZodBoolean>;
519
+ selfTransition: z.ZodOptional<z.ZodBoolean>;
520
+ clientHandler: z.ZodOptional<z.ZodString>;
521
+ }, z.core.$strip>>>;
522
+ triggers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
523
+ label: z.ZodOptional<z.ZodString>;
524
+ on: z.ZodArray<z.ZodString>;
525
+ clientHandler: z.ZodString;
526
+ }, z.core.$strip>>>;
527
+ layout: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
528
+ position: z.ZodOptional<z.ZodObject<{
529
+ x: z.ZodNumber;
530
+ y: z.ZodNumber;
531
+ }, z.core.$strip>>;
532
+ targetPosition: z.ZodOptional<z.ZodEnum<{
533
+ left: "left";
534
+ right: "right";
535
+ top: "top";
536
+ bottom: "bottom";
537
+ }>>;
538
+ sourcePosition: z.ZodOptional<z.ZodEnum<{
539
+ left: "left";
540
+ right: "right";
541
+ top: "top";
542
+ bottom: "bottom";
543
+ }>>;
467
544
  }, z.core.$strip>>>;
468
545
  }, z.core.$strip>>;
469
546
  inherits: z.ZodOptional<z.ZodString>;
@@ -513,15 +590,18 @@ export declare type FetchStrategy = z.infer<typeof FetchStrategy>;
513
590
  /**
514
591
  * Field options - flexible bag for type-specific configuration.
515
592
  *
516
- * Usage by fieldtype:
517
- * - Link/Doctype: target doctype slug as string ("customer", "sales-order-item")
593
+ * Usage:
518
594
  * - Select: array of choices (["Draft", "Submitted", "Cancelled"])
519
595
  * - Decimal: config object (\{ precision: 10, scale: 2 \})
520
596
  * - Code: config object (\{ language: "python" \})
521
597
  *
598
+ * Deliberately *not* a bare string: a string once meant "link target", which made the value's
599
+ * shape encode its meaning. That job belongs to `ValueField.doctype`, leaving this a plain
600
+ * choices-or-config bag.
601
+ *
522
602
  * @public
523
603
  */
524
- export declare const FieldOptions: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>, z.ZodRecord<z.ZodString, z.ZodUnknown>]>;
604
+ export declare const FieldOptions: z.ZodUnion<readonly [z.ZodArray<z.ZodString>, z.ZodRecord<z.ZodString, z.ZodUnknown>]>;
525
605
 
526
606
  /**
527
607
  * Field options type inferred from Zod schema
@@ -570,19 +650,16 @@ export declare const FieldsetFieldSchema: z.ZodObject<{
570
650
  }, z.core.$strip>;
571
651
 
572
652
  /**
573
- * Field template for TYPE_MAP entries.
574
- * Defines the default component and semantic field type for a field.
653
+ * The component a GraphQL scalar maps to.
654
+ *
655
+ * A one-property interface rather than a bare string so `customScalars` stays extensible: an
656
+ * override is a `Partial<FieldTemplate>`, and widening this later does not change that signature.
657
+ *
575
658
  * @public
576
659
  */
577
- export declare interface FieldTemplate {
578
- /**
579
- * The Vue component name to render this field (e.g., 'ATextInput', 'ADropdown')
580
- */
660
+ declare interface FieldTemplate {
661
+ /** The Vue component name to render fields of this scalar type (e.g. `'ATextInput'`). */
581
662
  component: string;
582
- /**
583
- * The semantic field type (e.g., 'Data', 'Int', 'Select')
584
- */
585
- fieldtype: BuiltinFieldType;
586
663
  }
587
664
 
588
665
  /**
@@ -600,15 +677,6 @@ export declare const FieldValidation: z.ZodObject<{
600
677
  */
601
678
  export declare type FieldValidation = z.infer<typeof FieldValidation>;
602
679
 
603
- /**
604
- * Get the default component for a builtin field type.
605
- * For an open-string fieldtype that may be custom, use {@link resolveComponent} instead.
606
- * @param fieldtype - A builtin field type
607
- * @returns The default component name
608
- * @public
609
- */
610
- export declare function getDefaultComponent(fieldtype: BuiltinFieldType): string;
611
-
612
680
  /**
613
681
  * Options for fetching a single record
614
682
  * @public
@@ -666,9 +734,7 @@ export declare const GQL_SCALAR_MAP: Record<string, FieldTemplate>;
666
734
  *
667
735
  * @public
668
736
  */
669
- export declare interface GraphQLConversionFieldMeta extends Omit<ValueField, 'fieldtype'> {
670
- /** Semantic field type - optional for link fields which don't have a fieldtype */
671
- fieldtype?: string;
737
+ export declare interface GraphQLConversionFieldMeta extends ValueField {
672
738
  /** Original GraphQL type name (for debugging/reference) */
673
739
  _graphqlType?: string;
674
740
  /** Marks fields that couldn't be automatically mapped */
@@ -702,22 +768,22 @@ export declare interface GraphQLConversionOptions {
702
768
  * ```typescript
703
769
  * {
704
770
  * SalesOrder: {
705
- * totalAmount: { fieldtype: 'Currency', component: 'ACurrencyInput' }
771
+ * totalAmount: { component: 'ANumericInput', align: 'right' }
706
772
  * }
707
773
  * }
708
774
  * ```
709
775
  */
710
776
  typeOverrides?: Record<string, Record<string, Omit<Partial<ValueField>, 'kind'>>>;
711
777
  /**
712
- * Map custom or non-standard GraphQL scalar types to Stonecrop field types.
778
+ * Map custom or non-standard GraphQL scalar types to the component that renders them.
713
779
  * Merged with the built-in scalar maps (GQL_SCALAR_MAP + WELL_KNOWN_SCALARS).
714
780
  * User-provided entries take highest precedence.
715
781
  *
716
782
  * @example
717
783
  * ```typescript
718
784
  * {
719
- * MyCustomMoney: { component: 'ACurrencyInput', fieldtype: 'Currency' },
720
- * PostGISPoint: { component: 'ATextInput', fieldtype: 'Data' }
785
+ * MyCustomMoney: { component: 'ANumericInput' },
786
+ * PostGISPoint: { component: 'ATextInput' }
721
787
  * }
722
788
  * ```
723
789
  */
@@ -804,10 +870,19 @@ export declare const INTERNAL_SCALARS: Set<string>;
804
870
  export declare type IntrospectionSource = IntrospectionQuery | string;
805
871
 
806
872
  /**
807
- * Returns `true` when `fieldtype` is one of the builtin types Stonecrop ships with.
873
+ * Whether a workflow action may run from `currentState`.
874
+ *
875
+ * Single source of truth for the "is this action available here" rule, shared by
876
+ * the frontend (`getAvailableTransitions`) and the server-side dispatch guard so
877
+ * the two can never disagree. Empty or absent `allowedStates` means the action is
878
+ * available in ALL states — a plain `allowedStates.includes(currentState)` would
879
+ * wrongly block such actions everywhere.
880
+ *
808
881
  * @public
809
882
  */
810
- export declare function isBuiltinFieldType(fieldtype: string): fieldtype is BuiltinFieldType;
883
+ export declare function isActionAllowedInState(action: {
884
+ allowedStates?: string[] | null;
885
+ }, currentState: string): boolean;
811
886
 
812
887
  /**
813
888
  * Lazy fetch strategy - data is fetched on demand in a separate query.
@@ -856,6 +931,49 @@ export declare const LinkDeclaration: z.ZodObject<{
856
931
  */
857
932
  export declare type LinkDeclaration = z.infer<typeof LinkDeclaration>;
858
933
 
934
+ /**
935
+ * Whether a link component expands its target doctype, or renders the link inline.
936
+ *
937
+ * This is the *only* axis the component decides. It deliberately does not choose between an
938
+ * embedded record and an embedded table: `cardinality` states whether the value is a scalar or
939
+ * an array, which is a fact about the data rather than a rendering preference, so a component
940
+ * must not be able to override it (an `AForm` over a `noneOrMany` link would be handed an array
941
+ * it cannot render). Component names encode both axes — `AFormLink`/`ATableLink` are the inline
942
+ * pair, `AForm`/`ATable` the expanding pair — but only the inline/expand half is authoritative.
943
+ *
944
+ * @public
945
+ */
946
+ export declare type LinkExpansion = 'inline' | 'expand';
947
+
948
+ /**
949
+ * How a link field renders.
950
+ *
951
+ * - `inline` — a scalar id-picker; the target is *not* expanded (the field keeps its own value
952
+ * and carries a `doctype` prop for async display-text resolution and navigation).
953
+ * - `record` — the target doctype is resolved and embedded as a nested form.
954
+ * - `table` — the target doctype is resolved and embedded as a child table.
955
+ *
956
+ * @public
957
+ */
958
+ export declare type LinkRenderMode = 'inline' | 'record' | 'table';
959
+
960
+ /**
961
+ * Recursively injects the `kind` discriminant into a raw field object and, for fieldsets,
962
+ * into each of its nested `schema` children — mirroring exactly what Zod's `preprocess`
963
+ * does at every level of the discriminated union.
964
+ *
965
+ * Table `columns` are {@link ColumnSchema} entries, not `DoctypeField`s, so they are left
966
+ * untouched — the Zod table schema validates them with a plain passthrough and never injects
967
+ * `kind` there either.
968
+ *
969
+ * Needed because `Doctype.fromObject` constructs a Doctype without running Zod, yet the
970
+ * registry's `resolveFields` gates link and fieldset handling on `field.kind`. Without this,
971
+ * a JSON-authored link resolves to a flat scalar and a fieldset's children are dropped.
972
+ *
973
+ * @public
974
+ */
975
+ export declare function normalizeFieldKind(field: unknown): unknown;
976
+
859
977
  /**
860
978
  * Parse and validate a doctype, throwing on failure
861
979
  * @param data - Data to parse
@@ -888,13 +1006,25 @@ export declare function parseField(data: unknown): DoctypeField;
888
1006
  export declare function pascalToSnake(pascal: string): string;
889
1007
 
890
1008
  /**
891
- * Resolve the component name for any fieldtype string, falling back to `'ATextInput'`
892
- * for unknown custom types.
893
- * @param fieldtype - Any fieldtype string (builtin or custom)
894
- * @returns The component name to use for rendering
1009
+ * Decide how a *declared* link (one with a `LinkDeclaration`) renders.
1010
+ *
1011
+ * Two independent axes: the **component** picks inline vs expand, and when expanding the
1012
+ * **cardinality** picks record vs table (many → table). The declaration's component wins over the
1013
+ * field's, matching the precedence the resolver already uses for the rendered component.
1014
+ *
1015
+ * This is the single definition of "does this link expand" — it is consumed by both the client
1016
+ * resolver (which builds the nested schema) and the server column builder (which must still
1017
+ * SELECT an `inline` link's FK column). Call it; never re-derive the rule at the call site, or
1018
+ * the two will drift and the client will render a table for a column the server never selected.
1019
+ *
1020
+ * @param link - the link declaration (only `component` and `cardinality` are consulted)
1021
+ * @param fieldComponent - the linked field's own `component`, used when the declaration names none
895
1022
  * @public
896
1023
  */
897
- export declare function resolveComponent(fieldtype: string): string;
1024
+ export declare function resolveLinkRenderMode(link: {
1025
+ component?: string;
1026
+ cardinality?: string;
1027
+ }, fieldComponent?: string): LinkRenderMode;
898
1028
 
899
1029
  /**
900
1030
  * Serialized function type - a function serialized to a string.
@@ -929,14 +1059,6 @@ export declare function snakeToCamel(snakeCase: string): string;
929
1059
  */
930
1060
  export declare function snakeToLabel(snakeCase: string): string;
931
1061
 
932
- /**
933
- * Stonecrop field type — any non-empty string is valid; Stonecrop provides default components
934
- * for the builtin types listed in {@link BUILTIN_FIELD_TYPES}. Custom fieldtypes are supported
935
- * by supplying an explicit `component` on the field definition.
936
- * @public
937
- */
938
- export declare const StonecropFieldType: z.ZodString;
939
-
940
1062
  /**
941
1063
  * Sync fetch strategy - data is fetched in the initial query.
942
1064
  * @public
@@ -1060,11 +1182,29 @@ export declare function toPascalCase(tableName: string): string;
1060
1182
  export declare function toSlug(name: string): string;
1061
1183
 
1062
1184
  /**
1063
- * Mapping from builtin fieldtypes to their default Vue component.
1064
- * Components can be overridden in the field definition.
1185
+ * Reactive field-validation trigger — advisory, client-side only.
1186
+ *
1187
+ * A Trigger is a docbuilder-authored validator: when any field in `on` is edited, its
1188
+ * `clientHandler` runs (client-side, no rollback) and may flag a field inline to block save
1189
+ * in the UI. It is deliberately a **sibling** to {@link (ActionDefinition:type)}, not a member of it —
1190
+ * a reactive validator is not a user-invoked action, so it lives in the `triggers` map on
1191
+ * {@link (WorkflowMeta:type)} and never appears to action readers (transition/command dropdowns, the FSM graph).
1192
+ *
1193
+ * The two bindings are independent: `on` is the fire-set (which fields' edits run it), while the
1194
+ * `setError(field, msg)` call inside `clientHandler` chooses which field displays the error.
1195
+ * @public
1196
+ */
1197
+ export declare const TriggerDefinition: z.ZodObject<{
1198
+ label: z.ZodOptional<z.ZodString>;
1199
+ on: z.ZodArray<z.ZodString>;
1200
+ clientHandler: z.ZodString;
1201
+ }, z.core.$strip>;
1202
+
1203
+ /**
1204
+ * Trigger definition type inferred from Zod schema
1065
1205
  * @public
1066
1206
  */
1067
- export declare const TYPE_MAP: Record<BuiltinFieldType, FieldTemplate>;
1207
+ export declare type TriggerDefinition = z.infer<typeof TriggerDefinition>;
1068
1208
 
1069
1209
  /**
1070
1210
  * Validate a doctype definition
@@ -1106,7 +1246,8 @@ export declare interface ValidationResult {
1106
1246
 
1107
1247
  /**
1108
1248
  * A field that holds a scalar value, a link to another record, or a select choice.
1109
- * The most common kind of field. `fieldtype` determines the default component and behavior.
1249
+ * The most common kind of field. `component` determines how it renders; the attributes below
1250
+ * carry everything else that is not a rendering concern.
1110
1251
  * @public
1111
1252
  */
1112
1253
  export declare interface ValueField {
@@ -1114,10 +1255,29 @@ export declare interface ValueField {
1114
1255
  kind: 'field';
1115
1256
  /** Unique identifier for this field within its doctype */
1116
1257
  fieldname: string;
1117
- /** Semantic field type — determines behavior and default rendering component */
1118
- fieldtype: string;
1119
- /** Vue component to render this field. Derived from `fieldtype` when absent. */
1120
- component?: string;
1258
+ /**
1259
+ * Vue component that renders this field — the primary (and only) rendering axis. Required:
1260
+ * there is nothing left to derive it from, and a field without one has nothing to render it.
1261
+ * Any string is valid; naming a custom component is how an app renders a field Stonecrop
1262
+ * ships no widget for. See `CANONICAL_COMPONENTS` for the set Stonecrop provides.
1263
+ */
1264
+ component: string;
1265
+ /** True for the field that identifies the record's primary-key column. */
1266
+ primaryKey?: boolean;
1267
+ /** True for a computed/display field with no backing DB column — excluded from SQL SELECT. */
1268
+ computed?: boolean;
1269
+ /** Editor language for code fields (e.g. `'json'`, `'typescript'`) — the only thing distinguishing
1270
+ * a JSON editor from a code editor, since both render with `ACodeEditor`. */
1271
+ language?: string;
1272
+ /**
1273
+ * Target doctype slug. Presence is what makes a field a link.
1274
+ *
1275
+ * How it renders is decided by `component`, not by this: `AFormLink` renders an
1276
+ * inline id-picker, while `AForm`/`ATable` expand the target (see `linkRenderMode`). Expansion
1277
+ * metadata — backlink, fetch strategy, authoritative cardinality — lives in the doctype's
1278
+ * `links` map, which is additive and never required for a plain foreign key.
1279
+ */
1280
+ doctype?: string;
1121
1281
  /** Human-readable label */
1122
1282
  label?: string;
1123
1283
  /** CSS width (e.g. `"40ch"`, `"200px"`) */
@@ -1128,9 +1288,14 @@ export declare interface ValueField {
1128
1288
  edit?: boolean;
1129
1289
  /** Input mask pattern or serialized function */
1130
1290
  mask?: string;
1291
+ /** Serialized `(value) => string` function for display formatting — distinct from `mask` (input).
1292
+ * Spreads through `schemaToColumns` to `ColumnSchema.format`; deserialized at render time by
1293
+ * ATable's `getFormattedValue`. */
1294
+ format?: string;
1131
1295
  /** Per-field interaction mode override */
1132
1296
  mode?: InteractionMode;
1133
- /** Type-specific options: Link target slug, Select choices, Decimal precision config, etc. */
1297
+ /** Type-specific options: Select choices, Decimal precision config, etc. A link's target is not
1298
+ * here — it is `doctype`. */
1134
1299
  options?: FieldOptions;
1135
1300
  /** Whether the field is required */
1136
1301
  required?: boolean;
@@ -1144,6 +1309,14 @@ export declare interface ValueField {
1144
1309
  validation?: FieldValidation;
1145
1310
  /** Cardinality for Link fields — authoritative value on LinkDeclaration takes precedence */
1146
1311
  cardinality?: 'atMostOne' | 'one' | 'noneOrMany' | 'atLeastOne';
1312
+ /**
1313
+ * Provenance marker — stamped only by the GraphQL converter; absence means hand-authored.
1314
+ * When present, the docbuilder freezes the field's identity set (`fieldname`, `primaryKey`,
1315
+ * `required`, `options`, `cardinality`, `doctype`), since `fieldname` is the GraphQL/column
1316
+ * binding and `doctype` is the FK's target. `component` is deliberately **not** frozen: it
1317
+ * chooses the widget, which is an authoring decision the database has no opinion about.
1318
+ */
1319
+ source?: 'introspected';
1147
1320
  }
1148
1321
 
1149
1322
  /**
@@ -1153,8 +1326,11 @@ export declare interface ValueField {
1153
1326
  export declare const ValueFieldSchema: z.ZodObject<{
1154
1327
  kind: z.ZodLiteral<"field">;
1155
1328
  fieldname: z.ZodString;
1156
- fieldtype: z.ZodString;
1157
- component: z.ZodOptional<z.ZodString>;
1329
+ component: z.ZodString;
1330
+ primaryKey: z.ZodOptional<z.ZodBoolean>;
1331
+ computed: z.ZodOptional<z.ZodBoolean>;
1332
+ language: z.ZodOptional<z.ZodString>;
1333
+ doctype: z.ZodOptional<z.ZodString>;
1158
1334
  label: z.ZodOptional<z.ZodString>;
1159
1335
  width: z.ZodOptional<z.ZodString>;
1160
1336
  align: z.ZodOptional<z.ZodEnum<{
@@ -1166,12 +1342,13 @@ export declare const ValueFieldSchema: z.ZodObject<{
1166
1342
  }>>;
1167
1343
  edit: z.ZodOptional<z.ZodBoolean>;
1168
1344
  mask: z.ZodOptional<z.ZodString>;
1345
+ format: z.ZodOptional<z.ZodString>;
1169
1346
  mode: z.ZodOptional<z.ZodEnum<{
1170
1347
  edit: "edit";
1171
1348
  read: "read";
1172
1349
  display: "display";
1173
1350
  }>>;
1174
- options: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>, z.ZodRecord<z.ZodString, z.ZodUnknown>]>>;
1351
+ options: z.ZodOptional<z.ZodUnion<readonly [z.ZodArray<z.ZodString>, z.ZodRecord<z.ZodString, z.ZodUnknown>]>>;
1175
1352
  required: z.ZodOptional<z.ZodBoolean>;
1176
1353
  readOnly: z.ZodOptional<z.ZodBoolean>;
1177
1354
  hidden: z.ZodOptional<z.ZodBoolean>;
@@ -1186,6 +1363,7 @@ export declare const ValueFieldSchema: z.ZodObject<{
1186
1363
  noneOrMany: "noneOrMany";
1187
1364
  atLeastOne: "atLeastOne";
1188
1365
  }>>;
1366
+ source: z.ZodOptional<z.ZodLiteral<"introspected">>;
1189
1367
  }, z.core.$strip>;
1190
1368
 
1191
1369
  /**
@@ -1200,6 +1378,40 @@ export declare const ValueFieldSchema: z.ZodObject<{
1200
1378
  */
1201
1379
  export declare const WELL_KNOWN_SCALARS: Record<string, FieldTemplate>;
1202
1380
 
1381
+ /**
1382
+ * DocBuilder graph layout — node positions for the workflow-state graph, keyed by state name.
1383
+ * Pure authoring view-state: persisted in the doctype JSON so an author's manual arrangement
1384
+ * survives reloads, but — exactly like {@link (WorkflowMeta:type)}'s `triggers` — it is client-only
1385
+ * and never mirrored into the runtime GraphQL SDL (see the WorkflowMeta type in the host SDLs, which
1386
+ * expose only `states`/`actions`). The shape mirrors VueFlow's node fields; `position` is the node's
1387
+ * canvas coordinate and `targetPosition`/`sourcePosition` are the handle sides.
1388
+ * @public
1389
+ */
1390
+ export declare const WorkflowLayout: z.ZodRecord<z.ZodString, z.ZodObject<{
1391
+ position: z.ZodOptional<z.ZodObject<{
1392
+ x: z.ZodNumber;
1393
+ y: z.ZodNumber;
1394
+ }, z.core.$strip>>;
1395
+ targetPosition: z.ZodOptional<z.ZodEnum<{
1396
+ left: "left";
1397
+ right: "right";
1398
+ top: "top";
1399
+ bottom: "bottom";
1400
+ }>>;
1401
+ sourcePosition: z.ZodOptional<z.ZodEnum<{
1402
+ left: "left";
1403
+ right: "right";
1404
+ top: "top";
1405
+ bottom: "bottom";
1406
+ }>>;
1407
+ }, z.core.$strip>>;
1408
+
1409
+ /**
1410
+ * Workflow layout type inferred from Zod schema
1411
+ * @public
1412
+ */
1413
+ export declare type WorkflowLayout = z.infer<typeof WorkflowLayout>;
1414
+
1203
1415
  /**
1204
1416
  * Workflow metadata - states and actions for a doctype
1205
1417
  * @public
@@ -1208,11 +1420,35 @@ export declare const WorkflowMeta: z.ZodObject<{
1208
1420
  states: z.ZodOptional<z.ZodArray<z.ZodString>>;
1209
1421
  actions: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
1210
1422
  label: z.ZodString;
1211
- handler: z.ZodString;
1212
1423
  requiredFields: z.ZodOptional<z.ZodArray<z.ZodString>>;
1213
1424
  allowedStates: z.ZodOptional<z.ZodArray<z.ZodString>>;
1214
- confirm: z.ZodOptional<z.ZodBoolean>;
1215
- args: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
1425
+ nextState: z.ZodOptional<z.ZodString>;
1426
+ stateless: z.ZodOptional<z.ZodBoolean>;
1427
+ selfTransition: z.ZodOptional<z.ZodBoolean>;
1428
+ clientHandler: z.ZodOptional<z.ZodString>;
1429
+ }, z.core.$strip>>>;
1430
+ triggers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
1431
+ label: z.ZodOptional<z.ZodString>;
1432
+ on: z.ZodArray<z.ZodString>;
1433
+ clientHandler: z.ZodString;
1434
+ }, z.core.$strip>>>;
1435
+ layout: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
1436
+ position: z.ZodOptional<z.ZodObject<{
1437
+ x: z.ZodNumber;
1438
+ y: z.ZodNumber;
1439
+ }, z.core.$strip>>;
1440
+ targetPosition: z.ZodOptional<z.ZodEnum<{
1441
+ left: "left";
1442
+ right: "right";
1443
+ top: "top";
1444
+ bottom: "bottom";
1445
+ }>>;
1446
+ sourcePosition: z.ZodOptional<z.ZodEnum<{
1447
+ left: "left";
1448
+ right: "right";
1449
+ top: "top";
1450
+ bottom: "bottom";
1451
+ }>>;
1216
1452
  }, z.core.$strip>>>;
1217
1453
  }, z.core.$strip>;
1218
1454