@stonecrop/schema 0.18.0 → 0.19.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.
@@ -1,34 +1,34 @@
1
- import { isScalarType as P, isEnumType as K, isObjectType as k, isNonNullType as v, isListType as V, isNamedType as z, buildSchema as B, buildClientSchema as W } from "graphql";
1
+ import { isScalarType as K, isEnumType as V, isObjectType as k, isNonNullType as v, isListType as z, isNamedType as B, buildSchema as W, buildClientSchema as J } from "graphql";
2
2
  import { z as e } from "zod";
3
- function je(t) {
3
+ function Ne(t) {
4
4
  return t.replace(/_([a-z])/g, (n, i) => i.toUpperCase());
5
5
  }
6
- function Oe(t) {
6
+ function $e(t) {
7
7
  return t.replace(/[A-Z]/g, (n) => `_${n.toLowerCase()}`);
8
8
  }
9
- function Ce(t) {
9
+ function ve(t) {
10
10
  return t.split("_").map((n) => n.charAt(0).toUpperCase() + n.slice(1).toLowerCase()).join(" ");
11
11
  }
12
12
  function G(t) {
13
13
  const n = t.replace(/([A-Z])/g, " $1").trim();
14
14
  return n.charAt(0).toUpperCase() + n.slice(1);
15
15
  }
16
- function J(t) {
16
+ function Q(t) {
17
17
  return t.split(/[-_\s]+/).map((n) => n.charAt(0).toUpperCase() + n.slice(1).toLowerCase()).join("");
18
18
  }
19
19
  function T(t) {
20
20
  return t.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/[\s_]+/g, "-").toLowerCase();
21
21
  }
22
- function Ne(t) {
22
+ function Me(t) {
23
23
  return t.replace(/([a-z])([A-Z])/g, "$1_$2").replace(/[\s-]+/g, "_").toLowerCase();
24
24
  }
25
- const Q = {
25
+ const H = {
26
26
  String: { component: "ATextInput" },
27
27
  Int: { component: "ANumericInput" },
28
28
  Float: { component: "ANumericInput" },
29
29
  Boolean: { component: "ACheckbox" },
30
30
  ID: { component: "ATextInput" }
31
- }, Z = {
31
+ }, Y = {
32
32
  // Arbitrary precision / large numbers — all numeric variants render with ANumericInput.
33
33
  BigFloat: { component: "ANumericInput" },
34
34
  BigDecimal: { component: "ANumericInput" },
@@ -48,17 +48,17 @@ const Q = {
48
48
  JSON: { component: "ACodeEditor" },
49
49
  JSONObject: { component: "ACodeEditor" },
50
50
  JsonNode: { component: "ACodeEditor" }
51
- }, H = /* @__PURE__ */ new Set(["Cursor"]);
52
- function Y(t) {
53
- const n = { ...Z };
54
- for (const [i, o] of Object.entries(Q))
51
+ }, Z = /* @__PURE__ */ new Set(["Cursor"]);
52
+ function X(t) {
53
+ const n = { ...Y };
54
+ for (const [i, o] of Object.entries(H))
55
55
  n[i] = o;
56
56
  if (t)
57
57
  for (const [i, o] of Object.entries(t))
58
58
  n[i] = { component: o.component ?? "ATextInput" };
59
59
  return n;
60
60
  }
61
- const X = [
61
+ const ee = [
62
62
  "Connection",
63
63
  "Edge",
64
64
  "Input",
@@ -74,66 +74,66 @@ const X = [
74
74
  "InsertResponse",
75
75
  "UpdateResponse",
76
76
  "MutationResponse"
77
- ], ee = /* @__PURE__ */ new Set(["Query", "Mutation", "Subscription"]);
78
- function te(t, n) {
79
- if (t.startsWith("__") || ee.has(t) || t === "Node")
77
+ ], te = /* @__PURE__ */ new Set(["Query", "Mutation", "Subscription"]);
78
+ function ne(t, n) {
79
+ if (t.startsWith("__") || te.has(t) || t === "Node")
80
80
  return !1;
81
- for (const o of X)
81
+ for (const o of ee)
82
82
  if (t.endsWith(o))
83
83
  return !1;
84
84
  const i = n.getFields();
85
85
  return Object.keys(i).length !== 0;
86
86
  }
87
- const ne = /* @__PURE__ */ new Set(["nodeId", "__typename", "clientMutationId"]);
88
- function ie(t, n, i) {
89
- return !ne.has(t);
87
+ const ie = /* @__PURE__ */ new Set(["nodeId", "__typename", "clientMutationId"]);
88
+ function oe(t, n, i) {
89
+ return !ie.has(t);
90
90
  }
91
91
  function _(t) {
92
92
  let n = !1, i = !1, o = t;
93
- if (v(o) && (n = !0, o = o.ofType), V(o) && (i = !0, o = o.ofType, v(o) && (o = o.ofType)), !z(o))
93
+ if (v(o) && (n = !0, o = o.ofType), z(o) && (i = !0, o = o.ofType, v(o) && (o = o.ofType)), !B(o))
94
94
  throw new Error(`Expected a named GraphQL type, got: ${String(o)}`);
95
95
  return { namedType: o, required: n, isList: i };
96
96
  }
97
- function oe(t) {
97
+ function ae(t) {
98
98
  const i = t.getFields().edges;
99
99
  if (!i) return;
100
- const { namedType: o, isList: r } = _(i.type);
101
- if (!r || !k(o)) return;
100
+ const { namedType: o, isList: a } = _(i.type);
101
+ if (!a || !k(o)) return;
102
102
  const f = o.getFields().node;
103
103
  if (!f) return;
104
104
  const { namedType: g } = _(f.type);
105
105
  if (k(g))
106
106
  return g.name;
107
107
  }
108
- function ae(t, n, i, o = {}) {
109
- const { namedType: r, required: p, isList: f } = _(n.type), g = Y(o.customScalars), a = {
108
+ function re(t, n, i, o = {}) {
109
+ const { namedType: a, required: p, isList: f } = _(n.type), g = X(o.customScalars), r = {
110
110
  kind: "field",
111
111
  fieldname: t,
112
112
  label: G(t),
113
113
  component: "ATextInput"
114
114
  };
115
- if (p && (a.required = !0), P(r)) {
116
- if (H.has(r.name))
117
- return a._unmapped = !0, o.includeUnmappedMeta && (a._graphqlType = r.name), a;
118
- if (r.name === "ID") {
119
- const u = J(t);
115
+ if (p && (r.required = !0), K(a)) {
116
+ if (Z.has(a.name))
117
+ return r._unmapped = !0, o.includeUnmappedMeta && (r._graphqlType = a.name), r;
118
+ if (a.name === "ID") {
119
+ const u = Q(t);
120
120
  if (i.has(u))
121
- return a.component = "AFormLink", a.doctype = T(u), a;
121
+ return r.component = "AFormLink", r.doctype = T(u), r;
122
122
  }
123
- const l = g[r.name];
124
- return l ? a.component = l.component : (a._unmapped = !0, o.includeUnmappedMeta && (a._graphqlType = r.name)), a;
123
+ const c = g[a.name];
124
+ return c ? r.component = c.component : (r._unmapped = !0, o.includeUnmappedMeta && (r._graphqlType = a.name)), r;
125
125
  }
126
- if (K(r))
127
- return a.component = "ADropdown", a.options = r.getValues().map((l) => l.name), a;
128
- if (k(r)) {
129
- if (!f && i.has(r.name))
130
- return a.component = "AFormLink", a.doctype = T(r.name), a;
131
- const l = oe(r);
132
- return l && i.has(l) ? (a.component = "ATable", a._isLink = !0, a.doctype = T(l), a.cardinality = "noneOrMany", a) : f && i.has(r.name) ? (a.component = "ATable", a._isLink = !0, a.doctype = T(r.name), a.cardinality = "noneOrMany", a) : (a._unmapped = !0, o.includeUnmappedMeta && (a._graphqlType = r.name), a);
126
+ if (V(a))
127
+ return r.component = "ADropdown", r.options = a.getValues().map((c) => c.name), r;
128
+ if (k(a)) {
129
+ if (!f && i.has(a.name))
130
+ return r.component = "AFormLink", r.doctype = T(a.name), r;
131
+ const c = ae(a);
132
+ return c && i.has(c) ? (r.component = "ATable", r._isLink = !0, r.doctype = T(c), r.cardinality = "noneOrMany", r) : f && i.has(a.name) ? (r.component = "ATable", r._isLink = !0, r.doctype = T(a.name), r.cardinality = "noneOrMany", r) : (r._unmapped = !0, o.includeUnmappedMeta && (r._graphqlType = a.name), r);
133
133
  }
134
- return a._unmapped = !0, o.includeUnmappedMeta && (a._graphqlType = r.name), a;
134
+ return r._unmapped = !0, o.includeUnmappedMeta && (r._graphqlType = a.name), r;
135
135
  }
136
- const re = e.object({
136
+ const se = e.object({
137
137
  /** The table view type */
138
138
  view: e.enum(["list", "uncounted", "list-expansion", "tree", "gantt", "tree-gantt"]).optional(),
139
139
  /** Allow the table to use the full width of its container */
@@ -145,7 +145,7 @@ const re = e.object({
145
145
  }).meta({
146
146
  title: "TableViewConfig",
147
147
  description: "JSON-safe view configuration for table fields in doctype authoring"
148
- }), se = e.union([
148
+ }), le = e.union([
149
149
  e.array(e.string()),
150
150
  // Select choices: ["A", "B", "C"]
151
151
  e.record(e.string(), e.unknown())
@@ -160,18 +160,18 @@ const re = e.object({
160
160
  title: "FieldValidation",
161
161
  description: "Validation configuration for form fields"
162
162
  });
163
- function $(t) {
163
+ function M(t) {
164
164
  if (typeof t != "object" || t === null || Array.isArray(t)) return t;
165
165
  const n = t;
166
166
  return "kind" in n ? t : "schema" in n ? { kind: "fieldset", ...n } : "columns" in n ? { kind: "table", ...n } : { kind: "field", ...n };
167
167
  }
168
- function le(t) {
169
- const n = $(t);
168
+ function de(t) {
169
+ const n = M(t);
170
170
  if (typeof n != "object" || n === null) return n;
171
171
  const i = n;
172
- return i.kind === "fieldset" && Array.isArray(i.schema) ? { ...i, schema: i.schema.map(le) } : n;
172
+ return i.kind === "fieldset" && Array.isArray(i.schema) ? { ...i, schema: i.schema.map(de) } : n;
173
173
  }
174
- const de = [
174
+ const pe = [
175
175
  "fieldname",
176
176
  "primaryKey",
177
177
  "required",
@@ -179,20 +179,32 @@ const de = [
179
179
  "cardinality",
180
180
  "doctype"
181
181
  ];
182
- function M(t) {
182
+ function q(t) {
183
183
  return t.find((n) => n.kind === "field" && !!n.primaryKey);
184
184
  }
185
- function ve(t) {
186
- return M(t)?.fieldname ?? "id";
185
+ function O(t) {
186
+ const n = [];
187
+ for (const i of t)
188
+ i.kind === "fieldset" ? n.push(...O(i.schema)) : n.push(i);
189
+ return n;
190
+ }
191
+ function me(t, n) {
192
+ if (n)
193
+ return O(t).find(
194
+ (i) => i.kind === "field" && !i.computed && i.fieldname === n
195
+ );
187
196
  }
188
- function $e(t, n) {
189
- const i = M(t), o = i ? [n[i.fieldname], n.id] : [n.id];
190
- for (const r of o) {
191
- if (typeof r == "number") return String(r);
192
- if (typeof r == "string" && r !== "") return r;
197
+ function qe(t) {
198
+ return q(t)?.fieldname ?? "id";
199
+ }
200
+ function Ee(t, n) {
201
+ const i = q(t), o = i ? [n[i.fieldname], n.id] : [n.id];
202
+ for (const a of o) {
203
+ if (typeof a == "number") return String(a);
204
+ if (typeof a == "string" && a !== "") return a;
193
205
  }
194
206
  }
195
- function pe() {
207
+ function ue() {
196
208
  const t = e.object({
197
209
  kind: e.literal("field"),
198
210
  fieldname: e.string().min(1),
@@ -208,7 +220,7 @@ function pe() {
208
220
  mask: e.string().optional(),
209
221
  format: e.string().optional(),
210
222
  mode: e.enum(["edit", "read", "display"]).optional(),
211
- options: se.optional(),
223
+ options: le.optional(),
212
224
  required: e.boolean().optional(),
213
225
  readOnly: e.boolean().optional(),
214
226
  hidden: e.boolean().optional(),
@@ -223,7 +235,7 @@ function pe() {
223
235
  label: e.string().optional(),
224
236
  // Validates that each column has fieldname; allows all other ColumnSchema properties
225
237
  columns: e.array(e.object({ fieldname: e.string().min(1) }).passthrough()),
226
- config: re.optional(),
238
+ config: se.optional(),
227
239
  mode: e.enum(["edit", "read", "display"]).optional()
228
240
  }).meta({ title: "TableField" });
229
241
  let i = e.never();
@@ -235,10 +247,10 @@ function pe() {
235
247
  collapsible: e.boolean().optional(),
236
248
  mode: e.enum(["edit", "read", "display"]).optional(),
237
249
  schema: e.lazy(() => i.array())
238
- }).meta({ title: "FieldsetField" }), r = e.discriminatedUnion("kind", [t, o, n]);
239
- return i = e.preprocess($, r), { ValueFieldSchema: t, TableFieldSchema: n, FieldsetFieldSchema: o, DoctypeFieldSchema: i };
250
+ }).meta({ title: "FieldsetField" }), a = e.discriminatedUnion("kind", [t, o, n]);
251
+ return i = e.preprocess(M, a), { ValueFieldSchema: t, TableFieldSchema: n, FieldsetFieldSchema: o, DoctypeFieldSchema: i };
240
252
  }
241
- const D = pe(), Me = D.ValueFieldSchema, qe = D.FieldsetFieldSchema, Ee = D.TableFieldSchema, O = D.DoctypeFieldSchema;
253
+ const D = ue(), xe = D.ValueFieldSchema, Ue = D.FieldsetFieldSchema, Pe = D.TableFieldSchema, C = D.DoctypeFieldSchema;
242
254
  function L(t) {
243
255
  const n = [];
244
256
  for (const i of t)
@@ -251,8 +263,8 @@ function j(t) {
251
263
  function A(t) {
252
264
  return t === void 0 ? "—" : JSON.stringify(t);
253
265
  }
254
- function xe(t, n) {
255
- const i = Array.isArray(t.fields) ? t.fields.filter(j) : [], o = new Map(n.fields.map((s) => [s.fieldname, s])), r = new Set(Object.keys(n.links ?? {})), p = {
266
+ function Re(t, n) {
267
+ const i = Array.isArray(t.fields) ? t.fields.filter(j) : [], o = new Map(n.fields.map((s) => [s.fieldname, s])), a = new Set(Object.keys(n.links ?? {})), p = {
256
268
  doctype: typeof t.name == "string" ? t.name : "(unnamed)",
257
269
  mode: "clean",
258
270
  tagged: [],
@@ -266,43 +278,43 @@ function xe(t, n) {
266
278
  return { ...s, schema: s.schema.filter(j).map(f) };
267
279
  const y = typeof s.fieldname == "string" ? s.fieldname : "", d = o.get(y);
268
280
  if (!d)
269
- return s.computed !== !0 && !r.has(y) && p.orphan.push(y), s;
281
+ return s.computed !== !0 && !a.has(y) && p.orphan.push(y), s;
270
282
  p.tagged.push(y), d.component !== s.component && p.componentDrift.push(`${y}: authored=${A(s.component)} schema=${A(d.component)}`), !!d.required != !!s.required && p.requiredDrift.push(`${y}: authored=${!!s.required} schema=${!!d.required}`);
271
- for (const m of de) {
283
+ for (const m of pe) {
272
284
  if (m === "fieldname" || m === "required") continue;
273
285
  const h = s[m], F = d[m];
274
286
  h === void 0 && F === void 0 || JSON.stringify(h) !== JSON.stringify(F) && p.identityDrift.push(`${y}.${m}: authored=${A(h)} schema=${A(F)}`);
275
287
  }
276
288
  return { ...s, source: "introspected" };
277
- }, g = { ...t, fields: i.map(f) }, a = new Set(L(i).map((s) => s.fieldname));
278
- p.omitted = n.fields.map((s) => s.fieldname).filter((s) => !a.has(s));
279
- const l = L(i).find((s) => s.primaryKey === !0), u = n.fields.find((s) => s.primaryKey === !0);
280
- return l && u && l.fieldname !== u.fieldname ? (p.mode = "partial", p.reason = `authored primary key '${String(l.fieldname)}' is not the derivable '${u.fieldname}' — left as authored`) : l && !u ? (p.mode = "partial", p.reason = `authored primary key '${String(l.fieldname)}' is not derivable from the schema — left as authored`) : !l && u && (p.mode = "partial", p.reason = `schema suggests '${u.fieldname}' as primary key but the doctype declares none — not applied`), { doctype: g, drift: p };
289
+ }, g = { ...t, fields: i.map(f) }, r = new Set(L(i).map((s) => s.fieldname));
290
+ p.omitted = n.fields.map((s) => s.fieldname).filter((s) => !r.has(s));
291
+ const c = L(i).find((s) => s.primaryKey === !0), u = n.fields.find((s) => s.primaryKey === !0);
292
+ return c && u && c.fieldname !== u.fieldname ? (p.mode = "partial", p.reason = `authored primary key '${String(c.fieldname)}' is not the derivable '${u.fieldname}' — left as authored`) : c && !u ? (p.mode = "partial", p.reason = `authored primary key '${String(c.fieldname)}' is not derivable from the schema — left as authored`) : !c && u && (p.mode = "partial", p.reason = `schema suggests '${u.fieldname}' as primary key but the doctype declares none — not applied`), { doctype: g, drift: p };
281
293
  }
282
- function Re(t) {
294
+ function Ke(t) {
283
295
  const n = [];
284
296
  t.reason && n.push(` ${t.doctype}: ${t.reason}`);
285
- const i = (o, r) => {
286
- r.length && n.push(` ${t.doctype}: ${o} ${r.join("; ")}`);
297
+ const i = (o, a) => {
298
+ a.length && n.push(` ${t.doctype}: ${o} ${a.join("; ")}`);
287
299
  };
288
300
  return i("identity drift", t.identityDrift), i("component drift", t.componentDrift), i("required drift", t.requiredDrift), i("authored fields with no schema field:", t.orphan), i("schema fields not modelled:", t.omitted), n;
289
301
  }
290
- function Ue(t, n = {}) {
291
- const i = me(t), o = i.getTypeMap(), r = /* @__PURE__ */ new Set(), p = i.getQueryType(), f = i.getMutationType(), g = i.getSubscriptionType();
292
- p && r.add(p.name), f && r.add(f.name), g && r.add(g.name);
293
- const a = n.isEntityType ?? te, l = /* @__PURE__ */ new Set();
302
+ function Ve(t, n = {}) {
303
+ const i = fe(t), o = i.getTypeMap(), a = /* @__PURE__ */ new Set(), p = i.getQueryType(), f = i.getMutationType(), g = i.getSubscriptionType();
304
+ p && a.add(p.name), f && a.add(f.name), g && a.add(g.name);
305
+ const r = n.isEntityType ?? ne, c = /* @__PURE__ */ new Set();
294
306
  for (const [d, m] of Object.entries(o))
295
- k(m) && (r.has(d) || a(d, m) && l.add(d));
296
- let u = l;
307
+ k(m) && (a.has(d) || r(d, m) && c.add(d));
308
+ let u = c;
297
309
  if (n.include) {
298
310
  const d = new Set(n.include);
299
- u = new Set([...l].filter((m) => d.has(m)));
311
+ u = new Set([...c].filter((m) => d.has(m)));
300
312
  }
301
313
  if (n.exclude) {
302
314
  const d = new Set(n.exclude);
303
315
  u = new Set([...u].filter((m) => !d.has(m)));
304
316
  }
305
- const s = n.isEntityField ?? ie, y = [];
317
+ const s = n.isEntityField ?? oe, y = [];
306
318
  for (const d of u) {
307
319
  const m = o[d];
308
320
  if (!k(m)) continue;
@@ -310,50 +322,50 @@ function Ue(t, n = {}) {
310
322
  F && n.onWarning?.(
311
323
  `${d}: schema exposes both 'id' (Relay identifier) and 'rowId' (the real column). Skipping 'id' and emitting 'rowId' verbatim — no primary key can be derived. Override the '_attributeName' and 'nodeIdFieldName' inflectors so the column keeps its own name.`
312
324
  );
313
- const C = Object.entries(h).filter(
314
- ([c, b]) => s(c, b, m) && !(F && c === "id")
315
- ).map(([c, b]) => {
325
+ const N = Object.entries(h).filter(
326
+ ([l, b]) => s(l, b, m) && !(F && l === "id")
327
+ ).map(([l, b]) => {
316
328
  if (n.classifyField) {
317
- const S = n.classifyField(c, b, m);
329
+ const S = n.classifyField(l, b, m);
318
330
  if (S != null)
319
331
  return {
320
332
  kind: "field",
321
- fieldname: c,
322
- label: S.label ?? c,
333
+ fieldname: l,
334
+ label: S.label ?? l,
323
335
  component: S.component ?? "ATextInput",
324
336
  ...S
325
337
  };
326
338
  }
327
- return ae(c, b, l, n);
328
- }), E = C.find(
329
- (c) => c.fieldname === "id" && c.required && !c.doctype && !c._isLink
330
- )?.fieldname, w = {}, x = C.filter((c) => c._isLink && c.doctype && c.cardinality ? (w[c.fieldname] = {
331
- target: c.doctype,
332
- cardinality: c.cardinality
333
- }, !1) : !0).map((c) => {
334
- const b = c.fieldname === E ? { primaryKey: !0 } : {};
339
+ return re(l, b, c, n);
340
+ }), x = N.find(
341
+ (l) => l.fieldname === "id" && l.required && !l.doctype && !l._isLink
342
+ )?.fieldname, w = {}, U = N.filter((l) => l._isLink && l.doctype && l.cardinality ? (w[l.fieldname] = {
343
+ target: l.doctype,
344
+ cardinality: l.cardinality
345
+ }, !1) : !0).map((l) => {
346
+ const b = l.fieldname === x ? { primaryKey: !0 } : {};
335
347
  if (!n.includeUnmappedMeta) {
336
- const { _graphqlType: De, _unmapped: we, _isLink: Ie, ...U } = c;
337
- return Object.assign(U, b, { source: "introspected" });
348
+ const { _graphqlType: _e, _unmapped: Le, _isLink: je, ...R } = l;
349
+ return Object.assign(R, b, { source: "introspected" });
338
350
  }
339
- const { _isLink: S, ...R } = c;
340
- return Object.assign(R, b, { source: "introspected" });
341
- }), N = n.doctypeNames?.[d] ?? d, I = {
342
- name: N,
343
- slug: T(N),
344
- fields: x
351
+ const { _isLink: S, ...P } = l;
352
+ return Object.assign(P, b, { source: "introspected" });
353
+ }), $ = n.doctypeNames?.[d] ?? d, I = {
354
+ name: $,
355
+ slug: T($),
356
+ fields: U
345
357
  };
346
358
  Object.keys(w).length > 0 && (I.links = w), n.includeUnmappedMeta && (I._graphqlTypeName = d), y.push(I);
347
359
  }
348
360
  return y;
349
361
  }
350
- function me(t) {
351
- return typeof t == "string" ? B(t) : W(t);
362
+ function fe(t) {
363
+ return typeof t == "string" ? W(t) : J(t);
352
364
  }
353
- const ue = e.enum(["atMostOne", "one", "noneOrMany", "atLeastOne"]).meta({
365
+ const ye = e.enum(["atMostOne", "one", "noneOrMany", "atLeastOne"]).meta({
354
366
  title: "Cardinality",
355
367
  description: "Cardinality for relationship links between doctypes"
356
- }), fe = e.object({
368
+ }), ge = e.object({
357
369
  /** Fetch method type */
358
370
  method: e.literal("sync"),
359
371
  /** Optional limit on number of records to fetch */
@@ -361,13 +373,13 @@ const ue = e.enum(["atMostOne", "one", "noneOrMany", "atLeastOne"]).meta({
361
373
  }).meta({
362
374
  title: "SyncFetch",
363
375
  description: "Sync fetch strategy - data is fetched in the initial query"
364
- }), ye = e.object({
376
+ }), he = e.object({
365
377
  /** Fetch method type */
366
378
  method: e.literal("lazy")
367
379
  }).meta({
368
380
  title: "LazyFetch",
369
381
  description: "Lazy fetch strategy - data is fetched on demand in a separate query"
370
- }), ge = e.object({
382
+ }), be = e.object({
371
383
  /** Fetch method type */
372
384
  method: e.literal("custom"),
373
385
  /** Serialized handler function to invoke */
@@ -375,14 +387,14 @@ const ue = e.enum(["atMostOne", "one", "noneOrMany", "atLeastOne"]).meta({
375
387
  }).meta({
376
388
  title: "CustomFetch",
377
389
  description: "Custom fetch strategy - uses a custom handler function"
378
- }), he = e.discriminatedUnion("method", [fe, ye, ge]).meta({
390
+ }), Fe = e.discriminatedUnion("method", [ge, he, be]).meta({
379
391
  title: "FetchStrategy",
380
392
  description: "Fetch strategy for link data loading"
381
- }), be = e.object({
393
+ }), Se = e.object({
382
394
  /** Target doctype slug */
383
395
  target: e.string().min(1),
384
396
  /** Cardinality of the relationship */
385
- cardinality: ue,
397
+ cardinality: ye,
386
398
  /** Backlink fieldname on the target doctype that points back to this link */
387
399
  backlink: e.string().optional(),
388
400
  /** Override default rendering component (AForm for 1:1, ATable for 1:many) */
@@ -390,13 +402,13 @@ const ue = e.enum(["atMostOne", "one", "noneOrMany", "atLeastOne"]).meta({
390
402
  /** Fieldname of the corresponding Link field in the fields array */
391
403
  fieldname: e.string().min(1).optional(),
392
404
  /** Fetch strategy for loading nested data */
393
- fetch: he.optional(),
405
+ fetch: Fe.optional(),
394
406
  /** Whether to block workflow actions until nested data is loaded (default: true) */
395
407
  blockWorkflows: e.boolean().optional()
396
408
  }).meta({
397
409
  title: "LinkDeclaration",
398
410
  description: "Declares a relationship from one doctype to another"
399
- }), Fe = e.object({
411
+ }), Te = e.object({
400
412
  /** Display label for the action */
401
413
  label: e.string().min(1),
402
414
  /** Fields that must have values before action can execute */
@@ -420,7 +432,7 @@ const ue = e.enum(["atMostOne", "one", "noneOrMany", "atLeastOne"]).meta({
420
432
  }).meta({
421
433
  title: "ActionDefinition",
422
434
  description: "Action definition within a workflow"
423
- }), Se = e.object({
435
+ }), ke = e.object({
424
436
  /** Optional display label; the map key is the trigger's identity */
425
437
  label: e.string().optional(),
426
438
  /** Fieldnames whose edits fire this trigger (fires when any listed field changes) */
@@ -431,44 +443,50 @@ const ue = e.enum(["atMostOne", "one", "noneOrMany", "atLeastOne"]).meta({
431
443
  title: "TriggerDefinition",
432
444
  description: "Reactive field-validation trigger — advisory client-side"
433
445
  });
434
- function Pe(t, n) {
446
+ function ze(t, n) {
435
447
  const i = t.allowedStates;
436
448
  return !i || i.length === 0 ? !0 : i.includes(n);
437
449
  }
438
- const Te = e.record(
450
+ const Ae = e.record(
439
451
  e.string(),
440
452
  e.object({
441
453
  position: e.object({ x: e.number(), y: e.number() }).optional(),
442
454
  targetPosition: e.enum(["left", "top", "right", "bottom"]).optional(),
443
455
  sourcePosition: e.enum(["left", "top", "right", "bottom"]).optional()
444
456
  })
445
- ), ke = e.object({
457
+ ), De = e.object({
446
458
  /** List of workflow states */
447
459
  states: e.array(e.string()).optional(),
448
460
  /** Actions available in this workflow */
449
- actions: e.record(e.string(), Fe).optional(),
461
+ actions: e.record(e.string(), Te).optional(),
450
462
  /** Reactive field-validation triggers (advisory, client-side), keyed by trigger name */
451
- triggers: e.record(e.string(), Se).optional(),
463
+ triggers: e.record(e.string(), ke).optional(),
452
464
  /**
453
465
  * DocBuilder node positions keyed by state name — authoring view-state. Persisted here so a
454
466
  * doctype author's manual graph arrangement survives reloads; like `triggers`, it is client-only
455
467
  * and never enters the runtime GraphQL SDL. See {@link (WorkflowLayout:variable)}.
456
468
  */
457
- layout: Te.optional()
469
+ layout: Ae.optional()
458
470
  }).meta({
459
471
  title: "WorkflowMeta",
460
472
  description: "Workflow metadata - states and actions for a doctype"
461
- }), q = e.object({
473
+ }), E = e.object({
462
474
  /** Display name of the doctype */
463
475
  name: e.string().min(1),
464
476
  /** URL-friendly slug (kebab-case) */
465
477
  slug: e.string().min(1).optional(),
478
+ /**
479
+ * Field on this doctype used when displaying a reference to one of its records.
480
+ * When a record elsewhere holds a foreign key to this doctype, the middleware may
481
+ * include `fieldname__display` alongside the raw id, resolved from this field.
482
+ */
483
+ displayField: e.string().min(1).optional(),
466
484
  /** Field definitions (a link field is one carrying `doctype`) */
467
- fields: e.array(O),
485
+ fields: e.array(C),
468
486
  /** Relationship links to other doctypes */
469
- links: e.record(e.string(), be).optional(),
487
+ links: e.record(e.string(), Se).optional(),
470
488
  /** Workflow configuration */
471
- workflow: ke.optional(),
489
+ workflow: De.optional(),
472
490
  /** Parent doctype for inheritance */
473
491
  inherits: e.string().optional()
474
492
  }).meta({
@@ -476,16 +494,26 @@ const Te = e.record(
476
494
  description: "Doctype metadata - complete definition of a doctype"
477
495
  }).superRefine((t, n) => {
478
496
  const i = t.fields.filter((o) => o.kind === "field" && o.primaryKey);
479
- i.length > 1 && n.addIssue({
497
+ if (i.length > 1 && n.addIssue({
480
498
  code: "custom",
481
499
  path: ["fields"],
482
500
  message: `Doctype declares ${i.length} primaryKey fields (${i.map((o) => o.kind === "field" ? o.fieldname : "").join(
483
501
  ", "
484
502
  )}); a record is identified by exactly one field. A composite database key is mapped to a single identity by the adapter, so a doctype never declares its parts`
485
- });
486
- });
487
- function Ke(t) {
488
- const n = O.safeParse(t);
503
+ }), t.displayField && !me(t.fields, t.displayField)) {
504
+ const o = O(t.fields).find((a) => a.fieldname === t.displayField);
505
+ n.addIssue({
506
+ code: "custom",
507
+ path: ["displayField"],
508
+ message: o ? `displayField "${t.displayField}" names a computed field, which has no column to read a display value from` : `displayField "${t.displayField}" is not declared on this doctype`
509
+ });
510
+ }
511
+ }), we = "__display";
512
+ function Be(t) {
513
+ return `${t}${we}`;
514
+ }
515
+ function We(t) {
516
+ const n = C.safeParse(t);
489
517
  return n.success ? { success: !0, errors: [] } : {
490
518
  success: !1,
491
519
  errors: n.error.issues.map((i) => ({
@@ -494,8 +522,8 @@ function Ke(t) {
494
522
  }))
495
523
  };
496
524
  }
497
- function Ve(t) {
498
- const n = q.safeParse(t);
525
+ function Je(t) {
526
+ const n = E.safeParse(t);
499
527
  return n.success ? { success: !0, errors: [] } : {
500
528
  success: !1,
501
529
  errors: n.error.issues.map((i) => ({
@@ -504,48 +532,52 @@ function Ve(t) {
504
532
  }))
505
533
  };
506
534
  }
507
- function ze(t) {
508
- return O.parse(t);
535
+ function Ge(t) {
536
+ return C.parse(t);
509
537
  }
510
- function Be(t) {
511
- return q.parse(t);
538
+ function Qe(t) {
539
+ return E.parse(t);
512
540
  }
513
541
  export {
514
- Fe as A,
515
- T as B,
516
- Ke as C,
517
- O as D,
518
- qe as F,
519
- Q as G,
520
- H as I,
521
- Ee as T,
522
- Me as V,
523
- Z as W,
524
- de as a,
525
- re as b,
526
- Ue as c,
527
- Se as d,
528
- Te as e,
529
- Re as f,
530
- ke as g,
531
- Y as h,
542
+ Te as A,
543
+ Ne as B,
544
+ ve as C,
545
+ C as D,
546
+ Q as E,
547
+ Ue as F,
548
+ H as G,
549
+ T as H,
550
+ Z as I,
551
+ We as J,
552
+ we as L,
553
+ Pe as T,
554
+ xe as V,
555
+ Y as W,
556
+ pe as a,
557
+ se as b,
558
+ Ve as c,
559
+ ke as d,
560
+ Ae as e,
561
+ Ke as f,
562
+ De as g,
563
+ X as h,
532
564
  G as i,
533
- Oe as j,
534
- ae as k,
535
- ie as l,
536
- xe as m,
537
- te as n,
538
- M as o,
539
- ve as p,
540
- $e as q,
541
- Pe as r,
542
- le as s,
543
- Be as t,
544
- ze as u,
545
- Ve as v,
546
- Ne as w,
547
- je as x,
548
- Ce as y,
549
- J as z
565
+ $e as j,
566
+ re as k,
567
+ oe as l,
568
+ Re as m,
569
+ ne as n,
570
+ O as o,
571
+ me as p,
572
+ q,
573
+ qe as r,
574
+ Ee as s,
575
+ ze as t,
576
+ Be as u,
577
+ Je as v,
578
+ de as w,
579
+ Qe as x,
580
+ Ge as y,
581
+ Me as z
550
582
  };
551
- //# sourceMappingURL=validation-C25HJXWs.js.map
583
+ //# sourceMappingURL=validation-1DGAGmuU.js.map