@atscript/ui 0.1.58

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/index.cjs ADDED
@@ -0,0 +1,1643 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ let _atscript_typescript_utils = require("@atscript/typescript/utils");
3
+ let _atscript_db_client = require("@atscript/db-client");
4
+ //#region src/shared/annotation-keys.ts
5
+ const UI_TYPE = "ui.type";
6
+ const UI_FORM_PLACEHOLDER = "ui.form.placeholder";
7
+ const UI_FORM_HINT = "ui.form.hint";
8
+ const UI_FORM_CLASSES = "ui.form.classes";
9
+ const UI_FORM_STYLES = "ui.form.styles";
10
+ const UI_FORM_AUTOCOMPLETE = "ui.form.autocomplete";
11
+ const UI_FORM_DISABLED = "ui.form.disabled";
12
+ const UI_FORM_OPTIONS = "ui.form.options";
13
+ const UI_FORM_ORDER = "ui.form.order";
14
+ const UI_FORM_TYPE = "ui.form.type";
15
+ const UI_FORM_COMPONENT = "ui.form.component";
16
+ const UI_FORM_HIDDEN = "ui.form.hidden";
17
+ const UI_FORM_ATTR = "ui.form.attr";
18
+ const UI_FORM_GRID_COL_SPAN = "ui.form.grid.colSpan";
19
+ const UI_FORM_GRID_ROW_SPAN = "ui.form.grid.rowSpan";
20
+ const UI_FORM_SUBMIT_TEXT = "ui.form.submit.text";
21
+ const UI_FORM_LABEL_SINGULAR = "ui.form.label.singular";
22
+ const UI_FORM_ACTION = "ui.form.action";
23
+ const UI_FORM_PREFIX = "ui.form.prefix";
24
+ const UI_FORM_PREFIX_REF = "ui.form.prefix.ref";
25
+ const UI_FORM_PREFIX_ICON = "ui.form.prefix.icon";
26
+ const UI_FORM_SUFFIX = "ui.form.suffix";
27
+ const UI_FORM_SUFFIX_REF = "ui.form.suffix.ref";
28
+ const UI_FORM_SUFFIX_ICON = "ui.form.suffix.icon";
29
+ const UI_TABLE_WIDTH = "ui.table.width";
30
+ const UI_TABLE_COMPONENT = "ui.table.component";
31
+ const UI_TABLE_HIDDEN = "ui.table.hidden";
32
+ const UI_TABLE_ATTR = "ui.table.attr";
33
+ const UI_TABLE_CLASSES = "ui.table.classes";
34
+ const UI_TABLE_STYLES = "ui.table.styles";
35
+ const UI_TABLE_TYPE = "ui.table.type";
36
+ const UI_TABLE_ORDER = "ui.table.order";
37
+ const UI_DICT_LABEL = "ui.dict.label";
38
+ const UI_DICT_DESCR = "ui.dict.descr";
39
+ const UI_DICT_ATTR = "ui.dict.attr";
40
+ const UI_DICT_FILTERABLE = "ui.dict.filterable";
41
+ const UI_DICT_SORTABLE = "ui.dict.sortable";
42
+ const UI_DICT_SEARCHABLE = "ui.dict.searchable";
43
+ const DB_REL_FK = "db.rel.FK";
44
+ const DB_HTTP_PATH = "db.http.path";
45
+ const DB_AMOUNT_CURRENCY = "db.amount.currency";
46
+ const DB_AMOUNT_CURRENCY_REF = "db.amount.currency.ref";
47
+ const DB_UNIT = "db.unit";
48
+ const DB_UNIT_REF = "db.unit.ref";
49
+ const DB_COLUMN_PRECISION = "db.column.precision";
50
+ const WF_ACTION_WITH_DATA = "wf.action.withData";
51
+ const META_LABEL = "meta.label";
52
+ const META_ID = "meta.id";
53
+ const META_DESCRIPTION = "meta.description";
54
+ const META_READONLY = "meta.readonly";
55
+ const META_REQUIRED = "meta.required";
56
+ const META_DEFAULT = "meta.default";
57
+ const META_SENSITIVE = "meta.sensitive";
58
+ const EXPECT_MAX_LENGTH = "expect.maxLength";
59
+ const UI_FORM_FN_PREFIX = "ui.form.fn.";
60
+ const UI_FORM_FN_LABEL = "ui.form.fn.label";
61
+ const UI_FORM_FN_PLACEHOLDER = "ui.form.fn.placeholder";
62
+ const UI_FORM_FN_DESCRIPTION = "ui.form.fn.description";
63
+ const UI_FORM_FN_HINT = "ui.form.fn.hint";
64
+ const UI_FORM_FN_HIDDEN = "ui.form.fn.hidden";
65
+ const UI_FORM_FN_DISABLED = "ui.form.fn.disabled";
66
+ const UI_FORM_FN_READONLY = "ui.form.fn.readonly";
67
+ const UI_FORM_FN_OPTIONS = "ui.form.fn.options";
68
+ const UI_FORM_FN_ATTR = "ui.form.fn.attr";
69
+ const UI_FORM_FN_VALUE = "ui.form.fn.value";
70
+ const UI_FORM_FN_CLASSES = "ui.form.fn.classes";
71
+ const UI_FORM_FN_STYLES = "ui.form.fn.styles";
72
+ const UI_FORM_FN_TITLE = "ui.form.fn.title";
73
+ const UI_FORM_FN_SUBMIT_TEXT = "ui.form.fn.submit.text";
74
+ const UI_FORM_FN_SUBMIT_DISABLED = "ui.form.fn.submit.disabled";
75
+ const UI_TABLE_FN_PREFIX = "ui.table.fn.";
76
+ const UI_TABLE_FN_ATTR = "ui.table.fn.attr";
77
+ const UI_TABLE_FN_CLASSES = "ui.table.fn.classes";
78
+ const UI_TABLE_FN_STYLES = "ui.table.fn.styles";
79
+ const UI_FORM_VALIDATE = "ui.form.validate";
80
+ //#endregion
81
+ //#region src/form/types.ts
82
+ /** Type guard: checks if a field def is an array field. */
83
+ function isArrayField(field) {
84
+ return field.type === "array";
85
+ }
86
+ /** Type guard: checks if a field def is an object field. */
87
+ function isObjectField(field) {
88
+ return field.type === "object";
89
+ }
90
+ /** Type guard: checks if a field def is a union field. */
91
+ function isUnionField(field) {
92
+ return field.type === "union";
93
+ }
94
+ /** Type guard: checks if a field def is a tuple field. */
95
+ function isTupleField(field) {
96
+ return field.type === "tuple";
97
+ }
98
+ //#endregion
99
+ //#region src/shared/field-resolver.ts
100
+ /** Static resolver — ignores fn keys, reads only static metadata. */
101
+ var StaticFieldResolver = class {
102
+ resolveFieldProp(prop, _fnKey, staticKey, _scope, opts) {
103
+ return resolveStatic(prop.metadata, staticKey, opts);
104
+ }
105
+ resolveFormProp(type, _fnKey, staticKey, _scope, opts) {
106
+ return resolveStatic(type.metadata, staticKey, opts);
107
+ }
108
+ hasComputedAnnotations(_prop) {
109
+ return false;
110
+ }
111
+ };
112
+ /** Resolves a static metadata value. Exported for reuse by dynamic resolvers. */
113
+ function resolveStatic(metadata, staticKey, opts) {
114
+ if (staticKey === void 0) return void 0;
115
+ const staticVal = metadata.get(staticKey);
116
+ if (staticVal !== void 0) {
117
+ if (opts?.staticAsBoolean) return true;
118
+ if (opts?.transform) return opts.transform(staticVal);
119
+ return staticVal;
120
+ }
121
+ }
122
+ /** Default static resolver instance. */
123
+ const defaultResolver = new StaticFieldResolver();
124
+ let activeResolver = defaultResolver;
125
+ /** Replace the active resolver (called by ui-fns to install dynamic resolution). */
126
+ function setResolver(resolver) {
127
+ activeResolver = resolver;
128
+ }
129
+ /** Get the current active resolver. */
130
+ function getResolver() {
131
+ return activeResolver;
132
+ }
133
+ /** Resolve a field-level metadata property via the active resolver. */
134
+ function resolveFieldProp(prop, fnKey, staticKey, scope, opts) {
135
+ return activeResolver.resolveFieldProp(prop, fnKey, staticKey, scope, opts);
136
+ }
137
+ /** Resolve a form-level metadata property via the active resolver. */
138
+ function resolveFormProp(type, fnKey, staticKey, scope, opts) {
139
+ return activeResolver.resolveFormProp(type, fnKey, staticKey, scope, opts);
140
+ }
141
+ /** Check if a prop has dynamic annotations via the active resolver. */
142
+ function hasComputedAnnotations(prop) {
143
+ return activeResolver.hasComputedAnnotations(prop);
144
+ }
145
+ function getFieldMeta(prop, key) {
146
+ return prop.metadata.get(key);
147
+ }
148
+ /** Ensures a value is an array — returns as-is if already one, wraps in `[x]` otherwise. */
149
+ function asArray(x) {
150
+ return Array.isArray(x) ? x : [x];
151
+ }
152
+ /**
153
+ * Parses static `ui.attr` metadata into a key-value record.
154
+ * Exported so ui-fns can reuse this without duplicating the parsing logic.
155
+ */
156
+ function parseStaticAttrs(staticAttrs) {
157
+ if (!staticAttrs) return void 0;
158
+ const result = {};
159
+ let hasAttrs = false;
160
+ for (const item of asArray(staticAttrs)) if (typeof item === "object" && item !== null && "name" in item && "value" in item) {
161
+ const { name, value } = item;
162
+ result[name] = value;
163
+ hasAttrs = true;
164
+ }
165
+ return hasAttrs ? result : void 0;
166
+ }
167
+ /**
168
+ * Resolves `<staticKey>` + `<fnKey>` attr metadata on demand.
169
+ * Defaults read `ui.form.attr` + `ui.form.fn.attr`; pass `{ staticKey, fnKey }` to read
170
+ * the table-side pair (`ui.table.attr` + `ui.table.fn.attr`) or any other surface.
171
+ */
172
+ function resolveAttrs(prop, scope, keys = {}) {
173
+ const staticKey = keys.staticKey ?? "ui.form.attr";
174
+ const fnKey = keys.fnKey ?? "ui.form.fn.attr";
175
+ const staticAttrs = getFieldMeta(prop, staticKey);
176
+ const fnAttrs = getFieldMeta(prop, fnKey);
177
+ if (!staticAttrs && !fnAttrs) return void 0;
178
+ const result = {};
179
+ let hasAttrs = false;
180
+ const parsed = parseStaticAttrs(staticAttrs);
181
+ if (parsed) {
182
+ Object.assign(result, parsed);
183
+ hasAttrs = true;
184
+ }
185
+ if (fnAttrs) {
186
+ const resolved = resolveFieldProp(prop, fnKey, void 0, scope);
187
+ if (resolved) {
188
+ Object.assign(result, resolved);
189
+ hasAttrs = true;
190
+ }
191
+ }
192
+ return hasAttrs ? result : void 0;
193
+ }
194
+ //#endregion
195
+ //#region src/value-help/extract-literals.ts
196
+ /**
197
+ * Extracts options from a union of literal types (e.g. 'a' | 'b' | 'c').
198
+ * Returns undefined if the type is not a pure union of literals.
199
+ *
200
+ * Handles nested unions created by flattenAnnotatedType, which recurses
201
+ * into union items and produces synthetic unions containing both individual
202
+ * literals and the original union type as nested items.
203
+ */
204
+ function extractLiteralOptions(prop) {
205
+ if (prop.type.kind !== "union") return void 0;
206
+ const result = collectLiterals(prop.type.items, /* @__PURE__ */ new Set());
207
+ return result && result.length > 0 ? result : void 0;
208
+ }
209
+ /** Returns true when the annotated type is a union composed entirely of literal values. */
210
+ function isPureLiteralUnion(prop) {
211
+ if (prop.type.kind !== "union") return false;
212
+ return checkAllLiterals(prop.type.items);
213
+ }
214
+ /** Walks union items returning true only if every leaf is a literal value. */
215
+ function checkAllLiterals(items) {
216
+ for (const item of items) {
217
+ if (item.type.kind === "" && item.type.value !== void 0) continue;
218
+ if (item.type.kind === "union") {
219
+ if (!checkAllLiterals(item.type.items)) return false;
220
+ continue;
221
+ }
222
+ return false;
223
+ }
224
+ return items.length > 0;
225
+ }
226
+ /**
227
+ * Recursively collects literal values from union items.
228
+ * Returns null if any non-literal, non-union item is found (invalid union).
229
+ * Returns empty array when all items are valid but already seen (deduped).
230
+ */
231
+ function collectLiterals(items, seen) {
232
+ const result = [];
233
+ for (const item of items) if (item.type.kind === "" && item.type.value !== void 0) {
234
+ const val = String(item.type.value);
235
+ if (!seen.has(val)) {
236
+ seen.add(val);
237
+ result.push({
238
+ key: val,
239
+ label: val
240
+ });
241
+ }
242
+ } else if (item.type.kind === "union") {
243
+ const nested = collectLiterals(item.type.items, seen);
244
+ if (nested === null) return null;
245
+ result.push(...nested);
246
+ } else return null;
247
+ return result;
248
+ }
249
+ //#endregion
250
+ //#region src/value-help/extract-ref.ts
251
+ /**
252
+ * Synchronous probe. Returns `{ url, targetField }` iff:
253
+ * 1. the prop carries `@db.rel.FK`,
254
+ * 2. the prop has a `.ref`,
255
+ * 3. the ref's target metadata carries `@db.http.path`.
256
+ */
257
+ function extractValueHelp(prop) {
258
+ if (!prop.metadata.has("db.rel.FK")) return void 0;
259
+ if (!prop.ref) return void 0;
260
+ const target = prop.ref.type();
261
+ if (!target) return void 0;
262
+ const url = target.metadata.get(DB_HTTP_PATH);
263
+ if (!url) return void 0;
264
+ return {
265
+ url,
266
+ targetField: prop.ref.field
267
+ };
268
+ }
269
+ //#endregion
270
+ //#region src/form/create-form-def.ts
271
+ /** Known atscript primitive extension tags that map directly to field types. */
272
+ const UI_TAGS = new Set([
273
+ "action",
274
+ "paragraph",
275
+ "select",
276
+ "radio",
277
+ "checkbox"
278
+ ]);
279
+ /**
280
+ * Converts an ATScript annotated type into a FormDef.
281
+ *
282
+ * - **Object types** (`kind === 'object'`): produces an object root with nested fields.
283
+ * - **Non-object types** (primitive, array, union, etc.): produces a single leaf root field
284
+ * with `path: ''`.
285
+ */
286
+ function createFormDef(type) {
287
+ if (type.type.kind !== "object") {
288
+ const rootField = createFieldDef("", type);
289
+ return {
290
+ type,
291
+ rootField,
292
+ fields: [rootField],
293
+ flatMap: /* @__PURE__ */ new Map()
294
+ };
295
+ }
296
+ const objectType = type;
297
+ const flatMap = (0, _atscript_typescript_utils.flattenAnnotatedType)(objectType, { excludePhantomTypes: false });
298
+ const fields = [];
299
+ const structuredPrefixes = /* @__PURE__ */ new Set();
300
+ for (const [path, prop] of flatMap.entries()) {
301
+ if (path === "") continue;
302
+ if (isChildOfStructured(path, structuredPrefixes)) continue;
303
+ const originalProp = resolveOriginalProp(objectType, path) ?? prop;
304
+ const kind = originalProp.type.kind;
305
+ if (kind === "object") {
306
+ const hasLabel = getFieldMeta(originalProp, META_LABEL) !== void 0;
307
+ const hasComponent = getFieldMeta(originalProp, UI_FORM_COMPONENT) !== void 0;
308
+ if (!hasLabel && !hasComponent) continue;
309
+ }
310
+ if (kind === "array") {
311
+ if (originalProp.type.of.type.kind === "array" && !getFieldMeta(originalProp, "ui.form.component")) continue;
312
+ }
313
+ if (kind === "array" || kind === "object" || kind === "union" || kind === "tuple") structuredPrefixes.add(path + ".");
314
+ fields.push(createFieldDef(path, originalProp));
315
+ }
316
+ const orderMap = new Map(fields.map((f) => [f, getFieldMeta(f.prop, "ui.form.order") ?? Infinity]));
317
+ fields.sort((a, b) => orderMap.get(a) - orderMap.get(b));
318
+ const rootField = {
319
+ path: "",
320
+ prop: type,
321
+ type: "object",
322
+ phantom: false,
323
+ name: "",
324
+ allStatic: false
325
+ };
326
+ const def = {
327
+ type,
328
+ rootField,
329
+ fields,
330
+ flatMap
331
+ };
332
+ rootField.objectDef = def;
333
+ return def;
334
+ }
335
+ /** Creates a FormFieldDef from any ATScript annotated type. */
336
+ function createFieldDef(path, prop) {
337
+ const kind = prop.type.kind;
338
+ const name = path.slice(path.lastIndexOf(".") + 1);
339
+ const allStatic = !hasComputedAnnotations(prop);
340
+ const uiType = getFieldMeta(prop, "ui.form.type") ?? getFieldMeta(prop, "ui.type");
341
+ const base = {
342
+ path,
343
+ prop,
344
+ phantom: false,
345
+ name,
346
+ allStatic
347
+ };
348
+ const customType = uiType;
349
+ if (kind === "array") {
350
+ const arrayType = prop.type;
351
+ return {
352
+ ...base,
353
+ type: "array",
354
+ customType,
355
+ itemType: arrayType.of,
356
+ itemField: createFieldDef("", arrayType.of)
357
+ };
358
+ }
359
+ if (kind === "object") return {
360
+ ...base,
361
+ type: "object",
362
+ customType,
363
+ objectDef: createFormDef(prop)
364
+ };
365
+ if (kind === "union") {
366
+ if (isPureLiteralUnion(prop)) return {
367
+ ...base,
368
+ type: uiType ?? "select"
369
+ };
370
+ const unionVariants = buildUnionVariants(prop);
371
+ if (unionVariants.length > 1) return {
372
+ ...base,
373
+ type: "union",
374
+ customType,
375
+ unionVariants
376
+ };
377
+ const v = unionVariants[0];
378
+ if (v?.itemField) return {
379
+ ...v.itemField,
380
+ path,
381
+ name,
382
+ allStatic
383
+ };
384
+ if (v?.def) return {
385
+ ...base,
386
+ type: "object",
387
+ customType,
388
+ objectDef: v.def
389
+ };
390
+ }
391
+ if (kind === "tuple") {
392
+ const tupleType = prop.type;
393
+ return {
394
+ ...base,
395
+ type: "tuple",
396
+ customType,
397
+ itemFields: tupleType.items.map((item, i) => {
398
+ const field = createFieldDef(String(i), item);
399
+ field.name = "";
400
+ return field;
401
+ })
402
+ };
403
+ }
404
+ if (extractValueHelp(prop)) return {
405
+ ...base,
406
+ type: uiType ?? "ref"
407
+ };
408
+ const tags = kind === "" ? prop.type.tags : void 0;
409
+ let uiTag;
410
+ let isTimestamp = false;
411
+ if (tags) for (const t of tags) {
412
+ if (uiTag === void 0 && UI_TAGS.has(t)) uiTag = t;
413
+ if (t === "timestamp") isTimestamp = true;
414
+ }
415
+ const dt = kind === "" ? prop.type.designType : void 0;
416
+ let numericType;
417
+ if (kind === "") {
418
+ const hasCurrency = getFieldMeta(prop, "db.amount.currency") !== void 0 || getFieldMeta(prop, "db.amount.currency.ref") !== void 0;
419
+ const isDecimalDesign = dt === "decimal";
420
+ if (hasCurrency) numericType = "decimal";
421
+ else if (isDecimalDesign) numericType = "decimal";
422
+ else if (dt === "number") {
423
+ const hasUnit = getFieldMeta(prop, "db.unit") !== void 0 || getFieldMeta(prop, "db.unit.ref") !== void 0;
424
+ const hasPrefix = getFieldMeta(prop, "ui.form.prefix") !== void 0 || getFieldMeta(prop, "ui.form.prefix.ref") !== void 0 || getFieldMeta(prop, "ui.form.prefix.icon") !== void 0;
425
+ const hasSuffix = getFieldMeta(prop, "ui.form.suffix") !== void 0 || getFieldMeta(prop, "ui.form.suffix.ref") !== void 0 || getFieldMeta(prop, "ui.form.suffix.icon") !== void 0;
426
+ if (hasUnit || hasPrefix || hasSuffix) numericType = "number";
427
+ }
428
+ }
429
+ const tagType = isTimestamp ? "datetime" : void 0;
430
+ return {
431
+ ...base,
432
+ type: uiType ?? uiTag ?? numericType ?? tagType ?? (dt === "number" ? "number" : dt === "boolean" ? "checkbox" : "text"),
433
+ phantom: kind === "" && dt === "phantom"
434
+ };
435
+ }
436
+ /** Resolves the original annotated type from the type hierarchy by path. */
437
+ function resolveOriginalProp(type, path) {
438
+ const parts = path.split(".");
439
+ let current = type.type;
440
+ for (let i = 0; i < parts.length - 1; i++) {
441
+ const prop = current.props.get(parts[i]);
442
+ if (!prop || prop.type.kind !== "object") return void 0;
443
+ current = prop.type;
444
+ }
445
+ return current.props.get(parts[parts.length - 1]);
446
+ }
447
+ /** Check if a path is a child of any structured prefix (prefixes are pre-suffixed with "."). */
448
+ function isChildOfStructured(path, prefixes) {
449
+ for (const prefix of prefixes) if (path.startsWith(prefix)) return true;
450
+ return false;
451
+ }
452
+ /**
453
+ * Builds union variant definitions from a union annotated type.
454
+ * Iterates top-level items directly — one variant per item.
455
+ */
456
+ function buildUnionVariants(typeDef) {
457
+ const items = typeDef.type.items ?? [typeDef];
458
+ const variants = [];
459
+ for (const item of items) {
460
+ const v = createVariant(item);
461
+ if (items.length > 1) v.label = `${String(variants.length + 1)}. ${v.label}`;
462
+ variants.push(v);
463
+ }
464
+ return variants;
465
+ }
466
+ /** Creates a single union variant from an annotated type item. */
467
+ function createVariant(def) {
468
+ const kind = def.type.kind;
469
+ if (kind === "object") {
470
+ const label = getFieldMeta(def, "meta.label") ?? "Object";
471
+ const hasComponent = getFieldMeta(def, UI_FORM_COMPONENT) !== void 0;
472
+ return {
473
+ label,
474
+ type: def,
475
+ def: createFormDef(def),
476
+ itemField: hasComponent ? createFieldDef("", def) : void 0
477
+ };
478
+ }
479
+ if (kind === "") {
480
+ const dt = def.type.designType;
481
+ return {
482
+ label: capitalize(dt === "phantom" ? "item" : dt),
483
+ type: def,
484
+ itemField: createFieldDef("", def),
485
+ designType: dt
486
+ };
487
+ }
488
+ return {
489
+ label: capitalize(kind),
490
+ type: def,
491
+ itemField: createFieldDef("", def)
492
+ };
493
+ }
494
+ function capitalize(s) {
495
+ return s.charAt(0).toUpperCase() + s.slice(1);
496
+ }
497
+ //#endregion
498
+ //#region src/value-help/dict-paths.ts
499
+ /**
500
+ * Paths that make up the "dict view" of a value-help target:
501
+ * PKs + label + descr + attr fields. Used by filter dialogs to clamp
502
+ * visible columns to the dictionary subset.
503
+ */
504
+ function valueHelpDictPaths(resolved) {
505
+ const paths = [
506
+ ...resolved.primaryKeys,
507
+ resolved.labelField,
508
+ ...resolved.attrFields
509
+ ];
510
+ if (resolved.descrField) paths.push(resolved.descrField);
511
+ return new Set(paths);
512
+ }
513
+ //#endregion
514
+ //#region src/value-help/resolve-options.ts
515
+ /** Extracts the key from an option entry. */
516
+ function optKey(opt) {
517
+ return typeof opt === "string" ? opt : opt.key;
518
+ }
519
+ /** Extracts the display label from an option entry. */
520
+ function optLabel(opt) {
521
+ return typeof opt === "string" ? opt : opt.label;
522
+ }
523
+ /**
524
+ * Converts raw option annotation value to a normalized array.
525
+ */
526
+ function parseStaticOptions(raw) {
527
+ return asArray(raw).map((item) => {
528
+ if (typeof item === "object" && item !== null && "label" in item) {
529
+ const { label, value } = item;
530
+ return value !== void 0 ? {
531
+ key: value,
532
+ label
533
+ } : label;
534
+ }
535
+ return String(item);
536
+ });
537
+ }
538
+ /**
539
+ * Resolves options from metadata with a fallback chain:
540
+ * 1. `@ui.form.fn.options` (dynamic, compiled by ui-fns)
541
+ * 2. `@ui.form.options` (static annotation)
542
+ * 3. Literal union type extraction (auto-derived from type)
543
+ * 4. Future: dictionary / value-help lookup
544
+ */
545
+ function resolveOptions(prop, scope) {
546
+ const resolved = resolveFieldProp(prop, UI_FORM_FN_OPTIONS, UI_FORM_OPTIONS, scope, { transform: parseStaticOptions });
547
+ if (resolved !== void 0) return resolved;
548
+ return extractLiteralOptions(prop);
549
+ }
550
+ //#endregion
551
+ //#region src/value-help/value-help-client.ts
552
+ /**
553
+ * Value-help query client. Wraps a `Client` from `@atscript/db-client`
554
+ * with FK-specific search logic (regex fallback for non-searchable tables,
555
+ * $select scoping).
556
+ *
557
+ * Consumers resolve the target's metadata once via `resolveValueHelp(url)`
558
+ * and pass the resulting `ResolvedValueHelp` to `search()`. Label resolution
559
+ * for cells is deliberately unsupported — cells always display raw ids.
560
+ */
561
+ var ValueHelpClient = class {
562
+ _client;
563
+ constructor(client) {
564
+ this._client = client;
565
+ }
566
+ /**
567
+ * Search the target with value-help semantics.
568
+ *
569
+ * - If target is searchable → sends `$search` (server full-text)
570
+ * - If not searchable → sends `$or` regex across select fields + exact PK match
571
+ */
572
+ async search(resolved, opts) {
573
+ const mode = opts?.mode ?? "form";
574
+ const limit = opts?.limit ?? 20;
575
+ const selectFields = opts?.select ?? computeSelectFields(resolved, mode);
576
+ const text = opts?.text;
577
+ if (!text) return { items: await this._client.query({ controls: {
578
+ $select: selectFields,
579
+ $limit: limit
580
+ } }) };
581
+ if (resolved.searchable) return { items: await this._client.query({ controls: {
582
+ $select: selectFields,
583
+ $limit: limit,
584
+ $search: text
585
+ } }) };
586
+ const filter = buildOrFilter(text, selectFields, resolved.primaryKeys[0] ?? resolved.labelField);
587
+ return { items: await this._client.query({
588
+ filter,
589
+ controls: {
590
+ $select: selectFields,
591
+ $limit: limit
592
+ }
593
+ }) };
594
+ }
595
+ };
596
+ /**
597
+ * Compute the $select fields for a value-help query.
598
+ */
599
+ function computeSelectFields(resolved, mode) {
600
+ const fields = [...resolved.primaryKeys, resolved.labelField];
601
+ if (resolved.descrField) fields.push(resolved.descrField);
602
+ if (mode === "filter") fields.push(...resolved.attrFields);
603
+ return [...new Set(fields)];
604
+ }
605
+ /**
606
+ * Build a Uniquery `$or` filter for value-help search across multiple fields.
607
+ * Uses regex startsWith (case-insensitive) on text fields + exact match on PK.
608
+ */
609
+ function buildOrFilter(text, fields, pkField) {
610
+ const escaped = text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
611
+ const conditions = [];
612
+ for (const field of fields) if (field !== pkField) conditions.push({ [field]: { $regex: `/^${escaped}/i` } });
613
+ const asNum = Number(text);
614
+ if (!Number.isNaN(asNum)) conditions.push({ [pkField]: asNum });
615
+ else conditions.push({ [pkField]: text });
616
+ return { $or: conditions };
617
+ }
618
+ //#endregion
619
+ //#region src/client-factory.ts
620
+ const builtin = (url) => new _atscript_db_client.Client(url);
621
+ let _default = builtin;
622
+ /**
623
+ * Override the app-wide default factory. Call once at startup (e.g. in
624
+ * `entry-client.ts`) to wire shared fetch, credentials, error handling, etc.
625
+ * Every table, value-help picker, and other client consumer will pick it up.
626
+ */
627
+ function setDefaultClientFactory(factory) {
628
+ _default = factory;
629
+ }
630
+ /** Current app-wide default factory. Falls back to `new Client(url)`. */
631
+ function getDefaultClientFactory() {
632
+ return _default;
633
+ }
634
+ /** Reset the default factory to the built-in one (primarily for tests). */
635
+ function resetDefaultClientFactory() {
636
+ _default = builtin;
637
+ }
638
+ //#endregion
639
+ //#region src/shared/meta-cache.ts
640
+ const cache = /* @__PURE__ */ new Map();
641
+ /**
642
+ * Get or create the cache entry for `url`. First caller's `factory` seeds the
643
+ * `Client`; subsequent callers reuse it. On `meta` rejection, the entry is
644
+ * evicted so the next call retries.
645
+ */
646
+ function getMetaEntry(url, factory) {
647
+ const existing = cache.get(url);
648
+ if (existing) return existing;
649
+ const client = (factory ?? getDefaultClientFactory())(url);
650
+ const meta = client.meta().catch((err) => {
651
+ cache.delete(url);
652
+ throw err;
653
+ });
654
+ const entry = {
655
+ client,
656
+ meta,
657
+ type: meta.then((m) => (0, _atscript_typescript_utils.deserializeAnnotatedType)(m.type))
658
+ };
659
+ cache.set(url, entry);
660
+ return entry;
661
+ }
662
+ function resetMetaCache() {
663
+ cache.clear();
664
+ }
665
+ //#endregion
666
+ //#region src/value-help/resolve.ts
667
+ /**
668
+ * Lazily fetch and extract value-help metadata for the given URL.
669
+ *
670
+ * - Issues exactly one `GET {url}/meta` per URL across the session (shared via meta-cache).
671
+ * - Concurrent callers with the same URL share the in-flight promise.
672
+ * - If the underlying fetch rejects, the cache entry is evicted so a later
673
+ * retry performs a fresh fetch.
674
+ */
675
+ function resolveValueHelp(url) {
676
+ const entry = getMetaEntry(url);
677
+ if (entry.resolved) return entry.resolved;
678
+ entry.resolved = Promise.all([entry.meta, entry.type]).then(([meta, type]) => buildResolved(url, meta, type));
679
+ return entry.resolved;
680
+ }
681
+ /** Thin alias over `resetMetaCache` — retained so existing test code keeps working. */
682
+ function resetValueHelpCache() {
683
+ resetMetaCache();
684
+ }
685
+ function buildResolved(url, meta, targetType) {
686
+ const objectType = targetType.type.kind === "object" ? targetType.type : void 0;
687
+ const primaryKeys = [];
688
+ let labelField;
689
+ let descrField;
690
+ const attrFields = [];
691
+ let firstStringField;
692
+ if (objectType) for (const [name, fieldProp] of objectType.props) {
693
+ const isPK = fieldProp.metadata.has(META_ID);
694
+ if (isPK) primaryKeys.push(name);
695
+ if (fieldProp.metadata.has("ui.dict.label")) labelField = name;
696
+ if (fieldProp.metadata.has("ui.dict.descr")) descrField = name;
697
+ if (fieldProp.metadata.has("ui.dict.attr")) attrFields.push(name);
698
+ if (!firstStringField && !isPK && fieldProp.type.kind === "" && fieldProp.type.designType === "string") firstStringField = name;
699
+ }
700
+ if (!labelField) labelField = firstStringField;
701
+ if (!labelField) labelField = primaryKeys[0] ?? "id";
702
+ const filterableFields = [];
703
+ const sortableFields = [];
704
+ if (meta.fields) for (const [name, fm] of Object.entries(meta.fields)) {
705
+ if (fm?.filterable) filterableFields.push(name);
706
+ if (fm?.sortable) sortableFields.push(name);
707
+ }
708
+ return {
709
+ url,
710
+ primaryKeys,
711
+ labelField,
712
+ descrField,
713
+ attrFields,
714
+ filterableFields,
715
+ sortableFields,
716
+ searchable: !!meta.searchable,
717
+ targetType
718
+ };
719
+ }
720
+ //#endregion
721
+ //#region src/form/path-utils.ts
722
+ /**
723
+ * Gets a nested value by dot-separated path.
724
+ * Always dereferences `obj.value` first (form data is wrapped in `{ value: domainData }`).
725
+ * When `path` is empty, returns the root domain data (`obj.value`).
726
+ */
727
+ function getByPath(obj, path) {
728
+ const root = obj.value;
729
+ if (!path) return root;
730
+ const keys = path.split(".");
731
+ let current = root;
732
+ for (const key of keys) {
733
+ if (current === null || current === void 0 || typeof current !== "object") return void 0;
734
+ current = current[key];
735
+ }
736
+ return current;
737
+ }
738
+ /**
739
+ * Sets a nested value by dot-separated path.
740
+ * Always dereferences `obj.value` first (form data is wrapped in `{ value: domainData }`).
741
+ * When `path` is empty, sets the root domain data (`obj.value = value`).
742
+ * Creates intermediate objects if they do not exist.
743
+ */
744
+ function setByPath(obj, path, value) {
745
+ if (!path) {
746
+ obj.value = value;
747
+ return;
748
+ }
749
+ const keys = path.split(".");
750
+ const last = keys.pop();
751
+ if (last === void 0) return;
752
+ let current = obj.value;
753
+ for (const key of keys) {
754
+ if (current[key] === null || current[key] === void 0 || typeof current[key] !== "object") current[key] = {};
755
+ current = current[key];
756
+ }
757
+ current[last] = value;
758
+ }
759
+ function parseStaticDefault(raw, prop) {
760
+ if (typeof raw !== "string") return raw;
761
+ if (prop.type.kind === "" && prop.type.designType === "string") return raw;
762
+ try {
763
+ return JSON.parse(raw);
764
+ } catch {
765
+ return;
766
+ }
767
+ }
768
+ /** Cached default resolver — reused when no resolver is provided. */
769
+ const defaultValueResolver = createFormValueResolver();
770
+ function createFormValueResolver(data = {}, context = {}) {
771
+ return (prop, _path) => {
772
+ return resolveFieldProp(prop, UI_FORM_FN_VALUE, META_DEFAULT, {
773
+ v: void 0,
774
+ data,
775
+ context,
776
+ entry: void 0
777
+ }, { transform: (raw) => parseStaticDefault(raw, prop) });
778
+ };
779
+ }
780
+ /**
781
+ * Type-appropriate "defined-but-empty" fallback for primitive design types
782
+ * whose structural default in atscript's `finalDefault` table is
783
+ * `undefined`. Without this, `createFormData` on an optional `decimal`
784
+ * field (or any other primitive missed by atscript's table) returns
785
+ * `undefined` — but `createFormData` is called from "explicit add"
786
+ * contexts (optional-toggle, array-add, tuple-pad, union-pick) where the
787
+ * caller's intent is "give me a value the renderer can edit", not "no
788
+ * value yet". Returning `undefined` leaves the empty-state placeholder
789
+ * stuck in AsFieldShell.
790
+ *
791
+ * `decimal → "0"` mirrors the `number → 0` default — the atscript runtime
792
+ * validator (≥ 0.1.54) rejects `""` for decimal fields, so committing the
793
+ * canonical zero is the only init that survives a submit-without-edit.
794
+ * `useAsDecimal` pads the display to the field's effective scale, so the
795
+ * user sees `0.00` / `0.000` per the `@db.column.precision` annotation.
796
+ */
797
+ function primitiveInitFallback(prop) {
798
+ if (prop.type.kind !== "") return void 0;
799
+ switch (prop.type.designType) {
800
+ case "decimal": return "0";
801
+ default: return;
802
+ }
803
+ }
804
+ function createFormData(type, resolver) {
805
+ let value = (0, _atscript_typescript_utils.createDataFromAnnotatedType)(type, { mode: resolver ?? defaultValueResolver });
806
+ if (value === void 0) value = primitiveInitFallback(type);
807
+ return { value };
808
+ }
809
+ const variantValidatorCache = /* @__PURE__ */ new WeakMap();
810
+ function getVariantValidator(variant) {
811
+ let v = variantValidatorCache.get(variant.type);
812
+ if (!v) {
813
+ v = variant.type.validator();
814
+ variantValidatorCache.set(variant.type, v);
815
+ }
816
+ return v;
817
+ }
818
+ const variantsDiscriminatorCache = /* @__PURE__ */ new WeakMap();
819
+ function getVariantsDiscriminator(variants) {
820
+ let cached = variantsDiscriminatorCache.get(variants);
821
+ if (cached === void 0) {
822
+ cached = (0, _atscript_typescript_utils.detectDiscriminator)(variants.map((v) => v.type));
823
+ variantsDiscriminatorCache.set(variants, cached);
824
+ }
825
+ return cached;
826
+ }
827
+ function detectUnionVariant(value, variants) {
828
+ if (variants.length <= 1) return 0;
829
+ const disc = getVariantsDiscriminator(variants);
830
+ if (disc && value !== null && typeof value === "object") {
831
+ const tag = value[disc.propertyName];
832
+ const idx = disc.indexMapping[String(tag)];
833
+ if (idx !== void 0) return idx;
834
+ }
835
+ for (let i = 0; i < variants.length; i++) try {
836
+ if (getVariantValidator(variants[i]).validate(value, true)) return i;
837
+ } catch {}
838
+ return 0;
839
+ }
840
+ //#endregion
841
+ //#region src/form/validate.ts
842
+ let defaultValidatorPlugins = [];
843
+ /** Replace the default validator plugins applied to every form/field validator. */
844
+ function setDefaultValidatorPlugins(plugins) {
845
+ defaultValidatorPlugins = plugins;
846
+ }
847
+ /** Get the currently registered default validator plugins. */
848
+ function getDefaultValidatorPlugins() {
849
+ return defaultValidatorPlugins;
850
+ }
851
+ /**
852
+ * Returns a reusable validator function for a whole FormDef.
853
+ *
854
+ * Validator is created once and reused on every call.
855
+ * ATScript's @expect.* validation runs automatically.
856
+ * For custom `ui.fn.*` validators, install ui-fns and pass its plugin via `opts.plugins`.
857
+ */
858
+ function getFormValidator(def, opts) {
859
+ const validator = new _atscript_typescript_utils.Validator(def.type, {
860
+ unknownProps: "ignore",
861
+ ...opts,
862
+ plugins: [...defaultValidatorPlugins, ...opts?.plugins ?? []]
863
+ });
864
+ return (callOpts) => {
865
+ if (validator.validate(callOpts.data, true, {
866
+ data: callOpts.data,
867
+ context: callOpts.context ?? {}
868
+ })) return {};
869
+ const errors = {};
870
+ for (const err of validator.errors) errors[err.path] = err.message;
871
+ return errors;
872
+ };
873
+ }
874
+ /**
875
+ * Creates a cached validator function for a single ATScript prop.
876
+ *
877
+ * The `Validator` instance is created lazily on first call and reused.
878
+ * Returns `true` when valid, or the first error message string when invalid.
879
+ */
880
+ function createFieldValidator(prop, opts) {
881
+ let cached;
882
+ return (value, externalCtx) => {
883
+ cached ??= new _atscript_typescript_utils.Validator(prop, { plugins: defaultValidatorPlugins });
884
+ if (!cached.validate(value, true, externalCtx)) {
885
+ if (opts?.rootOnly) {
886
+ const rootError = cached.errors?.find((e) => e.path === "");
887
+ if (rootError) return rootError.message;
888
+ return true;
889
+ }
890
+ return cached.errors?.[0]?.message || "Invalid value";
891
+ }
892
+ return true;
893
+ };
894
+ }
895
+ //#endregion
896
+ //#region src/form/error-utils.ts
897
+ /**
898
+ * Framework-agnostic helpers for working with form-error maps keyed by
899
+ * dotted path. Used by AsForm to drive error badges and auto-open
900
+ * collapsed sections; safe to share with React (or any other) bindings.
901
+ *
902
+ * Convention:
903
+ * - Keys are dotted paths (`a.b.c`); empty string and `__form` denote
904
+ * the form-level error.
905
+ * - Values may be `string | undefined`; falsy entries are dropped on
906
+ * merge.
907
+ */
908
+ const FORM_ERROR_KEY = "__form";
909
+ /**
910
+ * Merge any number of partial error maps into a single dense
911
+ * `Record<path, message>`. Falsy values are skipped — later sources do
912
+ * NOT overwrite earlier ones with an empty value.
913
+ */
914
+ function mergeErrorMaps(...maps) {
915
+ const merged = {};
916
+ for (const m of maps) {
917
+ if (!m) continue;
918
+ for (const k of Object.keys(m)) {
919
+ const v = m[k];
920
+ if (v) merged[k] = v;
921
+ }
922
+ }
923
+ return merged;
924
+ }
925
+ /**
926
+ * Yield every ancestor prefix of a dotted path, longest-first
927
+ * (`a.b.c` → `a.b.c`, `a.b`, `a`). Returns the path itself first so
928
+ * callers can include it in the iteration without a special case.
929
+ *
930
+ * Empty paths and the form-level key (`__form`) yield nothing.
931
+ */
932
+ function* iteratePathAncestors(path) {
933
+ if (!path || path === FORM_ERROR_KEY) return;
934
+ let pos = path.length;
935
+ while (pos > 0) {
936
+ yield path.slice(0, pos);
937
+ const dot = path.lastIndexOf(".", pos - 1);
938
+ if (dot < 0) return;
939
+ pos = dot;
940
+ }
941
+ }
942
+ /**
943
+ * Build an indexed `Map<absolutePath, descendantErrorCount>` so each
944
+ * struct in the tree can render an error-count badge in O(1).
945
+ *
946
+ * For every error path, the count is incremented on the path itself
947
+ * AND every dotted-path ancestor — so a struct at `a.b` reports the
948
+ * total of all errors at `a.b` or below.
949
+ */
950
+ function buildDescendantErrorCounts(errors) {
951
+ const map = /* @__PURE__ */ new Map();
952
+ for (const errPath of Object.keys(errors)) {
953
+ if (!errors[errPath]) continue;
954
+ for (const prefix of iteratePathAncestors(errPath)) map.set(prefix, (map.get(prefix) ?? 0) + 1);
955
+ }
956
+ return map;
957
+ }
958
+ //#endregion
959
+ //#region src/form/grid.ts
960
+ /** Grid layout parsing for `@ui.form.grid.colSpan` / `@ui.form.grid.rowSpan`. */
961
+ const DEFAULT_COL_SPAN = 12;
962
+ const DEFAULT_ROW_SPAN = 1;
963
+ const COL_SPAN_ALIASES = {
964
+ full: 12,
965
+ half: 6,
966
+ third: 4
967
+ };
968
+ /** Accepts "1"-"12" and the aliases "full" (12), "half" (6), "third" (4). */
969
+ function parseColSpan(raw) {
970
+ if (!raw) return void 0;
971
+ const aliased = COL_SPAN_ALIASES[raw];
972
+ if (aliased !== void 0) return aliased;
973
+ const n = Number.parseInt(raw, 10);
974
+ if (Number.isFinite(n) && n >= 1 && n <= 12 && String(n) === raw) return n;
975
+ }
976
+ /** Accepts numeric strings "1"+; rejects "0", negatives, decimals, aliases. */
977
+ function parseRowSpan(raw) {
978
+ if (!raw) return void 0;
979
+ const n = Number.parseInt(raw, 10);
980
+ if (Number.isFinite(n) && n >= 1 && String(n) === raw) return n;
981
+ }
982
+ /**
983
+ * Resolve a field's grid footprint. Narrow defaults to full-width / single-row
984
+ * regardless of the desktop value, so authors can opt into a narrow override
985
+ * via the second annotation arg without it inheriting an unintended desktop span.
986
+ */
987
+ function resolveGridSpec(colSpan, rowSpan) {
988
+ return {
989
+ col: {
990
+ desktop: parseColSpan(colSpan?.desktop) ?? 12,
991
+ narrow: parseColSpan(colSpan?.narrow) ?? 12
992
+ },
993
+ row: {
994
+ desktop: parseRowSpan(rowSpan?.desktop) ?? 1,
995
+ narrow: parseRowSpan(rowSpan?.narrow) ?? 1
996
+ }
997
+ };
998
+ }
999
+ /**
1000
+ * Build the UnoCSS class string for a field's grid footprint.
1001
+ *
1002
+ * - Skips desktop classes that match the default (`as-grid-item` already
1003
+ * covers `col-span-full row-span-1`).
1004
+ * - Skips narrow overrides that match the desktop value (no override needed).
1005
+ * - The narrow variant uses the custom `as-narrow:` prefix, which the
1006
+ * atscript-ui UnoCSS preset rewrites to `@container as-grid (max-width:
1007
+ * 480px) { ... }`. The parent grid is registered as
1008
+ * `container-name: as-grid` via the `as-form-grid` shortcut, so the
1009
+ * rule resolves against the actual grid's inline size — not the viewport.
1010
+ *
1011
+ * Returned string is space-separated, ready to drop into a Vue class binding.
1012
+ */
1013
+ function buildGridClasses(spec) {
1014
+ const out = [];
1015
+ if (spec.col.desktop !== 12) out.push(`col-span-${spec.col.desktop}`);
1016
+ if (spec.row.desktop !== 1) out.push(`row-span-${spec.row.desktop}`);
1017
+ if (spec.col.narrow !== spec.col.desktop) out.push(`as-narrow:col-span-${spec.col.narrow}`);
1018
+ if (spec.row.narrow !== spec.row.desktop) out.push(`as-narrow:row-span-${spec.row.narrow}`);
1019
+ return out.join(" ");
1020
+ }
1021
+ //#endregion
1022
+ //#region src/form/labels.ts
1023
+ /** Singular label for an array field (used by AsArray for "Add <singular>"). */
1024
+ function resolveSingularLabel(meta) {
1025
+ if (!meta) return "item";
1026
+ return getFieldMeta(meta, "ui.form.label.singular") || "item";
1027
+ }
1028
+ //#endregion
1029
+ //#region src/form/measurement.ts
1030
+ /**
1031
+ * Read measurement annotations off a single field prop. Returns `undefined`
1032
+ * for any annotation that's absent — callers can spread the result into a
1033
+ * larger record.
1034
+ */
1035
+ function extractMeasurement(prop) {
1036
+ const precisionMeta = getFieldMeta(prop, DB_COLUMN_PRECISION);
1037
+ return {
1038
+ currencyCode: getFieldMeta(prop, DB_AMOUNT_CURRENCY),
1039
+ currencyRefField: getFieldMeta(prop, DB_AMOUNT_CURRENCY_REF),
1040
+ unitCode: getFieldMeta(prop, DB_UNIT),
1041
+ unitRefField: getFieldMeta(prop, DB_UNIT_REF),
1042
+ precisionScale: precisionMeta?.scale
1043
+ };
1044
+ }
1045
+ //#endregion
1046
+ //#region src/form/decimal-format.ts
1047
+ const WHITESPACE_GROUP_RE = /[   ]/g;
1048
+ const numberFormatCache = /* @__PURE__ */ new Map();
1049
+ function getNumberFormat(locale, opts) {
1050
+ const key = `${locale ?? ""}|${opts.style ?? ""}|${opts.currency ?? ""}|${opts.currencyDisplay ?? ""}|${opts.useGrouping ?? ""}|${opts.minimumFractionDigits ?? ""}|${opts.maximumFractionDigits ?? ""}`;
1051
+ let f = numberFormatCache.get(key);
1052
+ if (!f) {
1053
+ f = new Intl.NumberFormat(locale, opts);
1054
+ numberFormatCache.set(key, f);
1055
+ }
1056
+ return f;
1057
+ }
1058
+ const decimalSeparatorCache = /* @__PURE__ */ new Map();
1059
+ const thousandsSeparatorCache = /* @__PURE__ */ new Map();
1060
+ const currencyDisplayCache = /* @__PURE__ */ new Map();
1061
+ const currencyDecimalsCache = /* @__PURE__ */ new Map();
1062
+ /** "." in en-US, "," in fr-FR. Returns "." when locale is undefined. */
1063
+ function getDecimalSeparator(locale) {
1064
+ const key = locale ?? "";
1065
+ const cached = decimalSeparatorCache.get(key);
1066
+ if (cached !== void 0) return cached;
1067
+ let out = ".";
1068
+ try {
1069
+ out = getNumberFormat(locale, { useGrouping: false }).formatToParts(1.1).find((p) => p.type === "decimal")?.value ?? ".";
1070
+ } catch {}
1071
+ decimalSeparatorCache.set(key, out);
1072
+ return out;
1073
+ }
1074
+ /**
1075
+ * "," in en-US, NNBSP (U+202F) in fr-FR. Returns "" when no grouping is
1076
+ * applied (or when Intl rejects the locale).
1077
+ */
1078
+ function getThousandsSeparator(locale) {
1079
+ const key = locale ?? "";
1080
+ const cached = thousandsSeparatorCache.get(key);
1081
+ if (cached !== void 0) return cached;
1082
+ let out = "";
1083
+ try {
1084
+ out = getNumberFormat(locale, { useGrouping: true }).formatToParts(1e6).find((p) => p.type === "group")?.value ?? "";
1085
+ } catch {}
1086
+ thousandsSeparatorCache.set(key, out);
1087
+ return out;
1088
+ }
1089
+ function getCurrencyDisplayParts(code, locale) {
1090
+ const key = `${locale ?? ""}|${code}`;
1091
+ const cached = currencyDisplayCache.get(key);
1092
+ if (cached) return cached;
1093
+ let out = {
1094
+ symbol: code,
1095
+ position: "prefix"
1096
+ };
1097
+ try {
1098
+ const parts = getNumberFormat(locale, {
1099
+ style: "currency",
1100
+ currency: code,
1101
+ currencyDisplay: "narrowSymbol"
1102
+ }).formatToParts(0);
1103
+ const idxCur = parts.findIndex((p) => p.type === "currency");
1104
+ const idxInt = parts.findIndex((p) => p.type === "integer");
1105
+ out = {
1106
+ symbol: parts[idxCur]?.value ?? code,
1107
+ position: idxInt === -1 || idxCur < idxInt ? "prefix" : "suffix"
1108
+ };
1109
+ } catch {}
1110
+ currencyDisplayCache.set(key, out);
1111
+ return out;
1112
+ }
1113
+ /**
1114
+ * Currency's natural decimal count via Intl. JPY=0, USD/EUR=2, BHD/KWD=3.
1115
+ * Returns `undefined` for codes Intl doesn't know — caller falls back to
1116
+ * `dbPrecisionScale`.
1117
+ */
1118
+ function getCurrencyDecimals(code, locale) {
1119
+ if (!code) return void 0;
1120
+ const key = `${locale ?? ""}|${code}`;
1121
+ if (currencyDecimalsCache.has(key)) return currencyDecimalsCache.get(key);
1122
+ let out;
1123
+ try {
1124
+ const max = getNumberFormat(locale, {
1125
+ style: "currency",
1126
+ currency: code
1127
+ }).resolvedOptions().maximumFractionDigits;
1128
+ out = typeof max === "number" ? max : void 0;
1129
+ } catch {
1130
+ out = void 0;
1131
+ }
1132
+ currencyDecimalsCache.set(key, out);
1133
+ return out;
1134
+ }
1135
+ /**
1136
+ * Parse a user-typed decimal string. Returns the canonical decimal
1137
+ * (no thousands separator, "." as decimal separator) or `null` if invalid.
1138
+ * Accepts both "." and "," as the decimal separator (locale-aware).
1139
+ * Strips the locale thousands separator. Preserves sign. Does NOT
1140
+ * enforce scale.
1141
+ */
1142
+ function parseDecimalInput(raw, locale) {
1143
+ if (typeof raw !== "string") return null;
1144
+ const trimmed = raw.trim();
1145
+ if (trimmed === "") return null;
1146
+ const decSep = getDecimalSeparator(locale);
1147
+ const thouSep = getThousandsSeparator(locale);
1148
+ let sign = "";
1149
+ let body = trimmed;
1150
+ if (body.startsWith("-")) {
1151
+ sign = "-";
1152
+ body = body.slice(1);
1153
+ } else if (body.startsWith("+")) body = body.slice(1);
1154
+ if (body.length === 0) return null;
1155
+ if (body.includes("-") || body.includes("+")) return null;
1156
+ if (thouSep && body.includes(thouSep)) {
1157
+ const decIdx = body.indexOf(decSep);
1158
+ const headRaw = decIdx === -1 ? body : body.slice(0, decIdx);
1159
+ const tail = decIdx === -1 ? "" : body.slice(decIdx);
1160
+ if (tail.includes(thouSep)) return null;
1161
+ if (headRaw.includes(thouSep)) {
1162
+ const groups = headRaw.split(thouSep);
1163
+ const first = groups[0];
1164
+ if (first.length < 1 || first.length > 3 || !/^\d+$/.test(first)) return null;
1165
+ for (let i = 1; i < groups.length; i += 1) if (!/^\d{3}$/.test(groups[i])) return null;
1166
+ }
1167
+ body = `${headRaw.split(thouSep).join("")}${tail}`;
1168
+ }
1169
+ body = body.replace(WHITESPACE_GROUP_RE, "");
1170
+ if (decSep !== ".") {
1171
+ if ((body.match(/,/g)?.length ?? 0) > 1) return null;
1172
+ if (body.includes(".") && body.includes(",")) return null;
1173
+ if (body.includes(",")) body = body.replace(",", ".");
1174
+ } else if (body.includes(",")) {
1175
+ if (body.includes(".")) return null;
1176
+ if ((body.match(/,/g)?.length ?? 0) > 1) return null;
1177
+ body = body.replace(",", ".");
1178
+ }
1179
+ if (!/^\d*\.?\d*$/.test(body)) return null;
1180
+ if (body === "" || body === ".") return null;
1181
+ if (body.endsWith(".")) body = body.slice(0, -1);
1182
+ if (body.startsWith(".")) body = `0${body}`;
1183
+ const dot = body.indexOf(".");
1184
+ if (dot === -1) body = body.replace(/^0+(\d)/, "$1");
1185
+ else {
1186
+ const head = body.slice(0, dot);
1187
+ const tail = body.slice(dot);
1188
+ body = `${head.replace(/^0+(\d)/, "$1")}${tail}`;
1189
+ }
1190
+ if (sign === "-" && /^0+(\.0+)?$/.test(body)) sign = "";
1191
+ return `${sign}${body}`;
1192
+ }
1193
+ /**
1194
+ * Enforce a fractional-digit count by truncating or padding. String-only —
1195
+ * no float arithmetic. Default behaviour truncates (no rounding) so digits
1196
+ * the user typed can't silently shift. Pass `roundHalfUp: true` to round.
1197
+ *
1198
+ * enforceScale("12.345", 2) → "12.34" (truncate)
1199
+ * enforceScale("12.3", 4) → "12.3000" (pad)
1200
+ * enforceScale("12", 0) → "12"
1201
+ * enforceScale("12.99", 0) → "12" (no rounding)
1202
+ */
1203
+ function enforceScale(s, scale, opts) {
1204
+ if (typeof s !== "string" || s === "") return s;
1205
+ if (scale === void 0 || scale < 0) return s;
1206
+ const parts = splitDecimalString(s);
1207
+ if (scale === 0) {
1208
+ if (opts?.roundHalfUp && parts.decimal.length > 0 && parts.decimal[0] >= "5") {
1209
+ const bumped = addOne(parts.integer);
1210
+ return joinDecimalString({
1211
+ sign: parts.sign,
1212
+ integer: bumped,
1213
+ decimal: ""
1214
+ });
1215
+ }
1216
+ return joinDecimalString({
1217
+ sign: parts.sign,
1218
+ integer: parts.integer,
1219
+ decimal: ""
1220
+ });
1221
+ }
1222
+ if (parts.decimal.length === scale) return joinDecimalString(parts);
1223
+ if (parts.decimal.length < scale) return joinDecimalString({
1224
+ sign: parts.sign,
1225
+ integer: parts.integer,
1226
+ decimal: parts.decimal.padEnd(scale, "0")
1227
+ });
1228
+ let head = parts.decimal.slice(0, scale);
1229
+ const dropped = parts.decimal.slice(scale);
1230
+ let integer = parts.integer;
1231
+ if (opts?.roundHalfUp && dropped.length > 0 && dropped[0] >= "5") {
1232
+ const bumpedFrac = addOne(head);
1233
+ if (bumpedFrac.length > head.length) {
1234
+ integer = addOne(integer);
1235
+ head = "0".repeat(scale);
1236
+ } else head = bumpedFrac;
1237
+ }
1238
+ return joinDecimalString({
1239
+ sign: parts.sign,
1240
+ integer,
1241
+ decimal: head
1242
+ });
1243
+ }
1244
+ function addOne(digits) {
1245
+ if (digits === "") return "1";
1246
+ const out = digits.split("");
1247
+ let i = out.length - 1;
1248
+ let carry = 1;
1249
+ while (i >= 0 && carry > 0) {
1250
+ const d = out[i].charCodeAt(0) - 48 + carry | 0;
1251
+ if (d >= 10) {
1252
+ out[i] = "0";
1253
+ carry = 1;
1254
+ } else {
1255
+ out[i] = String.fromCharCode(d + 48);
1256
+ carry = 0;
1257
+ }
1258
+ i -= 1;
1259
+ }
1260
+ if (carry > 0) out.unshift("1");
1261
+ return out.join("");
1262
+ }
1263
+ function splitDecimalString(s) {
1264
+ if (typeof s !== "string" || s === "") return {
1265
+ sign: "",
1266
+ integer: "0",
1267
+ decimal: ""
1268
+ };
1269
+ let sign = "";
1270
+ let body = s;
1271
+ if (body.startsWith("-")) {
1272
+ sign = "-";
1273
+ body = body.slice(1);
1274
+ } else if (body.startsWith("+")) body = body.slice(1);
1275
+ if (body === "") return {
1276
+ sign: "",
1277
+ integer: "0",
1278
+ decimal: ""
1279
+ };
1280
+ const dot = body.indexOf(".");
1281
+ let integer;
1282
+ let decimal;
1283
+ if (dot === -1) {
1284
+ integer = body;
1285
+ decimal = "";
1286
+ } else {
1287
+ integer = body.slice(0, dot);
1288
+ decimal = body.slice(dot + 1);
1289
+ }
1290
+ if (integer === "") integer = "0";
1291
+ integer = integer.replace(/^0+(\d)/, "$1");
1292
+ if (sign === "-" && integer === "0" && !decimal.replace(/0/g, "")) sign = "";
1293
+ return {
1294
+ sign,
1295
+ integer,
1296
+ decimal
1297
+ };
1298
+ }
1299
+ function joinDecimalString(parts) {
1300
+ const integer = parts.integer === "" ? "0" : parts.integer;
1301
+ const decimal = parts.decimal;
1302
+ const body = decimal === "" ? integer : `${integer}.${decimal}`;
1303
+ return `${parts.sign}${body}`;
1304
+ }
1305
+ function formatDecimalForDisplay(opts) {
1306
+ const { value, scale, locale, currency, unit, useGrouping = true } = opts;
1307
+ if (value === null || value === void 0 || value === "") return "";
1308
+ let canonical;
1309
+ if (typeof value === "number") {
1310
+ if (!Number.isFinite(value)) return "";
1311
+ canonical = String(value);
1312
+ } else canonical = value;
1313
+ if (!/^[+-]?\d*(\.\d*)?$/.test(canonical) || canonical === "." || canonical === "-") return canonical;
1314
+ if (scale !== void 0 && scale >= 0) canonical = enforceScale(canonical, scale);
1315
+ const parts = splitDecimalString(canonical);
1316
+ const asNumber = Number(canonical);
1317
+ const intlSafe = Number.isFinite(asNumber);
1318
+ if (currency) {
1319
+ try {
1320
+ const fmt = getNumberFormat(locale, {
1321
+ style: "currency",
1322
+ currency,
1323
+ currencyDisplay: "narrowSymbol",
1324
+ minimumFractionDigits: scale,
1325
+ maximumFractionDigits: scale,
1326
+ useGrouping
1327
+ });
1328
+ if (intlSafe) return fmt.format(asNumber);
1329
+ } catch {}
1330
+ return `${currency} ${formatPlain(parts, scale, locale, useGrouping)}`;
1331
+ }
1332
+ const base = formatPlain(parts, scale, locale, useGrouping);
1333
+ return unit ? `${base} ${unit}` : base;
1334
+ }
1335
+ function formatPlain(parts, scale, locale, useGrouping) {
1336
+ const intStr = useGrouping ? groupInteger(parts.integer, locale) : parts.integer;
1337
+ const decStr = scale !== void 0 && scale >= 0 ? parts.decimal.padEnd(scale, "0").slice(0, scale) : parts.decimal;
1338
+ const decSep = getDecimalSeparator(locale);
1339
+ const body = decStr.length > 0 ? `${intStr}${decSep}${decStr}` : intStr;
1340
+ return `${parts.sign}${body}`;
1341
+ }
1342
+ /**
1343
+ * Insert the locale's thousands separator into a plain integer string.
1344
+ * Pure string-based — no float math, handles arbitrary length.
1345
+ */
1346
+ function groupInteger(integer, locale) {
1347
+ const sep = getThousandsSeparator(locale);
1348
+ if (!sep) return integer;
1349
+ if (integer.length <= 3) return integer;
1350
+ const out = [];
1351
+ let i = integer.length;
1352
+ while (i > 3) {
1353
+ out.unshift(integer.slice(i - 3, i));
1354
+ i -= 3;
1355
+ }
1356
+ out.unshift(integer.slice(0, i));
1357
+ return out.join(sep);
1358
+ }
1359
+ //#endregion
1360
+ //#region src/table/create-table-def.ts
1361
+ /**
1362
+ * Builds a TableDef from a moost-db MetaResponse.
1363
+ *
1364
+ * 1. Deserializes `meta.type` into a live TAtscriptAnnotatedType
1365
+ * 2. Flattens to discover all field paths
1366
+ * 3. Builds ColumnDef per field using annotations + meta.fields capabilities
1367
+ * 4. Sorts by @ui.table.order
1368
+ */
1369
+ function createTableDef(meta, preDeserializedType) {
1370
+ const type = preDeserializedType ?? (0, _atscript_typescript_utils.deserializeAnnotatedType)(meta.type);
1371
+ const flatMap = type.type.kind === "object" ? (0, _atscript_typescript_utils.flattenAnnotatedType)(type, { excludePhantomTypes: true }) : /* @__PURE__ */ new Map();
1372
+ const columns = [];
1373
+ for (const [path, prop] of flatMap.entries()) {
1374
+ if (path === "") continue;
1375
+ if (!(path in meta.fields)) {
1376
+ const kind = prop.type.kind;
1377
+ if (path.includes(".") || kind === "object" || kind === "array") continue;
1378
+ }
1379
+ const fieldMeta = meta.fields[path];
1380
+ const options = extractLiteralOptions(prop);
1381
+ const valueHelpInfo = extractValueHelp(prop);
1382
+ const maxLengthMeta = getFieldMeta(prop, EXPECT_MAX_LENGTH);
1383
+ const tableType = getFieldMeta(prop, UI_TABLE_TYPE);
1384
+ const sharedType = getFieldMeta(prop, UI_TYPE);
1385
+ const tableComponent = getFieldMeta(prop, UI_TABLE_COMPONENT);
1386
+ columns.push({
1387
+ path,
1388
+ label: getFieldMeta(prop, "meta.label") ?? humanizePath(path),
1389
+ type: tableType ?? sharedType ?? (valueHelpInfo ? "ref" : inferDisplayType(prop, options)),
1390
+ component: tableComponent,
1391
+ sortable: fieldMeta?.sortable ?? false,
1392
+ filterable: fieldMeta?.filterable ?? false,
1393
+ nullable: prop.optional === true,
1394
+ visible: getFieldMeta(prop, UI_TABLE_HIDDEN) === void 0,
1395
+ width: getFieldMeta(prop, UI_TABLE_WIDTH),
1396
+ maxLen: maxLengthMeta?.length,
1397
+ order: getFieldMeta(prop, "ui.table.order") ?? Infinity,
1398
+ options,
1399
+ valueHelpInfo,
1400
+ ...extractMeasurement(prop)
1401
+ });
1402
+ }
1403
+ columns.sort((a, b) => a.order - b.order);
1404
+ const actions = groupActions(meta.actions ?? []);
1405
+ const crud = meta.crud ?? {};
1406
+ return {
1407
+ type,
1408
+ columns,
1409
+ flatMap,
1410
+ primaryKeys: meta.primaryKeys,
1411
+ preferredId: meta.preferredId ?? meta.primaryKeys,
1412
+ crud,
1413
+ canRemove: "remove" in crud,
1414
+ actions,
1415
+ searchable: meta.searchable,
1416
+ vectorSearchable: meta.vectorSearchable,
1417
+ searchIndexes: meta.searchIndexes,
1418
+ relations: meta.relations
1419
+ };
1420
+ }
1421
+ /** Sort by (order ?? 0). `toSorted` is stable per spec, so ties preserve declaration order. */
1422
+ function byOrder(xs) {
1423
+ return xs.toSorted((x, y) => (x.order ?? 0) - (y.order ?? 0));
1424
+ }
1425
+ /**
1426
+ * Partition actions by `level`, sort each group by `(order ?? 0)` then
1427
+ * declaration order, and pick the first `default: true` entry per level.
1428
+ * The synthesised `__remove` UI action lives outside this set and is never
1429
+ * a candidate for `default.row`.
1430
+ */
1431
+ function groupActions(actions) {
1432
+ const table = [];
1433
+ const row = [];
1434
+ const rows = [];
1435
+ for (const a of actions) if (a.level === "table") table.push(a);
1436
+ else if (a.level === "row") row.push(a);
1437
+ else if (a.level === "rows") rows.push(a);
1438
+ const tableSorted = byOrder(table);
1439
+ const rowSorted = byOrder(row);
1440
+ const rowsSorted = byOrder(rows);
1441
+ return {
1442
+ table: tableSorted,
1443
+ row: rowSorted,
1444
+ rows: rowsSorted,
1445
+ default: {
1446
+ table: tableSorted.find((a) => a.default === true),
1447
+ row: rowSorted.find((a) => a.default === true),
1448
+ rows: rowsSorted.find((a) => a.default === true)
1449
+ }
1450
+ };
1451
+ }
1452
+ /** Infers a display type string from the annotated type's kind and designType. */
1453
+ function inferDisplayType(prop, literalOpts) {
1454
+ const kind = prop.type.kind;
1455
+ if (kind === "array") return "array";
1456
+ if (kind === "object") return "object";
1457
+ if (kind === "union") return literalOpts !== void 0 ? "enum" : "union";
1458
+ if (kind === "") {
1459
+ const final = prop.type;
1460
+ const dt = final.designType;
1461
+ if (dt === "number") return final.tags?.has("timestamp") ? "datetime" : "number";
1462
+ if (dt === "decimal") return "number";
1463
+ if (dt === "boolean") return "boolean";
1464
+ return "text";
1465
+ }
1466
+ return "text";
1467
+ }
1468
+ /** Converts a dot-path to a human-readable label (e.g. 'firstName' → 'First Name'). */
1469
+ function humanizePath(path) {
1470
+ return path.slice(path.lastIndexOf(".") + 1).replace(/([a-z])([A-Z])/g, "$1 $2").replace(/^./, (s) => s.toUpperCase());
1471
+ }
1472
+ //#endregion
1473
+ //#region src/shared/str.ts
1474
+ /** Safely convert an unknown value to a string without triggering no-base-to-string lint errors. */
1475
+ function str(value) {
1476
+ if (typeof value === "string") return value;
1477
+ if (typeof value === "number" || typeof value === "bigint" || typeof value === "boolean") return `${value}`;
1478
+ return JSON.stringify(value) ?? "";
1479
+ }
1480
+ //#endregion
1481
+ //#region src/table/column-resolver.ts
1482
+ /** Get visible columns only, already sorted by order. */
1483
+ function getVisibleColumns(def) {
1484
+ return def.columns.filter((c) => c.visible);
1485
+ }
1486
+ /** Get sortable columns. */
1487
+ function getSortableColumns(def) {
1488
+ return def.columns.filter((c) => c.sortable);
1489
+ }
1490
+ /** Get filterable columns. */
1491
+ function getFilterableColumns(def) {
1492
+ return def.columns.filter((c) => c.filterable);
1493
+ }
1494
+ /** Find a column by path. */
1495
+ function getColumn(def, path) {
1496
+ return def.columns.find((c) => c.path === path);
1497
+ }
1498
+ //#endregion
1499
+ exports.DB_AMOUNT_CURRENCY = DB_AMOUNT_CURRENCY;
1500
+ exports.DB_AMOUNT_CURRENCY_REF = DB_AMOUNT_CURRENCY_REF;
1501
+ exports.DB_COLUMN_PRECISION = DB_COLUMN_PRECISION;
1502
+ exports.DB_HTTP_PATH = DB_HTTP_PATH;
1503
+ exports.DB_REL_FK = DB_REL_FK;
1504
+ exports.DB_UNIT = DB_UNIT;
1505
+ exports.DB_UNIT_REF = DB_UNIT_REF;
1506
+ exports.DEFAULT_COL_SPAN = DEFAULT_COL_SPAN;
1507
+ exports.DEFAULT_ROW_SPAN = DEFAULT_ROW_SPAN;
1508
+ exports.EXPECT_MAX_LENGTH = EXPECT_MAX_LENGTH;
1509
+ exports.META_DEFAULT = META_DEFAULT;
1510
+ exports.META_DESCRIPTION = META_DESCRIPTION;
1511
+ exports.META_ID = META_ID;
1512
+ exports.META_LABEL = META_LABEL;
1513
+ exports.META_READONLY = META_READONLY;
1514
+ exports.META_REQUIRED = META_REQUIRED;
1515
+ exports.META_SENSITIVE = META_SENSITIVE;
1516
+ exports.StaticFieldResolver = StaticFieldResolver;
1517
+ exports.UI_DICT_ATTR = UI_DICT_ATTR;
1518
+ exports.UI_DICT_DESCR = UI_DICT_DESCR;
1519
+ exports.UI_DICT_FILTERABLE = UI_DICT_FILTERABLE;
1520
+ exports.UI_DICT_LABEL = UI_DICT_LABEL;
1521
+ exports.UI_DICT_SEARCHABLE = UI_DICT_SEARCHABLE;
1522
+ exports.UI_DICT_SORTABLE = UI_DICT_SORTABLE;
1523
+ exports.UI_FORM_ACTION = UI_FORM_ACTION;
1524
+ exports.UI_FORM_ATTR = UI_FORM_ATTR;
1525
+ exports.UI_FORM_AUTOCOMPLETE = UI_FORM_AUTOCOMPLETE;
1526
+ exports.UI_FORM_CLASSES = UI_FORM_CLASSES;
1527
+ exports.UI_FORM_COMPONENT = UI_FORM_COMPONENT;
1528
+ exports.UI_FORM_DISABLED = UI_FORM_DISABLED;
1529
+ exports.UI_FORM_FN_ATTR = UI_FORM_FN_ATTR;
1530
+ exports.UI_FORM_FN_CLASSES = UI_FORM_FN_CLASSES;
1531
+ exports.UI_FORM_FN_DESCRIPTION = UI_FORM_FN_DESCRIPTION;
1532
+ exports.UI_FORM_FN_DISABLED = UI_FORM_FN_DISABLED;
1533
+ exports.UI_FORM_FN_HIDDEN = UI_FORM_FN_HIDDEN;
1534
+ exports.UI_FORM_FN_HINT = UI_FORM_FN_HINT;
1535
+ exports.UI_FORM_FN_LABEL = UI_FORM_FN_LABEL;
1536
+ exports.UI_FORM_FN_OPTIONS = UI_FORM_FN_OPTIONS;
1537
+ exports.UI_FORM_FN_PLACEHOLDER = UI_FORM_FN_PLACEHOLDER;
1538
+ exports.UI_FORM_FN_PREFIX = UI_FORM_FN_PREFIX;
1539
+ exports.UI_FORM_FN_READONLY = UI_FORM_FN_READONLY;
1540
+ exports.UI_FORM_FN_STYLES = UI_FORM_FN_STYLES;
1541
+ exports.UI_FORM_FN_SUBMIT_DISABLED = UI_FORM_FN_SUBMIT_DISABLED;
1542
+ exports.UI_FORM_FN_SUBMIT_TEXT = UI_FORM_FN_SUBMIT_TEXT;
1543
+ exports.UI_FORM_FN_TITLE = UI_FORM_FN_TITLE;
1544
+ exports.UI_FORM_FN_VALUE = UI_FORM_FN_VALUE;
1545
+ exports.UI_FORM_GRID_COL_SPAN = UI_FORM_GRID_COL_SPAN;
1546
+ exports.UI_FORM_GRID_ROW_SPAN = UI_FORM_GRID_ROW_SPAN;
1547
+ exports.UI_FORM_HIDDEN = UI_FORM_HIDDEN;
1548
+ exports.UI_FORM_HINT = UI_FORM_HINT;
1549
+ exports.UI_FORM_LABEL_SINGULAR = UI_FORM_LABEL_SINGULAR;
1550
+ exports.UI_FORM_OPTIONS = UI_FORM_OPTIONS;
1551
+ exports.UI_FORM_ORDER = UI_FORM_ORDER;
1552
+ exports.UI_FORM_PLACEHOLDER = UI_FORM_PLACEHOLDER;
1553
+ exports.UI_FORM_PREFIX = UI_FORM_PREFIX;
1554
+ exports.UI_FORM_PREFIX_ICON = UI_FORM_PREFIX_ICON;
1555
+ exports.UI_FORM_PREFIX_REF = UI_FORM_PREFIX_REF;
1556
+ exports.UI_FORM_STYLES = UI_FORM_STYLES;
1557
+ exports.UI_FORM_SUBMIT_TEXT = UI_FORM_SUBMIT_TEXT;
1558
+ exports.UI_FORM_SUFFIX = UI_FORM_SUFFIX;
1559
+ exports.UI_FORM_SUFFIX_ICON = UI_FORM_SUFFIX_ICON;
1560
+ exports.UI_FORM_SUFFIX_REF = UI_FORM_SUFFIX_REF;
1561
+ exports.UI_FORM_TYPE = UI_FORM_TYPE;
1562
+ exports.UI_FORM_VALIDATE = UI_FORM_VALIDATE;
1563
+ exports.UI_TABLE_ATTR = UI_TABLE_ATTR;
1564
+ exports.UI_TABLE_CLASSES = UI_TABLE_CLASSES;
1565
+ exports.UI_TABLE_COMPONENT = UI_TABLE_COMPONENT;
1566
+ exports.UI_TABLE_FN_ATTR = UI_TABLE_FN_ATTR;
1567
+ exports.UI_TABLE_FN_CLASSES = UI_TABLE_FN_CLASSES;
1568
+ exports.UI_TABLE_FN_PREFIX = UI_TABLE_FN_PREFIX;
1569
+ exports.UI_TABLE_FN_STYLES = UI_TABLE_FN_STYLES;
1570
+ exports.UI_TABLE_HIDDEN = UI_TABLE_HIDDEN;
1571
+ exports.UI_TABLE_ORDER = UI_TABLE_ORDER;
1572
+ exports.UI_TABLE_STYLES = UI_TABLE_STYLES;
1573
+ exports.UI_TABLE_TYPE = UI_TABLE_TYPE;
1574
+ exports.UI_TABLE_WIDTH = UI_TABLE_WIDTH;
1575
+ exports.UI_TYPE = UI_TYPE;
1576
+ exports.ValueHelpClient = ValueHelpClient;
1577
+ exports.WF_ACTION_WITH_DATA = WF_ACTION_WITH_DATA;
1578
+ exports.asArray = asArray;
1579
+ exports.buildDescendantErrorCounts = buildDescendantErrorCounts;
1580
+ exports.buildGridClasses = buildGridClasses;
1581
+ exports.buildUnionVariants = buildUnionVariants;
1582
+ exports.createFieldValidator = createFieldValidator;
1583
+ exports.createFormData = createFormData;
1584
+ exports.createFormDef = createFormDef;
1585
+ exports.createFormValueResolver = createFormValueResolver;
1586
+ exports.createTableDef = createTableDef;
1587
+ exports.defaultResolver = defaultResolver;
1588
+ exports.detectUnionVariant = detectUnionVariant;
1589
+ exports.enforceScale = enforceScale;
1590
+ exports.extractLiteralOptions = extractLiteralOptions;
1591
+ exports.extractMeasurement = extractMeasurement;
1592
+ exports.extractValueHelp = extractValueHelp;
1593
+ exports.formatDecimalForDisplay = formatDecimalForDisplay;
1594
+ exports.getByPath = getByPath;
1595
+ exports.getColumn = getColumn;
1596
+ exports.getCurrencyDecimals = getCurrencyDecimals;
1597
+ exports.getCurrencyDisplayParts = getCurrencyDisplayParts;
1598
+ exports.getDecimalSeparator = getDecimalSeparator;
1599
+ exports.getDefaultClientFactory = getDefaultClientFactory;
1600
+ exports.getDefaultValidatorPlugins = getDefaultValidatorPlugins;
1601
+ exports.getFieldMeta = getFieldMeta;
1602
+ exports.getFilterableColumns = getFilterableColumns;
1603
+ exports.getFormValidator = getFormValidator;
1604
+ exports.getMetaEntry = getMetaEntry;
1605
+ exports.getResolver = getResolver;
1606
+ exports.getSortableColumns = getSortableColumns;
1607
+ exports.getThousandsSeparator = getThousandsSeparator;
1608
+ exports.getVisibleColumns = getVisibleColumns;
1609
+ exports.groupInteger = groupInteger;
1610
+ exports.hasComputedAnnotations = hasComputedAnnotations;
1611
+ exports.isArrayField = isArrayField;
1612
+ exports.isObjectField = isObjectField;
1613
+ exports.isPureLiteralUnion = isPureLiteralUnion;
1614
+ exports.isTupleField = isTupleField;
1615
+ exports.isUnionField = isUnionField;
1616
+ exports.iteratePathAncestors = iteratePathAncestors;
1617
+ exports.joinDecimalString = joinDecimalString;
1618
+ exports.mergeErrorMaps = mergeErrorMaps;
1619
+ exports.optKey = optKey;
1620
+ exports.optLabel = optLabel;
1621
+ exports.parseColSpan = parseColSpan;
1622
+ exports.parseDecimalInput = parseDecimalInput;
1623
+ exports.parseRowSpan = parseRowSpan;
1624
+ exports.parseStaticAttrs = parseStaticAttrs;
1625
+ exports.parseStaticOptions = parseStaticOptions;
1626
+ exports.resetDefaultClientFactory = resetDefaultClientFactory;
1627
+ exports.resetMetaCache = resetMetaCache;
1628
+ exports.resetValueHelpCache = resetValueHelpCache;
1629
+ exports.resolveAttrs = resolveAttrs;
1630
+ exports.resolveFieldProp = resolveFieldProp;
1631
+ exports.resolveFormProp = resolveFormProp;
1632
+ exports.resolveGridSpec = resolveGridSpec;
1633
+ exports.resolveOptions = resolveOptions;
1634
+ exports.resolveSingularLabel = resolveSingularLabel;
1635
+ exports.resolveStatic = resolveStatic;
1636
+ exports.resolveValueHelp = resolveValueHelp;
1637
+ exports.setByPath = setByPath;
1638
+ exports.setDefaultClientFactory = setDefaultClientFactory;
1639
+ exports.setDefaultValidatorPlugins = setDefaultValidatorPlugins;
1640
+ exports.setResolver = setResolver;
1641
+ exports.splitDecimalString = splitDecimalString;
1642
+ exports.str = str;
1643
+ exports.valueHelpDictPaths = valueHelpDictPaths;