@stonecrop/schema 0.29.0 → 0.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -3,7 +3,7 @@ import { readFileSync as i, existsSync as y, mkdirSync as E, writeFileSync as C
3
3
  import { resolve as u, join as A } from "node:path";
4
4
  import { parseArgs as J } from "node:util";
5
5
  import { getIntrospectionQuery as R } from "graphql";
6
- import { c as F, a as G, p as L, m as q, f as D, s as T, v as U } from "./validation-BjRDR6sh.js";
6
+ import { c as F, a as G, p as L, m as q, f as D, s as T, v as U } from "./validation-C9P__pRF.js";
7
7
  async function Q(e, w) {
8
8
  const o = await fetch(e, {
9
9
  method: "POST",
@@ -0,0 +1,10 @@
1
+ function f(t) {
2
+ const e = [];
3
+ for (const s of t)
4
+ s.kind === "fieldset" ? e.push(...f(s.schema)) : e.push(s);
5
+ return e;
6
+ }
7
+ export {
8
+ f
9
+ };
10
+ //# sourceMappingURL=flatten-C1MjkzFh.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flatten-C1MjkzFh.js","sources":["../src/flatten.ts"],"sourcesContent":["import type { DoctypeField, TableField, ValueField } from './field'\n\n/**\n * Recursively flatten Fieldset containers into a flat array of non-container fields.\n * Fieldset entries are replaced by their children; all other fields pass through.\n *\n * A fieldset is a layout grouping, not a scope: every field inside one is a field of the doctype,\n * with a column of its own and a name a link can bind to. Anything asking \"what does this doctype\n * declare\" must therefore descend, and the two ways to get that wrong point opposite ways — the\n * SELECT builder would omit real columns, while a validator would report a working declaration as\n * broken.\n *\n * Lives here rather than in the adapter because both sides need it: the middleware builds SQL from\n * it, and `DoctypeMeta`'s own validation asks the same question at the load gate. It sat in the\n * adapter while the validator hand-rolled a top-level-only scan, and that is exactly the second\n * failure this comment names — a `displayField` inside a fieldset was rejected at authoring time\n * and would have worked at runtime.\n *\n * A module of its own, importing nothing at runtime, because callers need the descent without\n * needing the rest of `field.ts` — which defines the Zod schemas, so a runtime edge to it is a\n * runtime edge to Zod. Zod reaching a Nitro SSR entry collides with the `process` Nitro imports\n * there and takes the server down with a `SyntaxError` per request, which no build reports.\n * `field.ts` still calls this and `index.ts` still exports it, so nothing outside moves.\n *\n * @param fields - the doctype's top-level fields\n * @returns every non-container field, fieldset children included\n * @public\n */\nexport function flattenFields(fields: readonly DoctypeField[]): (ValueField | TableField)[] {\n\tconst result: (ValueField | TableField)[] = []\n\tfor (const f of fields) {\n\t\tif (f.kind === 'fieldset') {\n\t\t\tresult.push(...flattenFields(f.schema))\n\t\t} else {\n\t\t\tresult.push(f)\n\t\t}\n\t}\n\treturn result\n}\n"],"names":["flattenFields","fields","result","f"],"mappings":"AA4BO,SAASA,EAAcC,GAA8D;AAC3F,QAAMC,IAAsC,CAAA;AAC5C,aAAWC,KAAKF;AACf,IAAIE,EAAE,SAAS,aACdD,EAAO,KAAK,GAAGF,EAAcG,EAAE,MAAM,CAAC,IAEtCD,EAAO,KAAKC,CAAC;AAGf,SAAOD;AACR;"}
package/dist/index.js CHANGED
@@ -1,163 +1,131 @@
1
- import { A as L, D as p, F as E, G as h, I as _, b as P, L as R, T as j, d as x, e as B, V as M, W as w, g as k, h as K, i as G, j as V, k as W, l as Q, n as Y, o as z, c as q, q as J, r as X, t as H, f as U, u as Z, w as $, x as ee, y as re, z as ae, B as te, C as ne, E as ie, m as se, H as oe, J as le, K as ce, M as de, p as ue, N as fe, O as Ae, s as ye, P as ge, Q as be, v as Se, R as ve } from "./validation-BjRDR6sh.js";
2
- const l = {
3
- ATextInput: "text",
4
- ATextboxInput: "text",
5
- ANumericInput: "number",
6
- ACheckbox: "boolean",
7
- ADate: "date",
8
- ADatePicker: "date",
9
- ADateSelection: "date",
10
- ADateTime: "datetime",
11
- ADuration: "text",
12
- ADateRange: "date",
13
- ADropdown: "select",
14
- ASegmentedControl: "select",
15
- ACodeEditor: "code",
16
- AFormLink: "link",
17
- AFileAttach: "attach",
18
- AQuantityInput: "quantity",
19
- ACurrencyInput: "currency"
20
- };
21
- function S(e) {
22
- return e ? l[e] : void 0;
23
- }
24
- const c = {
25
- AFormLink: "inline",
26
- AForm: "expand",
27
- ATable: "expand"
28
- };
29
- function f(e) {
30
- return e ? c[e] : void 0;
31
- }
32
- function v(e, r) {
33
- return f(e.component ?? r) === "inline" ? "inline" : e.cardinality === "noneOrMany" || e.cardinality === "atLeastOne" ? "table" : "record";
34
- }
35
- const O = [
36
- .../* @__PURE__ */ new Set([...Object.keys(l), ...Object.keys(c)])
37
- ].toSorted(), A = /* @__PURE__ */ new Set(["neutral", "success", "warning", "danger", "brand"]), y = /* @__PURE__ */ new Set(["variant", "color", "label"]);
1
+ import { A as F, D as T, F as N, G as C, I as D, b as E, L as I, T as L, d as _, e as h, V as p, W as P, g as B, h as R, i as j, j as M, k as w, l as K, n as G, o as x, c as k, q as V, r as W, f as Y, t as z, u as Q, w as J, x as X, y as q, z as H, B as U, C as Z, m as $, E as ee, H as re, J as ae, K as te, p as ie, M as ne, N as se, s as le, O as fe, P as oe, v as ce, Q as de } from "./validation-C9P__pRF.js";
2
+ import { C as ge, a as Ae, b as ye, c as Se, d as be, r as ve, u as Oe } from "./record-Bc0lI9Rq.js";
3
+ import { f as Fe } from "./flatten-C1MjkzFh.js";
4
+ const c = /* @__PURE__ */ new Set(["neutral", "success", "warning", "danger", "brand"]), d = /* @__PURE__ */ new Set(["variant", "color", "label"]);
38
5
  function t(e) {
39
6
  return !Array.isArray(e);
40
7
  }
41
- function i(e) {
42
- return typeof e == "string" && A.has(e);
8
+ function n(e) {
9
+ return typeof e == "string" && c.has(e);
43
10
  }
44
- function d(e) {
11
+ function f(e) {
45
12
  if (e === null || typeof e != "object" || Array.isArray(e)) return !1;
46
13
  for (const a of Object.keys(e))
47
- if (!y.has(a)) return !1;
14
+ if (!d.has(a)) return !1;
48
15
  const r = e;
49
- return !(r.variant !== void 0 && !i(r.variant) || r.color !== void 0 && typeof r.color != "string" || r.label !== void 0 && typeof r.label != "string");
16
+ return !(r.variant !== void 0 && !n(r.variant) || r.color !== void 0 && typeof r.color != "string" || r.label !== void 0 && typeof r.label != "string");
50
17
  }
51
- function o(e) {
52
- return i(e) || d(e);
18
+ function l(e) {
19
+ return n(e) || f(e);
53
20
  }
54
- function n(e) {
21
+ function i(e) {
55
22
  return Array.isArray(e.choices);
56
23
  }
57
- function m(e) {
24
+ function A(e) {
58
25
  if (e === null || typeof e != "object" || Array.isArray(e)) return !1;
59
26
  const r = e;
60
- return !(typeof r.label != "string" || r.variant !== void 0 && !i(r.variant) || r.color !== void 0 && typeof r.color != "string");
27
+ return !(typeof r.label != "string" || r.variant !== void 0 && !n(r.variant) || r.color !== void 0 && typeof r.color != "string");
61
28
  }
62
29
  function s(e) {
63
30
  if (e === void 0 || Array.isArray(e) || !t(e)) return !1;
64
- if (n(e)) {
31
+ if (i(e)) {
65
32
  const a = e.badges;
66
- return a === void 0 ? !1 : Object.values(a).every(o);
33
+ return a === void 0 ? !1 : Object.values(a).every(l);
67
34
  }
68
35
  const r = Object.entries(e);
69
- return r.length === 0 ? !1 : r.every(([, a]) => o(a));
36
+ return r.length === 0 ? !1 : r.every(([, a]) => l(a));
70
37
  }
71
- function D(e) {
72
- return e === void 0 || Array.isArray(e) || !t(e) ? !1 : n(e);
38
+ function y(e) {
39
+ return e === void 0 || Array.isArray(e) || !t(e) ? !1 : i(e);
73
40
  }
74
- function F(e) {
75
- return e === void 0 ? [] : Array.isArray(e) ? e : t(e) ? n(e) ? e.choices : s(e) ? Object.keys(e) : [] : [];
41
+ function S(e) {
42
+ return e === void 0 ? [] : Array.isArray(e) ? e : t(e) ? i(e) ? e.choices : s(e) ? Object.keys(e) : [] : [];
76
43
  }
77
- function u(e, r) {
78
- return i(e) ? { label: r, variant: e } : d(e) ? {
44
+ function o(e, r) {
45
+ return n(e) ? { label: r, variant: e } : f(e) ? {
79
46
  label: e.label ?? r,
80
47
  variant: e.variant,
81
48
  color: e.color
82
49
  } : { label: r };
83
50
  }
84
- function g(e, r) {
51
+ function u(e, r) {
85
52
  if (!(r === void 0 || r === "") && r in e)
86
- return u(e[r], r);
53
+ return o(e[r], r);
87
54
  }
88
- function b(e, r) {
55
+ function g(e, r) {
89
56
  if (r === void 0 || r === "") return;
90
57
  const a = e.badges;
91
58
  if (!(!a || !(r in a)))
92
- return u(a[r], r);
59
+ return o(a[r], r);
93
60
  }
94
- function T(e, r) {
61
+ function b(e, r) {
95
62
  if (!(e === void 0 || r === void 0 || r === "") && !Array.isArray(e) && t(e)) {
96
- if (n(e)) return b(e, r);
97
- if (s(e)) return g(e, r);
63
+ if (i(e)) return g(e, r);
64
+ if (s(e)) return u(e, r);
98
65
  }
99
66
  }
100
- function C(e) {
101
- return e === void 0 || Array.isArray(e) || !t(e) ? !1 : n(e) ? e.badges !== void 0 && Object.keys(e.badges).length > 0 : s(e);
67
+ function v(e) {
68
+ return e === void 0 || Array.isArray(e) || !t(e) ? !1 : i(e) ? e.badges !== void 0 && Object.keys(e.badges).length > 0 : s(e);
102
69
  }
103
70
  export {
104
- L as ActionDefinition,
105
- O as CANONICAL_COMPONENTS,
106
- l as COMPONENT_CATEGORY,
107
- c as COMPONENT_LINK_EXPANSION,
108
- p as DoctypeFieldSchema,
109
- E as FieldsetFieldSchema,
110
- h as GQL_SCALAR_MAP,
111
- _ as INTERNAL_SCALARS,
112
- P as INTROSPECTED_IDENTITY_PROPS,
113
- R as LINK_DISPLAY_SUFFIX,
114
- j as TableFieldSchema,
115
- x as TableViewConfig,
116
- B as TriggerDefinition,
117
- M as ValueFieldSchema,
118
- w as WELL_KNOWN_SCALARS,
119
- k as WorkflowLayout,
120
- K as WorkflowMeta,
121
- G as aggregateDoctypeName,
122
- V as buildAggregateDoctype,
123
- W as buildScalarMap,
124
- Q as camelToLabel,
125
- Y as camelToSnake,
126
- z as classifyFieldType,
127
- S as componentCategory,
128
- f as componentLinkExpansion,
129
- q as convertGraphQLSchema,
130
- J as defaultIsEntityField,
131
- X as defaultIsEntityType,
132
- H as flattenFields,
133
- U as formatDoctypeDrift,
134
- Z as getDisplayField,
135
- $ as getDoctypeSlug,
136
- ee as getPrimaryKeyField,
137
- re as getRecordIdField,
138
- ae as getRecordIdentity,
139
- C as hasBadgeOptions,
140
- te as inferFieldKind,
141
- ne as isActionAllowedInState,
142
- m as isBadgeDescriptor,
71
+ F as ActionDefinition,
72
+ ge as CANONICAL_COMPONENTS,
73
+ Ae as COMPONENT_CATEGORY,
74
+ ye as COMPONENT_LINK_EXPANSION,
75
+ T as DoctypeFieldSchema,
76
+ N as FieldsetFieldSchema,
77
+ C as GQL_SCALAR_MAP,
78
+ D as INTERNAL_SCALARS,
79
+ E as INTROSPECTED_IDENTITY_PROPS,
80
+ I as LINK_DISPLAY_SUFFIX,
81
+ L as TableFieldSchema,
82
+ _ as TableViewConfig,
83
+ h as TriggerDefinition,
84
+ p as ValueFieldSchema,
85
+ P as WELL_KNOWN_SCALARS,
86
+ B as WorkflowLayout,
87
+ R as WorkflowMeta,
88
+ j as aggregateDoctypeName,
89
+ M as buildAggregateDoctype,
90
+ w as buildScalarMap,
91
+ K as camelToLabel,
92
+ G as camelToSnake,
93
+ x as classifyFieldType,
94
+ Se as componentCategory,
95
+ be as componentLinkExpansion,
96
+ k as convertGraphQLSchema,
97
+ V as defaultIsEntityField,
98
+ W as defaultIsEntityType,
99
+ Fe as flattenFields,
100
+ Y as formatDoctypeDrift,
101
+ z as getDisplayField,
102
+ Q as getDoctypeSlug,
103
+ J as getPrimaryKeyField,
104
+ X as getRecordIdField,
105
+ q as getRecordIdentity,
106
+ v as hasBadgeOptions,
107
+ H as inferFieldKind,
108
+ U as isActionAllowedInState,
109
+ A as isBadgeDescriptor,
143
110
  s as isSelectChoiceMap,
144
- D as isSelectOptions,
145
- ie as linkDisplayFieldname,
146
- T as lookupBadge,
147
- se as mergeIntrospectedDoctype,
148
- oe as normalizeFieldKind,
149
- le as parseDoctype,
150
- ce as parseField,
151
- de as pascalToSnake,
152
- ue as planGeneration,
153
- v as resolveLinkRenderMode,
154
- F as selectChoices,
155
- fe as snakeToCamel,
156
- Ae as snakeToLabel,
157
- ye as stripFieldKind,
158
- ge as toPascalCase,
159
- be as toSlug,
160
- Se as validateDoctype,
161
- ve as validateField
111
+ y as isSelectOptions,
112
+ Z as linkDisplayFieldname,
113
+ b as lookupBadge,
114
+ $ as mergeIntrospectedDoctype,
115
+ ee as normalizeFieldKind,
116
+ re as parseDoctype,
117
+ ae as parseField,
118
+ te as pascalToSnake,
119
+ ie as planGeneration,
120
+ ve as resolveLinkRenderMode,
121
+ S as selectChoices,
122
+ ne as snakeToCamel,
123
+ se as snakeToLabel,
124
+ le as stripFieldKind,
125
+ fe as toPascalCase,
126
+ oe as toSlug,
127
+ Oe as unwrapInlineLinks,
128
+ ce as validateDoctype,
129
+ de as validateField
162
130
  };
163
131
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/component-meta.ts","../src/badge.ts"],"sourcesContent":["/**\n * Semantic category for a rendering component.\n *\n * `component` is the primary field axis, so the runtime consumers that need to know what a field\n * *means* (atable cell formatting / filter widgets, record-default init) derive it from here. This\n * is the single source of \"what kind of value does this component render\", keyed by the canonical\n * registered component names — each consumer maps the category to its own concern (filter widget,\n * default value, …).\n *\n * @public\n */\nexport type ComponentCategory =\n\t'text' | 'number' | 'boolean' | 'date' | 'datetime' | 'select' | 'code' | 'link' | 'attach' | 'quantity' | 'currency'\n\n/**\n * Canonical component → semantic category. Only the components Stonecrop ships with appear here;\n * custom/unknown component names have no category and consumers fall back to their default.\n * @public\n */\nexport const COMPONENT_CATEGORY: Record<string, ComponentCategory> = {\n\tATextInput: 'text',\n\tATextboxInput: 'text',\n\tANumericInput: 'number',\n\tACheckbox: 'boolean',\n\tADate: 'date',\n\tADatePicker: 'date',\n\tADateSelection: 'date',\n\tADateTime: 'datetime',\n\tADuration: 'text',\n\tADateRange: 'date',\n\tADropdown: 'select',\n\tASegmentedControl: 'select',\n\tACodeEditor: 'code',\n\tAFormLink: 'link',\n\tAFileAttach: 'attach',\n\tAQuantityInput: 'quantity',\n\tACurrencyInput: 'currency',\n}\n\n/**\n * Resolve a component's semantic category, or `undefined` for an unknown (custom) component —\n * callers treat that as \"no opinion\" and use their own default.\n * @public\n */\nexport function componentCategory(component?: string): ComponentCategory | undefined {\n\treturn component ? COMPONENT_CATEGORY[component] : undefined\n}\n\n/**\n * Whether a link component expands its target doctype, or renders the link inline.\n *\n * This is the *only* axis the component decides. It deliberately does not choose between an\n * embedded record and an embedded table: `cardinality` states whether the value is a scalar or\n * an array, which is a fact about the data rather than a rendering preference, so a component\n * must not be able to override it (an `AForm` over a `noneOrMany` link would be handed an array\n * it cannot render). Component names encode both axes — `AFormLink`/`ATableLink` are the inline\n * pair, `AForm`/`ATable` the expanding pair — but only the inline/expand half is authoritative.\n *\n * @public\n */\nexport type LinkExpansion = 'inline' | 'expand'\n\n/**\n * Canonical link component → expansion. Only components Stonecrop ships with appear here; an\n * unmapped (custom) component has none, and callers treat that as `expand` — the behaviour that\n * predates this map, so a custom component can never silently collapse a link to a picker.\n * @public\n */\nexport const COMPONENT_LINK_EXPANSION: Record<string, LinkExpansion> = {\n\tAFormLink: 'inline',\n\tAForm: 'expand',\n\tATable: 'expand',\n}\n\n/**\n * Resolve a component's link expansion, or `undefined` for an absent/unmapped component.\n * @public\n */\nexport function componentLinkExpansion(component?: string): LinkExpansion | undefined {\n\treturn component ? COMPONENT_LINK_EXPANSION[component] : undefined\n}\n\n/**\n * How a link field renders.\n *\n * - `inline` — a scalar id-picker; the target is *not* expanded (the field keeps its own value\n * and carries a `doctype` prop for async display-text resolution and navigation).\n * - `record` — the target doctype is resolved and embedded as a nested form.\n * - `table` — the target doctype is resolved and embedded as a child table.\n *\n * @public\n */\nexport type LinkRenderMode = 'inline' | 'record' | 'table'\n\n/**\n * Decide how a *declared* link (one with a `LinkDeclaration`) renders.\n *\n * Two independent axes: the **component** picks inline vs expand, and when expanding the\n * **cardinality** picks record vs table (many → table). The declaration's component wins over the\n * field's, matching the precedence the resolver already uses for the rendered component.\n *\n * This is the single definition of \"does this link expand\" — it is consumed by both the client\n * resolver (which builds the nested schema) and the server column builder (which must still\n * SELECT an `inline` link's FK column). Call it; never re-derive the rule at the call site, or\n * the two will drift and the client will render a table for a column the server never selected.\n *\n * @param link - the link declaration (only `component` and `cardinality` are consulted)\n * @param fieldComponent - the linked field's own `component`, used when the declaration names none\n * @public\n */\nexport function resolveLinkRenderMode(\n\tlink: { component?: string; cardinality?: string },\n\tfieldComponent?: string\n): LinkRenderMode {\n\tif (componentLinkExpansion(link.component ?? fieldComponent) === 'inline') return 'inline'\n\treturn link.cardinality === 'noneOrMany' || link.cardinality === 'atLeastOne' ? 'table' : 'record'\n}\n\n/**\n * Every component Stonecrop ships with that can render a value field, sorted by name.\n *\n * The union of the two maps above is the definition, not a copy of it: a shipped component either\n * categorises a value ({@link COMPONENT_CATEGORY}) or is one of the link containers that has no\n * value of its own ({@link COMPONENT_LINK_EXPANSION}'s `AForm`/`ATable`). `AFieldset` is absent by\n * the same rule — it is a `kind: 'fieldset'` container, so it is never a value field's component.\n *\n * `component` is an **open** axis: any string is valid, and naming a custom component is how an app\n * renders a field Stonecrop ships no widget for. This list is therefore the set to *suggest* to an\n * author, and to check first-party data against — never a set to validate arbitrary input against.\n *\n * @public\n */\nexport const CANONICAL_COMPONENTS: readonly string[] = [\n\t...new Set([...Object.keys(COMPONENT_CATEGORY), ...Object.keys(COMPONENT_LINK_EXPANSION)]),\n].toSorted()\n","import type { FieldOptions } from './field'\n\n/**\n * Semantic badge variant. Maps to theme tokens `--sc-badge-{variant}-*`.\n * @public\n */\nexport type BadgeVariant = 'neutral' | 'success' | 'warning' | 'danger' | 'brand'\n\n/**\n * Where ABadge paints the same descriptor.\n * @public\n */\nexport type BadgePresentation = 'cell-fill' | 'input-accent'\n\n/**\n * Per-choice badge configuration in a Select options map.\n * @public\n */\nexport interface BadgeSpecObject {\n\tvariant?: BadgeVariant\n\tcolor?: string\n\tlabel?: string\n}\n\n/**\n * Value in a choice→badge map: shorthand variant or object form.\n * @public\n */\nexport type BadgeSpec = BadgeVariant | BadgeSpecObject\n\n/**\n * Select field options when choices carry badge colors.\n * @public\n */\nexport interface SelectOptions extends Record<string, unknown> {\n\tchoices: string[]\n\tbadges?: Record<string, BadgeSpec>\n}\n\n/**\n * Resolved badge for rendering. Returned by `format` or built from an options map.\n * @public\n */\nexport interface BadgeDescriptor {\n\tlabel: string\n\tvariant?: BadgeVariant\n\tcolor?: string\n}\n\nconst BADGE_VARIANTS = new Set<string>(['neutral', 'success', 'warning', 'danger', 'brand'])\n\nconst BADGE_SPEC_OBJECT_KEYS = new Set(['variant', 'color', 'label'])\n\nfunction isOptionsRecord(options: FieldOptions): options is Record<string, unknown> {\n\treturn !Array.isArray(options)\n}\n\nfunction isBadgeVariant(value: unknown): value is BadgeVariant {\n\treturn typeof value === 'string' && BADGE_VARIANTS.has(value)\n}\n\nfunction isBadgeSpecObject(value: unknown): value is BadgeSpecObject {\n\tif (value === null || typeof value !== 'object' || Array.isArray(value)) return false\n\tfor (const key of Object.keys(value)) {\n\t\tif (!BADGE_SPEC_OBJECT_KEYS.has(key)) return false\n\t}\n\tconst obj = value as BadgeSpecObject\n\tif (obj.variant !== undefined && !isBadgeVariant(obj.variant)) return false\n\tif (obj.color !== undefined && typeof obj.color !== 'string') return false\n\tif (obj.label !== undefined && typeof obj.label !== 'string') return false\n\treturn true\n}\n\nfunction isBadgeSpec(value: unknown): value is BadgeSpec {\n\treturn isBadgeVariant(value) || isBadgeSpecObject(value)\n}\n\n/**\n * Narrows an options record to the structured `{ choices, badges }` form. A type predicate rather\n * than a cast: `SelectOptions` requires `choices`, so asserting into it trips `no-unsafe-type-assertion`.\n */\nfunction isStructuredSelectOptions(options: Record<string, unknown>): options is SelectOptions {\n\treturn Array.isArray(options.choices)\n}\n\n/**\n * True when `value` is a resolved badge descriptor for ACell / ADropdown.\n * @public\n */\nexport function isBadgeDescriptor(value: unknown): value is BadgeDescriptor {\n\tif (value === null || typeof value !== 'object' || Array.isArray(value)) return false\n\t// Partial<> keeps this a widening assertion; asserting into BadgeDescriptor itself (required\n\t// `label`) narrows, which `no-unsafe-type-assertion` rejects.\n\tconst obj = value as Partial<BadgeDescriptor>\n\tif (typeof obj.label !== 'string') return false\n\tif (obj.variant !== undefined && !isBadgeVariant(obj.variant)) return false\n\tif (obj.color !== undefined && typeof obj.color !== 'string') return false\n\treturn true\n}\n\n/**\n * True when `options` is a Select choice map (`{ Open: \"warning\", ... }` or\n * `{ choices: [...], badges: {...} }`), not a quantity/currency/code config bag.\n * @public\n */\nexport function isSelectChoiceMap(options: FieldOptions | undefined): options is Record<string, BadgeSpec> {\n\tif (options === undefined || Array.isArray(options) || !isOptionsRecord(options)) return false\n\tif (isStructuredSelectOptions(options)) {\n\t\tconst badges = options.badges\n\t\tif (badges === undefined) return false\n\t\treturn Object.values(badges).every(isBadgeSpec)\n\t}\n\tconst entries = Object.entries(options)\n\tif (entries.length === 0) return false\n\treturn entries.every(([, value]) => isBadgeSpec(value))\n}\n\n/**\n * True when `options` uses the structured SelectOptions shape.\n * @public\n */\nexport function isSelectOptions(options: FieldOptions | undefined): options is SelectOptions {\n\tif (options === undefined || Array.isArray(options) || !isOptionsRecord(options)) return false\n\treturn isStructuredSelectOptions(options)\n}\n\n/**\n * Dropdown / filter choice strings.\n * @public\n */\nexport function selectChoices(options: FieldOptions | undefined): string[] {\n\tif (options === undefined) return []\n\tif (Array.isArray(options)) return options\n\tif (!isOptionsRecord(options)) return []\n\tif (isStructuredSelectOptions(options)) return options.choices\n\tif (isSelectChoiceMap(options)) return Object.keys(options)\n\treturn []\n}\n\nfunction normalizeBadgeSpec(spec: unknown, key: string): BadgeDescriptor {\n\tif (isBadgeVariant(spec)) {\n\t\treturn { label: key, variant: spec }\n\t}\n\t// A malformed spec still names a real choice, so keep the label and drop only the styling.\n\t// Reading `.variant`/`.color` off an unvalidated value is what let a misspelt variant reach the\n\t// DOM as an unmatched class, and threw outright on null.\n\tif (!isBadgeSpecObject(spec)) {\n\t\treturn { label: key }\n\t}\n\treturn {\n\t\tlabel: spec.label ?? key,\n\t\tvariant: spec.variant,\n\t\tcolor: spec.color,\n\t}\n}\n\nfunction lookupFromBareMap(map: Record<string, BadgeSpec>, key: string | undefined): BadgeDescriptor | undefined {\n\tif (key === undefined || key === '') return undefined\n\tif (!(key in map)) return undefined\n\treturn normalizeBadgeSpec(map[key], key)\n}\n\nfunction lookupFromStructured(options: SelectOptions, key: string | undefined): BadgeDescriptor | undefined {\n\tif (key === undefined || key === '') return undefined\n\tconst badges = options.badges\n\tif (!badges || !(key in badges)) return undefined\n\treturn normalizeBadgeSpec(badges[key], key)\n}\n\n/**\n * Resolve a stored choice value to a badge descriptor using field options.\n * @public\n */\nexport function lookupBadge(options: FieldOptions | undefined, key: string | undefined): BadgeDescriptor | undefined {\n\tif (options === undefined || key === undefined || key === '') return undefined\n\tif (Array.isArray(options)) return undefined\n\tif (!isOptionsRecord(options)) return undefined\n\tif (isStructuredSelectOptions(options)) return lookupFromStructured(options, key)\n\tif (isSelectChoiceMap(options)) return lookupFromBareMap(options, key)\n\treturn undefined\n}\n\n/**\n * Whether field options carry any badge mapping.\n * @public\n */\nexport function hasBadgeOptions(options: FieldOptions | undefined): boolean {\n\tif (options === undefined || Array.isArray(options)) return false\n\tif (!isOptionsRecord(options)) return false\n\tif (isStructuredSelectOptions(options)) return options.badges !== undefined && Object.keys(options.badges).length > 0\n\treturn isSelectChoiceMap(options)\n}\n"],"names":["COMPONENT_CATEGORY","componentCategory","component","COMPONENT_LINK_EXPANSION","componentLinkExpansion","resolveLinkRenderMode","link","fieldComponent","CANONICAL_COMPONENTS","BADGE_VARIANTS","BADGE_SPEC_OBJECT_KEYS","isOptionsRecord","options","isBadgeVariant","value","isBadgeSpecObject","key","obj","isBadgeSpec","isStructuredSelectOptions","isBadgeDescriptor","isSelectChoiceMap","badges","entries","isSelectOptions","selectChoices","normalizeBadgeSpec","spec","lookupFromBareMap","map","lookupFromStructured","lookupBadge","hasBadgeOptions"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/badge.ts"],"sourcesContent":["import type { FieldOptions } from './field'\n\n/**\n * Semantic badge variant. Maps to theme tokens `--sc-badge-{variant}-*`.\n * @public\n */\nexport type BadgeVariant = 'neutral' | 'success' | 'warning' | 'danger' | 'brand'\n\n/**\n * Where ABadge paints the same descriptor.\n * @public\n */\nexport type BadgePresentation = 'cell-fill' | 'input-accent'\n\n/**\n * Per-choice badge configuration in a Select options map.\n * @public\n */\nexport interface BadgeSpecObject {\n\tvariant?: BadgeVariant\n\tcolor?: string\n\tlabel?: string\n}\n\n/**\n * Value in a choice→badge map: shorthand variant or object form.\n * @public\n */\nexport type BadgeSpec = BadgeVariant | BadgeSpecObject\n\n/**\n * Select field options when choices carry badge colors.\n * @public\n */\nexport interface SelectOptions extends Record<string, unknown> {\n\tchoices: string[]\n\tbadges?: Record<string, BadgeSpec>\n}\n\n/**\n * Resolved badge for rendering. Returned by `format` or built from an options map.\n * @public\n */\nexport interface BadgeDescriptor {\n\tlabel: string\n\tvariant?: BadgeVariant\n\tcolor?: string\n}\n\nconst BADGE_VARIANTS = new Set<string>(['neutral', 'success', 'warning', 'danger', 'brand'])\n\nconst BADGE_SPEC_OBJECT_KEYS = new Set(['variant', 'color', 'label'])\n\nfunction isOptionsRecord(options: FieldOptions): options is Record<string, unknown> {\n\treturn !Array.isArray(options)\n}\n\nfunction isBadgeVariant(value: unknown): value is BadgeVariant {\n\treturn typeof value === 'string' && BADGE_VARIANTS.has(value)\n}\n\nfunction isBadgeSpecObject(value: unknown): value is BadgeSpecObject {\n\tif (value === null || typeof value !== 'object' || Array.isArray(value)) return false\n\tfor (const key of Object.keys(value)) {\n\t\tif (!BADGE_SPEC_OBJECT_KEYS.has(key)) return false\n\t}\n\tconst obj = value as BadgeSpecObject\n\tif (obj.variant !== undefined && !isBadgeVariant(obj.variant)) return false\n\tif (obj.color !== undefined && typeof obj.color !== 'string') return false\n\tif (obj.label !== undefined && typeof obj.label !== 'string') return false\n\treturn true\n}\n\nfunction isBadgeSpec(value: unknown): value is BadgeSpec {\n\treturn isBadgeVariant(value) || isBadgeSpecObject(value)\n}\n\n/**\n * Narrows an options record to the structured `{ choices, badges }` form. A type predicate rather\n * than a cast: `SelectOptions` requires `choices`, so asserting into it trips `no-unsafe-type-assertion`.\n */\nfunction isStructuredSelectOptions(options: Record<string, unknown>): options is SelectOptions {\n\treturn Array.isArray(options.choices)\n}\n\n/**\n * True when `value` is a resolved badge descriptor for ACell / ADropdown.\n * @public\n */\nexport function isBadgeDescriptor(value: unknown): value is BadgeDescriptor {\n\tif (value === null || typeof value !== 'object' || Array.isArray(value)) return false\n\t// Partial<> keeps this a widening assertion; asserting into BadgeDescriptor itself (required\n\t// `label`) narrows, which `no-unsafe-type-assertion` rejects.\n\tconst obj = value as Partial<BadgeDescriptor>\n\tif (typeof obj.label !== 'string') return false\n\tif (obj.variant !== undefined && !isBadgeVariant(obj.variant)) return false\n\tif (obj.color !== undefined && typeof obj.color !== 'string') return false\n\treturn true\n}\n\n/**\n * True when `options` is a Select choice map (`{ Open: \"warning\", ... }` or\n * `{ choices: [...], badges: {...} }`), not a quantity/currency/code config bag.\n * @public\n */\nexport function isSelectChoiceMap(options: FieldOptions | undefined): options is Record<string, BadgeSpec> {\n\tif (options === undefined || Array.isArray(options) || !isOptionsRecord(options)) return false\n\tif (isStructuredSelectOptions(options)) {\n\t\tconst badges = options.badges\n\t\tif (badges === undefined) return false\n\t\treturn Object.values(badges).every(isBadgeSpec)\n\t}\n\tconst entries = Object.entries(options)\n\tif (entries.length === 0) return false\n\treturn entries.every(([, value]) => isBadgeSpec(value))\n}\n\n/**\n * True when `options` uses the structured SelectOptions shape.\n * @public\n */\nexport function isSelectOptions(options: FieldOptions | undefined): options is SelectOptions {\n\tif (options === undefined || Array.isArray(options) || !isOptionsRecord(options)) return false\n\treturn isStructuredSelectOptions(options)\n}\n\n/**\n * Dropdown / filter choice strings.\n * @public\n */\nexport function selectChoices(options: FieldOptions | undefined): string[] {\n\tif (options === undefined) return []\n\tif (Array.isArray(options)) return options\n\tif (!isOptionsRecord(options)) return []\n\tif (isStructuredSelectOptions(options)) return options.choices\n\tif (isSelectChoiceMap(options)) return Object.keys(options)\n\treturn []\n}\n\nfunction normalizeBadgeSpec(spec: unknown, key: string): BadgeDescriptor {\n\tif (isBadgeVariant(spec)) {\n\t\treturn { label: key, variant: spec }\n\t}\n\t// A malformed spec still names a real choice, so keep the label and drop only the styling.\n\t// Reading `.variant`/`.color` off an unvalidated value is what let a misspelt variant reach the\n\t// DOM as an unmatched class, and threw outright on null.\n\tif (!isBadgeSpecObject(spec)) {\n\t\treturn { label: key }\n\t}\n\treturn {\n\t\tlabel: spec.label ?? key,\n\t\tvariant: spec.variant,\n\t\tcolor: spec.color,\n\t}\n}\n\nfunction lookupFromBareMap(map: Record<string, BadgeSpec>, key: string | undefined): BadgeDescriptor | undefined {\n\tif (key === undefined || key === '') return undefined\n\tif (!(key in map)) return undefined\n\treturn normalizeBadgeSpec(map[key], key)\n}\n\nfunction lookupFromStructured(options: SelectOptions, key: string | undefined): BadgeDescriptor | undefined {\n\tif (key === undefined || key === '') return undefined\n\tconst badges = options.badges\n\tif (!badges || !(key in badges)) return undefined\n\treturn normalizeBadgeSpec(badges[key], key)\n}\n\n/**\n * Resolve a stored choice value to a badge descriptor using field options.\n * @public\n */\nexport function lookupBadge(options: FieldOptions | undefined, key: string | undefined): BadgeDescriptor | undefined {\n\tif (options === undefined || key === undefined || key === '') return undefined\n\tif (Array.isArray(options)) return undefined\n\tif (!isOptionsRecord(options)) return undefined\n\tif (isStructuredSelectOptions(options)) return lookupFromStructured(options, key)\n\tif (isSelectChoiceMap(options)) return lookupFromBareMap(options, key)\n\treturn undefined\n}\n\n/**\n * Whether field options carry any badge mapping.\n * @public\n */\nexport function hasBadgeOptions(options: FieldOptions | undefined): boolean {\n\tif (options === undefined || Array.isArray(options)) return false\n\tif (!isOptionsRecord(options)) return false\n\tif (isStructuredSelectOptions(options)) return options.badges !== undefined && Object.keys(options.badges).length > 0\n\treturn isSelectChoiceMap(options)\n}\n"],"names":["BADGE_VARIANTS","BADGE_SPEC_OBJECT_KEYS","isOptionsRecord","options","isBadgeVariant","value","isBadgeSpecObject","key","obj","isBadgeSpec","isStructuredSelectOptions","isBadgeDescriptor","isSelectChoiceMap","badges","entries","isSelectOptions","selectChoices","normalizeBadgeSpec","spec","lookupFromBareMap","map","lookupFromStructured","lookupBadge","hasBadgeOptions"],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ import { f as d } from "./flatten-C1MjkzFh.js";
2
+ const r = {
3
+ ATextInput: "text",
4
+ ATextboxInput: "text",
5
+ ANumericInput: "number",
6
+ ACheckbox: "boolean",
7
+ ADate: "date",
8
+ ADatePicker: "date",
9
+ ADateSelection: "date",
10
+ ADateTime: "datetime",
11
+ ADuration: "text",
12
+ ADateRange: "date",
13
+ ADropdown: "select",
14
+ ASegmentedControl: "select",
15
+ ACodeEditor: "code",
16
+ AFormLink: "link",
17
+ AFileAttach: "attach",
18
+ AQuantityInput: "quantity",
19
+ ACurrencyInput: "currency"
20
+ };
21
+ function A(e) {
22
+ return e ? r[e] : void 0;
23
+ }
24
+ const c = {
25
+ AFormLink: "inline",
26
+ AForm: "expand",
27
+ ATable: "expand"
28
+ };
29
+ function s(e) {
30
+ return e ? c[e] : void 0;
31
+ }
32
+ function f(e, n) {
33
+ return s(e.component ?? n) === "inline" ? "inline" : e.cardinality === "noneOrMany" || e.cardinality === "atLeastOne" ? "table" : "record";
34
+ }
35
+ const p = [
36
+ .../* @__PURE__ */ new Set([...Object.keys(r), ...Object.keys(c)])
37
+ ].toSorted();
38
+ function m(e, n) {
39
+ const o = new Set(
40
+ d(e).filter((t) => t.kind === "field" && !!t.doctype).filter((t) => s(t.component) === "inline").map((t) => t.fieldname)
41
+ );
42
+ if (o.size === 0) return n;
43
+ const i = new Set(e.filter((t) => t.kind === "fieldset").map((t) => t.fieldname));
44
+ return u(o, i, n);
45
+ }
46
+ function u(e, n, o) {
47
+ const i = { ...o };
48
+ for (const [t, a] of Object.entries(i))
49
+ a === null || typeof a != "object" || Array.isArray(a) || (e.has(t) ? "id" in a && (i[t] = a.id) : n.has(t) && (i[t] = u(e, n, a)));
50
+ return i;
51
+ }
52
+ export {
53
+ p as C,
54
+ r as a,
55
+ c as b,
56
+ A as c,
57
+ s as d,
58
+ f as r,
59
+ m as u
60
+ };
61
+ //# sourceMappingURL=record-Bc0lI9Rq.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"record-Bc0lI9Rq.js","sources":["../src/component-meta.ts","../src/record.ts"],"sourcesContent":["/**\n * Semantic category for a rendering component.\n *\n * `component` is the primary field axis, so the runtime consumers that need to know what a field\n * *means* (atable cell formatting / filter widgets, record-default init) derive it from here. This\n * is the single source of \"what kind of value does this component render\", keyed by the canonical\n * registered component names — each consumer maps the category to its own concern (filter widget,\n * default value, …).\n *\n * @public\n */\nexport type ComponentCategory =\n\t'text' | 'number' | 'boolean' | 'date' | 'datetime' | 'select' | 'code' | 'link' | 'attach' | 'quantity' | 'currency'\n\n/**\n * Canonical component → semantic category. Only the components Stonecrop ships with appear here;\n * custom/unknown component names have no category and consumers fall back to their default.\n * @public\n */\nexport const COMPONENT_CATEGORY: Record<string, ComponentCategory> = {\n\tATextInput: 'text',\n\tATextboxInput: 'text',\n\tANumericInput: 'number',\n\tACheckbox: 'boolean',\n\tADate: 'date',\n\tADatePicker: 'date',\n\tADateSelection: 'date',\n\tADateTime: 'datetime',\n\tADuration: 'text',\n\tADateRange: 'date',\n\tADropdown: 'select',\n\tASegmentedControl: 'select',\n\tACodeEditor: 'code',\n\tAFormLink: 'link',\n\tAFileAttach: 'attach',\n\tAQuantityInput: 'quantity',\n\tACurrencyInput: 'currency',\n}\n\n/**\n * Resolve a component's semantic category, or `undefined` for an unknown (custom) component —\n * callers treat that as \"no opinion\" and use their own default.\n * @public\n */\nexport function componentCategory(component?: string): ComponentCategory | undefined {\n\treturn component ? COMPONENT_CATEGORY[component] : undefined\n}\n\n/**\n * Whether a link component expands its target doctype, or renders the link inline.\n *\n * This is the *only* axis the component decides. It deliberately does not choose between an\n * embedded record and an embedded table: `cardinality` states whether the value is a scalar or\n * an array, which is a fact about the data rather than a rendering preference, so a component\n * must not be able to override it (an `AForm` over a `noneOrMany` link would be handed an array\n * it cannot render). Component names encode both axes — `AFormLink`/`ATableLink` are the inline\n * pair, `AForm`/`ATable` the expanding pair — but only the inline/expand half is authoritative.\n *\n * @public\n */\nexport type LinkExpansion = 'inline' | 'expand'\n\n/**\n * Canonical link component → expansion. Only components Stonecrop ships with appear here; an\n * unmapped (custom) component has none, and callers treat that as `expand` — the behaviour that\n * predates this map, so a custom component can never silently collapse a link to a picker.\n * @public\n */\nexport const COMPONENT_LINK_EXPANSION: Record<string, LinkExpansion> = {\n\tAFormLink: 'inline',\n\tAForm: 'expand',\n\tATable: 'expand',\n}\n\n/**\n * Resolve a component's link expansion, or `undefined` for an absent/unmapped component.\n * @public\n */\nexport function componentLinkExpansion(component?: string): LinkExpansion | undefined {\n\treturn component ? COMPONENT_LINK_EXPANSION[component] : undefined\n}\n\n/**\n * How a link field renders.\n *\n * - `inline` — a scalar id-picker; the target is *not* expanded (the field keeps its own value\n * and carries a `doctype` prop for async display-text resolution and navigation).\n * - `record` — the target doctype is resolved and embedded as a nested form.\n * - `table` — the target doctype is resolved and embedded as a child table.\n *\n * @public\n */\nexport type LinkRenderMode = 'inline' | 'record' | 'table'\n\n/**\n * Decide how a *declared* link (one with a `LinkDeclaration`) renders.\n *\n * Two independent axes: the **component** picks inline vs expand, and when expanding the\n * **cardinality** picks record vs table (many → table). The declaration's component wins over the\n * field's, matching the precedence the resolver already uses for the rendered component.\n *\n * This is the single definition of \"does this link expand\" — it is consumed by both the client\n * resolver (which builds the nested schema) and the server column builder (which must still\n * SELECT an `inline` link's FK column). Call it; never re-derive the rule at the call site, or\n * the two will drift and the client will render a table for a column the server never selected.\n *\n * @param link - the link declaration (only `component` and `cardinality` are consulted)\n * @param fieldComponent - the linked field's own `component`, used when the declaration names none\n * @public\n */\nexport function resolveLinkRenderMode(\n\tlink: { component?: string; cardinality?: string },\n\tfieldComponent?: string\n): LinkRenderMode {\n\tif (componentLinkExpansion(link.component ?? fieldComponent) === 'inline') return 'inline'\n\treturn link.cardinality === 'noneOrMany' || link.cardinality === 'atLeastOne' ? 'table' : 'record'\n}\n\n/**\n * Every component Stonecrop ships with that can render a value field, sorted by name.\n *\n * The union of the two maps above is the definition, not a copy of it: a shipped component either\n * categorises a value ({@link COMPONENT_CATEGORY}) or is one of the link containers that has no\n * value of its own ({@link COMPONENT_LINK_EXPANSION}'s `AForm`/`ATable`). `AFieldset` is absent by\n * the same rule — it is a `kind: 'fieldset'` container, so it is never a value field's component.\n *\n * `component` is an **open** axis: any string is valid, and naming a custom component is how an app\n * renders a field Stonecrop ships no widget for. This list is therefore the set to *suggest* to an\n * author, and to check first-party data against — never a set to validate arbitrary input against.\n *\n * @public\n */\nexport const CANONICAL_COMPONENTS: readonly string[] = [\n\t...new Set([...Object.keys(COMPONENT_CATEGORY), ...Object.keys(COMPONENT_LINK_EXPANSION)]),\n].toSorted()\n","import { componentLinkExpansion } from './component-meta'\nimport type { DoctypeField } from './field'\nimport { flattenFields } from './flatten'\n\n/**\n * Reduce a record's *inline* link values to the ids that get persisted.\n *\n * The adapter returns an inline link as `{ id, displayText }`, so that is what a record holds\n * everywhere it is read — the store, a list row, a form field. A column takes the id alone, so\n * this is the single definition of the shape a record leaves in, and it belongs at the boundary\n * a record crosses on its way to the server, never on the way into the store.\n *\n * Doing it on the way in destroys the text the adapter looked up: nothing else holds it, so the\n * field that resolved a moment ago renders its raw id, and the same record then renders\n * differently depending on whether anything had edited the form yet. That is the bug this exists\n * to prevent, and its damage is a wrong render, not a throw.\n *\n * Only *inline* links may be reduced. An inline link's value is indistinguishable by inspection\n * from an expanded one (`{ id, ...the whole target record }`), so `component` — which states\n * which of the two a field is — is what tells them apart, via {@link componentLinkExpansion}.\n * Reducing an expanded link would send the id in place of the record.\n *\n * Fieldsets are descended into in both shapes a record appears in: flat, as the store and the\n * server hold it, and nested under the fieldset's own key, as a form emits it.\n *\n * @param fields - the doctype's top-level fields\n * @param record - the record to reduce; not mutated\n * @returns a shallow copy with every inline link reduced to its id\n * @public\n */\nexport function unwrapInlineLinks(fields: readonly DoctypeField[], record: Record<string, any>): Record<string, any> {\n\tconst inline = new Set(\n\t\tflattenFields(fields)\n\t\t\t.filter(field => field.kind === 'field' && Boolean(field.doctype))\n\t\t\t.filter(field => componentLinkExpansion(field.component) === 'inline')\n\t\t\t.map(field => field.fieldname)\n\t)\n\tif (inline.size === 0) return record\n\n\tconst fieldsets = new Set(fields.filter(field => field.kind === 'fieldset').map(field => field.fieldname))\n\treturn unwrapWith(inline, fieldsets, record)\n}\n\nfunction unwrapWith(\n\tinline: ReadonlySet<string>,\n\tfieldsets: ReadonlySet<string>,\n\trecord: Record<string, any>\n): Record<string, any> {\n\tconst result: Record<string, any> = { ...record }\n\tfor (const [key, value] of Object.entries(result)) {\n\t\tif (value === null || typeof value !== 'object' || Array.isArray(value)) continue\n\t\tif (inline.has(key)) {\n\t\t\t// `'id' in value` rather than a truthiness test: an inline link that was never resolved\n\t\t\t// is still a bare scalar, and reducing it a second time would be a no-op at best.\n\t\t\tif ('id' in value) result[key] = value.id\n\t\t} else if (fieldsets.has(key)) {\n\t\t\tresult[key] = unwrapWith(inline, fieldsets, value)\n\t\t}\n\t}\n\treturn result\n}\n"],"names":["COMPONENT_CATEGORY","componentCategory","component","COMPONENT_LINK_EXPANSION","componentLinkExpansion","resolveLinkRenderMode","link","fieldComponent","CANONICAL_COMPONENTS","unwrapInlineLinks","fields","record","inline","flattenFields","field","fieldsets","unwrapWith","result","key","value"],"mappings":";AAmBO,MAAMA,IAAwD;AAAA,EACpE,YAAY;AAAA,EACZ,eAAe;AAAA,EACf,eAAe;AAAA,EACf,WAAW;AAAA,EACX,OAAO;AAAA,EACP,aAAa;AAAA,EACb,gBAAgB;AAAA,EAChB,WAAW;AAAA,EACX,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,mBAAmB;AAAA,EACnB,aAAa;AAAA,EACb,WAAW;AAAA,EACX,aAAa;AAAA,EACb,gBAAgB;AAAA,EAChB,gBAAgB;AACjB;AAOO,SAASC,EAAkBC,GAAmD;AACpF,SAAOA,IAAYF,EAAmBE,CAAS,IAAI;AACpD;AAsBO,MAAMC,IAA0D;AAAA,EACtE,WAAW;AAAA,EACX,OAAO;AAAA,EACP,QAAQ;AACT;AAMO,SAASC,EAAuBF,GAA+C;AACrF,SAAOA,IAAYC,EAAyBD,CAAS,IAAI;AAC1D;AA8BO,SAASG,EACfC,GACAC,GACiB;AACjB,SAAIH,EAAuBE,EAAK,aAAaC,CAAc,MAAM,WAAiB,WAC3ED,EAAK,gBAAgB,gBAAgBA,EAAK,gBAAgB,eAAe,UAAU;AAC3F;AAgBO,MAAME,IAA0C;AAAA,EACtD,GAAG,oBAAI,IAAI,CAAC,GAAG,OAAO,KAAKR,CAAkB,GAAG,GAAG,OAAO,KAAKG,CAAwB,CAAC,CAAC;AAC1F,EAAE,SAAA;ACxGK,SAASM,EAAkBC,GAAiCC,GAAkD;AACpH,QAAMC,IAAS,IAAI;AAAA,IAClBC,EAAcH,CAAM,EAClB,OAAO,CAAAI,MAASA,EAAM,SAAS,WAAW,EAAQA,EAAM,OAAQ,EAChE,OAAO,CAAAA,MAASV,EAAuBU,EAAM,SAAS,MAAM,QAAQ,EACpE,IAAI,CAAAA,MAASA,EAAM,SAAS;AAAA,EAAA;AAE/B,MAAIF,EAAO,SAAS,EAAG,QAAOD;AAE9B,QAAMI,IAAY,IAAI,IAAIL,EAAO,OAAO,CAAAI,MAASA,EAAM,SAAS,UAAU,EAAE,IAAI,CAAAA,MAASA,EAAM,SAAS,CAAC;AACzG,SAAOE,EAAWJ,GAAQG,GAAWJ,CAAM;AAC5C;AAEA,SAASK,EACRJ,GACAG,GACAJ,GACsB;AACtB,QAAMM,IAA8B,EAAE,GAAGN,EAAA;AACzC,aAAW,CAACO,GAAKC,CAAK,KAAK,OAAO,QAAQF,CAAM;AAC/C,IAAIE,MAAU,QAAQ,OAAOA,KAAU,YAAY,MAAM,QAAQA,CAAK,MAClEP,EAAO,IAAIM,CAAG,IAGb,QAAQC,MAAOF,EAAOC,CAAG,IAAIC,EAAM,MAC7BJ,EAAU,IAAIG,CAAG,MAC3BD,EAAOC,CAAG,IAAIF,EAAWJ,GAAQG,GAAWI,CAAK;AAGnD,SAAOF;AACR;"}
package/dist/record.js ADDED
@@ -0,0 +1,6 @@
1
+ import { u as o } from "./record-Bc0lI9Rq.js";
2
+ import "./flatten-C1MjkzFh.js";
3
+ export {
4
+ o as unwrapInlineLinks
5
+ };
6
+ //# sourceMappingURL=record.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"record.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}
package/dist/schema.d.ts CHANGED
@@ -849,6 +849,12 @@ export declare type FieldValidation = z.infer<typeof FieldValidation>;
849
849
  * failure this comment names — a `displayField` inside a fieldset was rejected at authoring time
850
850
  * and would have worked at runtime.
851
851
  *
852
+ * A module of its own, importing nothing at runtime, because callers need the descent without
853
+ * needing the rest of `field.ts` — which defines the Zod schemas, so a runtime edge to it is a
854
+ * runtime edge to Zod. Zod reaching a Nitro SSR entry collides with the `process` Nitro imports
855
+ * there and takes the server down with a `SyntaxError` per request, which no build reports.
856
+ * `field.ts` still calls this and `index.ts` still exports it, so nothing outside moves.
857
+ *
852
858
  * @param fields - the doctype's top-level fields
853
859
  * @returns every non-container field, fieldset children included
854
860
  * @public
@@ -1787,6 +1793,34 @@ export declare const TriggerDefinition: z.ZodObject<{
1787
1793
  */
1788
1794
  export declare type TriggerDefinition = z.infer<typeof TriggerDefinition>;
1789
1795
 
1796
+ /**
1797
+ * Reduce a record's *inline* link values to the ids that get persisted.
1798
+ *
1799
+ * The adapter returns an inline link as `{ id, displayText }`, so that is what a record holds
1800
+ * everywhere it is read — the store, a list row, a form field. A column takes the id alone, so
1801
+ * this is the single definition of the shape a record leaves in, and it belongs at the boundary
1802
+ * a record crosses on its way to the server, never on the way into the store.
1803
+ *
1804
+ * Doing it on the way in destroys the text the adapter looked up: nothing else holds it, so the
1805
+ * field that resolved a moment ago renders its raw id, and the same record then renders
1806
+ * differently depending on whether anything had edited the form yet. That is the bug this exists
1807
+ * to prevent, and its damage is a wrong render, not a throw.
1808
+ *
1809
+ * Only *inline* links may be reduced. An inline link's value is indistinguishable by inspection
1810
+ * from an expanded one (`{ id, ...the whole target record }`), so `component` — which states
1811
+ * which of the two a field is — is what tells them apart, via {@link componentLinkExpansion}.
1812
+ * Reducing an expanded link would send the id in place of the record.
1813
+ *
1814
+ * Fieldsets are descended into in both shapes a record appears in: flat, as the store and the
1815
+ * server hold it, and nested under the fieldset's own key, as a form emits it.
1816
+ *
1817
+ * @param fields - the doctype's top-level fields
1818
+ * @param record - the record to reduce; not mutated
1819
+ * @returns a shallow copy with every inline link reduced to its id
1820
+ * @public
1821
+ */
1822
+ export declare function unwrapInlineLinks(fields: readonly DoctypeField[], record: Record<string, any>): Record<string, any>;
1823
+
1790
1824
  /**
1791
1825
  * Validate a doctype definition
1792
1826
  * @param data - Data to validate
@@ -1,7 +1,9 @@
1
1
  import { z } from 'zod';
2
2
  import type { ColumnSchema } from './column-schema';
3
+ import { flattenFields } from './flatten';
3
4
  import type { InteractionMode } from './mode';
4
5
  import { TableViewConfig } from './table';
6
+ export { flattenFields };
5
7
  /**
6
8
  * Field options - flexible bag for type-specific configuration.
7
9
  *
@@ -257,27 +259,6 @@ export declare const INTROSPECTED_IDENTITY_PROPS: readonly ["fieldname", "primar
257
259
  * @public
258
260
  */
259
261
  export declare function getPrimaryKeyField(fields: readonly DoctypeField[]): ValueField | undefined;
260
- /**
261
- * Recursively flatten Fieldset containers into a flat array of non-container fields.
262
- * Fieldset entries are replaced by their children; all other fields pass through.
263
- *
264
- * A fieldset is a layout grouping, not a scope: every field inside one is a field of the doctype,
265
- * with a column of its own and a name a link can bind to. Anything asking "what does this doctype
266
- * declare" must therefore descend, and the two ways to get that wrong point opposite ways — the
267
- * SELECT builder would omit real columns, while a validator would report a working declaration as
268
- * broken.
269
- *
270
- * Lives here rather than in the adapter because both sides need it: the middleware builds SQL from
271
- * it, and `DoctypeMeta`'s own validation asks the same question at the load gate. It sat in the
272
- * adapter while the validator hand-rolled a top-level-only scan, and that is exactly the second
273
- * failure this comment names — a `displayField` inside a fieldset was rejected at authoring time
274
- * and would have worked at runtime.
275
- *
276
- * @param fields - the doctype's top-level fields
277
- * @returns every non-container field, fieldset children included
278
- * @public
279
- */
280
- export declare function flattenFields(fields: readonly DoctypeField[]): (ValueField | TableField)[];
281
262
  /**
282
263
  * Resolve the field a doctype nominates as its display text, or `undefined` when the nomination
283
264
  * does not name a readable column.
@@ -1 +1 @@
1
- {"version":3,"file":"field.d.ts","sourceRoot":"","sources":["../../src/field.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"field.d.ts","sourceRoot":"","sources":["../../src/field.ts"],"names":[],"mappings":"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"}
package/dist/src/field.js CHANGED
@@ -1,5 +1,9 @@
1
1
  import { z } from 'zod';
2
+ import { flattenFields } from './flatten';
2
3
  import { TableViewConfig } from './table';
4
+ // Re-exported so callers already on this module keep one import; `flatten.ts` says why the
5
+ // definition itself sits off to the side.
6
+ export { flattenFields };
3
7
  /**
4
8
  * Field options - flexible bag for type-specific configuration.
5
9
  *
@@ -209,38 +213,6 @@ export const INTROSPECTED_IDENTITY_PROPS = [
209
213
  export function getPrimaryKeyField(fields) {
210
214
  return flattenFields(fields).find((f) => f.kind === 'field' && Boolean(f.primaryKey));
211
215
  }
212
- /**
213
- * Recursively flatten Fieldset containers into a flat array of non-container fields.
214
- * Fieldset entries are replaced by their children; all other fields pass through.
215
- *
216
- * A fieldset is a layout grouping, not a scope: every field inside one is a field of the doctype,
217
- * with a column of its own and a name a link can bind to. Anything asking "what does this doctype
218
- * declare" must therefore descend, and the two ways to get that wrong point opposite ways — the
219
- * SELECT builder would omit real columns, while a validator would report a working declaration as
220
- * broken.
221
- *
222
- * Lives here rather than in the adapter because both sides need it: the middleware builds SQL from
223
- * it, and `DoctypeMeta`'s own validation asks the same question at the load gate. It sat in the
224
- * adapter while the validator hand-rolled a top-level-only scan, and that is exactly the second
225
- * failure this comment names — a `displayField` inside a fieldset was rejected at authoring time
226
- * and would have worked at runtime.
227
- *
228
- * @param fields - the doctype's top-level fields
229
- * @returns every non-container field, fieldset children included
230
- * @public
231
- */
232
- export function flattenFields(fields) {
233
- const result = [];
234
- for (const f of fields) {
235
- if (f.kind === 'fieldset') {
236
- result.push(...flattenFields(f.schema));
237
- }
238
- else {
239
- result.push(f);
240
- }
241
- }
242
- return result;
243
- }
244
216
  /**
245
217
  * Resolve the field a doctype nominates as its display text, or `undefined` when the nomination
246
218
  * does not name a readable column.
@@ -0,0 +1,29 @@
1
+ import type { DoctypeField, TableField, ValueField } from './field';
2
+ /**
3
+ * Recursively flatten Fieldset containers into a flat array of non-container fields.
4
+ * Fieldset entries are replaced by their children; all other fields pass through.
5
+ *
6
+ * A fieldset is a layout grouping, not a scope: every field inside one is a field of the doctype,
7
+ * with a column of its own and a name a link can bind to. Anything asking "what does this doctype
8
+ * declare" must therefore descend, and the two ways to get that wrong point opposite ways — the
9
+ * SELECT builder would omit real columns, while a validator would report a working declaration as
10
+ * broken.
11
+ *
12
+ * Lives here rather than in the adapter because both sides need it: the middleware builds SQL from
13
+ * it, and `DoctypeMeta`'s own validation asks the same question at the load gate. It sat in the
14
+ * adapter while the validator hand-rolled a top-level-only scan, and that is exactly the second
15
+ * failure this comment names — a `displayField` inside a fieldset was rejected at authoring time
16
+ * and would have worked at runtime.
17
+ *
18
+ * A module of its own, importing nothing at runtime, because callers need the descent without
19
+ * needing the rest of `field.ts` — which defines the Zod schemas, so a runtime edge to it is a
20
+ * runtime edge to Zod. Zod reaching a Nitro SSR entry collides with the `process` Nitro imports
21
+ * there and takes the server down with a `SyntaxError` per request, which no build reports.
22
+ * `field.ts` still calls this and `index.ts` still exports it, so nothing outside moves.
23
+ *
24
+ * @param fields - the doctype's top-level fields
25
+ * @returns every non-container field, fieldset children included
26
+ * @public
27
+ */
28
+ export declare function flattenFields(fields: readonly DoctypeField[]): (ValueField | TableField)[];
29
+ //# sourceMappingURL=flatten.d.ts.map