@atscript/ui 0.1.103 → 0.1.104

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.mjs CHANGED
@@ -29,7 +29,7 @@ const UI_FORM_SUFFIX_ICON = "ui.form.suffix.icon";
29
29
  const UI_TABLE_WIDTH = "ui.table.width";
30
30
  const UI_TABLE_COMPONENT = "ui.table.component";
31
31
  const UI_TABLE_SELECT_WITH = "ui.table.selectWith";
32
- const UI_TABLE_HIDDEN = "ui.table.hidden";
32
+ const UI_TABLE_EXCLUDE = "ui.table.exclude";
33
33
  const UI_TABLE_ATTR = "ui.table.attr";
34
34
  const UI_TABLE_CLASSES = "ui.table.classes";
35
35
  const UI_TABLE_STYLES = "ui.table.styles";
@@ -810,6 +810,35 @@ function setByPath(obj, path, value) {
810
810
  }
811
811
  current[last] = value;
812
812
  }
813
+ /**
814
+ * Deletes the own key at a dot-separated path (form-data wrapper aware — derefs
815
+ * `obj.value` first). Walks to the parent WITHOUT vivifying intermediate nodes:
816
+ * if any ancestor is missing, the call is a no-op (nothing to delete).
817
+ *
818
+ * Unlike `setByPath(obj, path, undefined)`, this leaves NO own key behind — the
819
+ * leaf reads as absent (`'k' in parent === false`), which keeps `deepEqual`
820
+ * structural comparisons in sync (a present `undefined` own-key and an absent
821
+ * key are NOT structurally equal under the own-key walk). Used by
822
+ * {@link applyFormChanges} to apply a clear-to-`undefined` change as a delete.
823
+ *
824
+ * Empty path clears the root domain value (`obj.value = undefined`).
825
+ */
826
+ function deleteByPath(obj, path) {
827
+ if (!path) {
828
+ obj.value = void 0;
829
+ return;
830
+ }
831
+ const keys = path.split(".");
832
+ const last = keys.pop();
833
+ if (last === void 0) return;
834
+ let current = obj.value;
835
+ for (const key of keys) {
836
+ if (current === null || current === void 0 || typeof current !== "object") return;
837
+ current = current[key];
838
+ }
839
+ if (current === null || current === void 0 || typeof current !== "object") return;
840
+ delete current[last];
841
+ }
813
842
  function parseStaticDefault(raw, prop) {
814
843
  if (typeof raw !== "string") return raw;
815
844
  if (prop.type.kind === "" && prop.type.designType === "string") return raw;
@@ -883,8 +912,11 @@ function detectUnionVariant(value, variants) {
883
912
  const disc = getVariantsDiscriminator(variants);
884
913
  if (disc && value !== null && typeof value === "object") {
885
914
  const tag = value[disc.propertyName];
886
- const idx = disc.indexMapping[String(tag)];
887
- if (idx !== void 0) return idx;
915
+ const key = String(tag);
916
+ if (Object.prototype.hasOwnProperty.call(disc.indexMapping, key)) {
917
+ const idx = disc.indexMapping[key];
918
+ if (idx !== void 0) return idx;
919
+ }
888
920
  }
889
921
  for (let i = 0; i < variants.length; i++) try {
890
922
  if (getVariantValidator(variants[i]).validate(value, true)) return i;
@@ -892,6 +924,35 @@ function detectUnionVariant(value, variants) {
892
924
  return 0;
893
925
  }
894
926
  //#endregion
927
+ //#region src/form/clone.ts
928
+ /**
929
+ * Structural deep clone of plain JSON-ish data (objects / arrays / primitives /
930
+ * `Date`). Walks OWN-ENUMERABLE keys only (matches the own-key discipline in
931
+ * `diff.ts` — never copies an accidental prototype) and copies leaves by value.
932
+ *
933
+ * `structuredClone` is deliberately NOT used: it throws on functions and on Vue
934
+ * reactive proxies. The optional `unwrap` hook lets a framework caller
935
+ * de-proxy each value first (vue-form passes `toRaw`); the core omits it.
936
+ *
937
+ * The SINGLE deep-clone primitive for the form engine — used by
938
+ * `applyFormChanges`, `buildFormRebase`, and vue-form's baseline snapshot. Do
939
+ * not reimplement structural cloning elsewhere.
940
+ */
941
+ function deepClone(value, unwrap) {
942
+ const v = unwrap ? unwrap(value) : value;
943
+ if (v === null || typeof v !== "object") return v;
944
+ if (v instanceof Date) return new Date(v.getTime());
945
+ if (Array.isArray(v)) {
946
+ const out = [];
947
+ for (let i = 0; i < v.length; i++) out.push(deepClone(v[i], unwrap));
948
+ return out;
949
+ }
950
+ const src = v;
951
+ const out = {};
952
+ for (const k of Object.keys(src)) out[k] = deepClone(src[k], unwrap);
953
+ return out;
954
+ }
955
+ //#endregion
895
956
  //#region src/form/validate.ts
896
957
  let defaultValidatorPlugins = [];
897
958
  /** Replace the default validator plugins applied to every form/field validator. */
@@ -1350,6 +1411,10 @@ function stableKey(v) {
1350
1411
  * Structural deep equality (order-sensitive for arrays). `NaN` equals `NaN`
1351
1412
  * (revert-aware for NaN scalars) while `0` / `-0` stay equal (matches DB
1352
1413
  * intent — `===` treats them equal, only NaN is special-cased).
1414
+ *
1415
+ * The single comparator shared across the form engine: diff, conflict
1416
+ * detection ({@link buildFormRebase}), and apply all route through this — never
1417
+ * reimplement equality elsewhere.
1353
1418
  */
1354
1419
  function deepEqual(a, b) {
1355
1420
  if (a === b) return true;
@@ -1376,6 +1441,228 @@ function deepEqual(a, b) {
1376
1441
  return true;
1377
1442
  }
1378
1443
  //#endregion
1444
+ //#region src/form/dirty.ts
1445
+ /**
1446
+ * True when the field at dot-path `path` is dirty given a {@link FormFieldChange}
1447
+ * list (as produced by {@link buildFormDiff}).
1448
+ *
1449
+ * The change list is leaf-grained for scalars/objects but WHOLE-ARRAY for arrays,
1450
+ * so a field at `path` is dirty iff some change path equals `path` OR starts with
1451
+ * `path + "."`:
1452
+ *
1453
+ * - scalar / leaf field (incl. nested `address.city`) → exact match.
1454
+ * - object / section container → no entry at its own path, only its leaves →
1455
+ * matched by the PREFIX branch.
1456
+ * - whole-array field → one entry at the array root → exact match.
1457
+ * - a field rendered for an array-ITEM leaf (e.g. `items.0.qty`) → NOT detectable:
1458
+ * the array diff emits a single whole-array change at the array root, never
1459
+ * per-item leaf paths, so this correctly returns false (the array container
1460
+ * lights up instead). This is a known, documented limitation.
1461
+ *
1462
+ * The prefix uses `path + "."` so field `item` never matches a change at `items`
1463
+ * (no false positives).
1464
+ *
1465
+ * Empty `path` `''` is the wrapped form root — every change is nested under it,
1466
+ * so it is considered dirty iff there are ANY changes.
1467
+ */
1468
+ function isPathDirty(changes, path) {
1469
+ if (path === "") return changes.length > 0;
1470
+ const prefix = `${path}.`;
1471
+ for (const change of changes) if (change.path === path || change.path.startsWith(prefix)) return true;
1472
+ return false;
1473
+ }
1474
+ /**
1475
+ * Precomputes the set of ALL dirty paths from a {@link FormFieldChange} list so
1476
+ * that membership is an O(1) `Set.has(path)` instead of {@link isPathDirty}'s
1477
+ * per-call O(changes) prefix scan. Callers that probe many fields against the
1478
+ * same change list (e.g. a form rendering one field per leaf) build this once
1479
+ * and query it per field.
1480
+ *
1481
+ * For each change path `C` it adds `C` AND every dot-prefix ancestor of `C`
1482
+ * (so `'address.city'` adds both `'address.city'` and `'address'`), matching
1483
+ * `isPathDirty`'s "exact OR `path + '.'` prefix" predicate — an ancestor
1484
+ * container is dirty exactly when some change is nested under it. The wrapped
1485
+ * root `''` is added iff there are ANY changes, mirroring `isPathDirty('')`.
1486
+ *
1487
+ * INVARIANT (locked, tested): for EVERY path `P`,
1488
+ * `collectDirtyPaths(changes).has(P) === isPathDirty(changes, P)`. This is a
1489
+ * precompute of the SAME predicate, not a second one — keep them in lockstep.
1490
+ */
1491
+ function collectDirtyPaths(changes) {
1492
+ const dirty = /* @__PURE__ */ new Set();
1493
+ if (changes.length === 0) return dirty;
1494
+ dirty.add("");
1495
+ for (const change of changes) {
1496
+ const path = change.path;
1497
+ dirty.add(path);
1498
+ let dot = path.indexOf(".");
1499
+ while (dot !== -1) {
1500
+ dirty.add(path.slice(0, dot));
1501
+ dot = path.indexOf(".", dot + 1);
1502
+ }
1503
+ }
1504
+ return dirty;
1505
+ }
1506
+ //#endregion
1507
+ //#region src/form/apply.ts
1508
+ /**
1509
+ * Applies a {@link FormFieldChange} list onto a WRAPPED form-data container
1510
+ * (`{ value: domainData }`), mutating it in place and returning the same
1511
+ * reference. The inverse direction of {@link buildFormDiff}: where the diff
1512
+ * READS `(baseline, current)` into changes, this WRITES changes onto data.
1513
+ *
1514
+ * IMPORTANT: pass a CLONE, never the live fetched row — every write mutates
1515
+ * `data` directly. Callers that need the original intact should
1516
+ * `deepClone(data)` first (see {@link deepClone}).
1517
+ *
1518
+ * Per-change semantics (the single place the apply rules live, so
1519
+ * {@link buildFormRebase} stays consistent):
1520
+ *
1521
+ * - `kind: 'set'`:
1522
+ * - `change.after === undefined` → DELETE the own key at `change.path` (walk
1523
+ * to parent, `delete`). A cleared field must read as ABSENT, not as a
1524
+ * present `undefined` own-key — otherwise a re-diff sees a structural
1525
+ * mismatch where the form intends "no value". `setByPath(…, undefined)`
1526
+ * leaves an own key behind, so we use {@link deleteByPath} instead.
1527
+ * - otherwise → `setByPath(data, change.path, change.after)`.
1528
+ * - `kind: 'array'`: whole-array set via `setByPath(data, change.path,
1529
+ * change.after)` (LOCKED Option A — no per-element merge; the diff already
1530
+ * carried the full after-array).
1531
+ *
1532
+ * The `def` is currently unused by the apply walk (paths fully describe the
1533
+ * write target) but is part of the signature for parity with
1534
+ * `buildFormDiff`/`buildFormRebase`, so the rebase engine threads one `def`
1535
+ * uniformly through diff + apply.
1536
+ */
1537
+ function applyFormChanges(_def, data, changes) {
1538
+ for (const change of changes) if (change.kind === "set" && change.after === void 0) deleteByPath(data, change.path);
1539
+ else setByPath(data, change.path, change.after);
1540
+ return data;
1541
+ }
1542
+ //#endregion
1543
+ //#region src/form/rebase.ts
1544
+ /**
1545
+ * Pure 3-way rebase for a change-tracked form. Given the current baseline `B0`,
1546
+ * the live form `C`, and a fresh upstream `U`, produces the form rewritten as
1547
+ * `U` + the local diff (`C` vs `B0`) reapplied on top:
1548
+ *
1549
+ * - Fields the user never touched adopt upstream's value.
1550
+ * - Local edits survive (reapplied onto the upstream clone).
1551
+ * - Fields changed on BOTH sides to different values are conflicts, resolved by
1552
+ * `opts.conflict` (`'ours'` keeps local, `'theirs'` takes upstream).
1553
+ *
1554
+ * All inputs are WRAPPED form-data containers (`{ value: domainData }`). The
1555
+ * result `next` is a fresh container; no input is mutated.
1556
+ *
1557
+ * `diffOptions` are forwarded to BOTH internal `buildFormDiff` passes so the
1558
+ * same field exclusions apply (notably the `@db.column.version` column and the
1559
+ * `$cas` policy) on the local and upstream sides — keep them identical to the
1560
+ * options the caller uses for its own change tracking.
1561
+ */
1562
+ function buildFormRebase(def, baseline, current, upstream, opts, diffOptions) {
1563
+ const conflictMode = opts?.conflict ?? "ours";
1564
+ const local = buildFormDiff(def, baseline, current, diffOptions).changes;
1565
+ const upstreamChanges = buildFormDiff(def, baseline, upstream, diffOptions).changes;
1566
+ const upstreamByPath = /* @__PURE__ */ new Map();
1567
+ for (const uc of upstreamChanges) upstreamByPath.set(uc.path, uc);
1568
+ const next = deepClone(upstream);
1569
+ const conflicts = [];
1570
+ for (const lc of local) {
1571
+ const clearedAncestor = findClearedAncestor(lc.path, baseline, upstream);
1572
+ if (clearedAncestor !== void 0) {
1573
+ conflicts.push(clearedAncestor);
1574
+ if (conflictMode === "ours") setByPath(next, clearedAncestor, deepClone(getByPath(current, clearedAncestor)));
1575
+ continue;
1576
+ }
1577
+ const uc = upstreamByPath.get(lc.path);
1578
+ if (uc !== void 0) {
1579
+ if (deepEqual(lc.after, uc.after)) continue;
1580
+ conflicts.push(lc.path);
1581
+ if (conflictMode === "ours") reapply(def, next, lc);
1582
+ continue;
1583
+ }
1584
+ reapply(def, next, lc);
1585
+ }
1586
+ const reapplied = buildFormDiff(def, upstream, deepClone(next), diffOptions).changes;
1587
+ return {
1588
+ next,
1589
+ conflicts: [...new Set(conflicts)],
1590
+ reapplied
1591
+ };
1592
+ }
1593
+ /**
1594
+ * Reapplies a single local change onto `next`, DEEP-CLONING its `after` value
1595
+ * first. `lc.after` is a LIVE reference into `current` (buildFormDiff holds live
1596
+ * refs), so for a `kind:'array'` or whole-object/union `set` change a raw apply
1597
+ * would make `next.value`'s node `===` `current.value`'s node — violating the
1598
+ * `FormRebaseResult.next` contract ("never aliases any input container"). The
1599
+ * ancestor-clear branch already deep-clones before writing; this keeps the two
1600
+ * leaf-reapply sites consistent.
1601
+ */
1602
+ function reapply(def, next, lc) {
1603
+ applyFormChanges(def, next, [{
1604
+ ...lc,
1605
+ after: deepClone(lc.after)
1606
+ }]);
1607
+ }
1608
+ /**
1609
+ * Returns the SHALLOWEST strict ancestor of `leafPath` that was an object/array
1610
+ * in `baseline` but is `null`/`undefined` in `upstream` (upstream cleared the
1611
+ * subtree), or `undefined` when no ancestor was cleared. The leaf path itself is
1612
+ * never considered an ancestor.
1613
+ */
1614
+ function findClearedAncestor(leafPath, baseline, upstream) {
1615
+ if (!leafPath.includes(".")) return void 0;
1616
+ const segs = leafPath.split(".");
1617
+ let acc = "";
1618
+ for (let i = 0; i < segs.length - 1; i++) {
1619
+ acc = acc ? `${acc}.${segs[i]}` : segs[i];
1620
+ const base = getByPath(baseline, acc);
1621
+ if (typeof base !== "object" || base === null) continue;
1622
+ const up = getByPath(upstream, acc);
1623
+ if (up === null || up === void 0) return acc;
1624
+ }
1625
+ }
1626
+ //#endregion
1627
+ //#region src/form/union-detect.ts
1628
+ /**
1629
+ * True when ANY union field in the form resolves to a DIFFERENT discriminated
1630
+ * variant between two wrapped data containers. A variant picker typically
1631
+ * detects its variant index once at setup and keys the variant subtree on it,
1632
+ * so a rebase that lands a different variant (via conflict OR an upstream-only
1633
+ * switch) needs a remount to re-detect. This walks union + nested-object fields
1634
+ * and compares `detectUnionVariant` at each union path.
1635
+ *
1636
+ * Scope note (pragmatic): walks standalone + nested-OBJECT union fields. Unions
1637
+ * nested INSIDE array items are not walked — an array renderer that keeps a
1638
+ * stable per-item key across in-place value mutations would not remount an
1639
+ * existing row's picker on an upstream-driven variant flip, but that collision
1640
+ * (a 3-way rebase landing a different union variant inside an unchanged array
1641
+ * row) is a rare edge. TODO: extend to array-item unions if a real consumer
1642
+ * hits a stuck picker inside an array row.
1643
+ */
1644
+ function unionVariantChanged(def, before, after) {
1645
+ return walkUnionFields(def.fields, "", before, after);
1646
+ }
1647
+ function walkUnionFields(fields, prefix, before, after) {
1648
+ for (const field of fields) {
1649
+ if (field.phantom) continue;
1650
+ const fullPath = field.path ? prefix ? `${prefix}.${field.path}` : field.path : prefix;
1651
+ if (isUnionField(field)) {
1652
+ const variants = field.unionVariants;
1653
+ if (variants.length > 1) {
1654
+ if (detectUnionVariant(getByPath(before, fullPath), variants) !== detectUnionVariant(getByPath(after, fullPath), variants)) return true;
1655
+ }
1656
+ continue;
1657
+ }
1658
+ if (isObjectField(field)) {
1659
+ const objectDef = field.objectDef;
1660
+ if (walkUnionFields(objectDef.fields, fullPath, before, after)) return true;
1661
+ }
1662
+ }
1663
+ return false;
1664
+ }
1665
+ //#endregion
1379
1666
  //#region src/form/error-utils.ts
1380
1667
  /**
1381
1668
  * Framework-agnostic helpers for working with form-error maps keyed by
@@ -1860,6 +2147,7 @@ function createTableDef(meta, preDeserializedType) {
1860
2147
  const kind = prop.type.kind;
1861
2148
  if (path.includes(".") || kind === "object" || kind === "array") continue;
1862
2149
  }
2150
+ if (getFieldMeta(prop, "ui.table.exclude") !== void 0) continue;
1863
2151
  const fieldMeta = meta.fields[path];
1864
2152
  const options = extractLiteralOptions(prop);
1865
2153
  const valueHelpInfo = extractValueHelp(prop);
@@ -1876,7 +2164,6 @@ function createTableDef(meta, preDeserializedType) {
1876
2164
  sortable: fieldMeta?.sortable ?? false,
1877
2165
  filterable: fieldMeta?.filterable ?? false,
1878
2166
  nullable: prop.optional === true,
1879
- visible: getFieldMeta(prop, UI_TABLE_HIDDEN) === void 0,
1880
2167
  width: getFieldMeta(prop, UI_TABLE_WIDTH),
1881
2168
  maxLen: maxLengthMeta?.length,
1882
2169
  order: getFieldMeta(prop, "ui.table.order") ?? Infinity,
@@ -1892,6 +2179,7 @@ function createTableDef(meta, preDeserializedType) {
1892
2179
  type,
1893
2180
  columns,
1894
2181
  flatMap,
2182
+ fetchableFields: new Set(Object.keys(meta.fields)),
1895
2183
  primaryKeys: meta.primaryKeys,
1896
2184
  preferredId: meta.preferredId ?? meta.primaryKeys,
1897
2185
  versionColumn: meta.versionColumn,
@@ -1965,10 +2253,6 @@ function str(value) {
1965
2253
  }
1966
2254
  //#endregion
1967
2255
  //#region src/table/column-resolver.ts
1968
- /** Get visible columns only, already sorted by order. */
1969
- function getVisibleColumns(def) {
1970
- return def.columns.filter((c) => c.visible);
1971
- }
1972
2256
  /** Get sortable columns. */
1973
2257
  function getSortableColumns(def) {
1974
2258
  return def.columns.filter((c) => c.sortable);
@@ -1982,4 +2266,4 @@ function getColumn(def, path) {
1982
2266
  return def.columns.find((c) => c.path === path);
1983
2267
  }
1984
2268
  //#endregion
1985
- export { DB_AMOUNT_CURRENCY, DB_AMOUNT_CURRENCY_REF, DB_COLUMN_PRECISION, DB_HTTP_PATH, DB_REL_FK, DB_UNIT, DB_UNIT_REF, DEFAULT_COL_SPAN, DEFAULT_ROW_SPAN, EXPECT_MAX_LENGTH, META_DEFAULT, META_DESCRIPTION, META_ID, META_LABEL, META_READONLY, META_REQUIRED, META_SENSITIVE, StaticFieldResolver, UI_DICT_ATTR, UI_DICT_DESCR, UI_DICT_FILTERABLE, UI_DICT_LABEL, UI_DICT_SEARCHABLE, UI_DICT_SORTABLE, UI_FORM_ACTION, UI_FORM_ATTR, UI_FORM_AUTOCOMPLETE, UI_FORM_CLASSES, UI_FORM_COMPONENT, UI_FORM_DISABLED, UI_FORM_FN_ATTR, UI_FORM_FN_CLASSES, UI_FORM_FN_DESCRIPTION, UI_FORM_FN_DISABLED, UI_FORM_FN_HIDDEN, UI_FORM_FN_HINT, UI_FORM_FN_LABEL, UI_FORM_FN_OPTIONS, UI_FORM_FN_PLACEHOLDER, UI_FORM_FN_PREFIX, UI_FORM_FN_READONLY, UI_FORM_FN_STYLES, UI_FORM_FN_SUBMIT_DISABLED, UI_FORM_FN_SUBMIT_TEXT, UI_FORM_FN_TITLE, UI_FORM_FN_VALUE, UI_FORM_GRID_COL_SPAN, UI_FORM_GRID_ROW_SPAN, UI_FORM_HIDDEN, UI_FORM_HINT, UI_FORM_LABEL_SINGULAR, UI_FORM_OPTIONS, UI_FORM_ORDER, UI_FORM_PLACEHOLDER, UI_FORM_PREFIX, UI_FORM_PREFIX_ICON, UI_FORM_PREFIX_REF, UI_FORM_STYLES, UI_FORM_SUBMIT_TEXT, UI_FORM_SUFFIX, UI_FORM_SUFFIX_ICON, UI_FORM_SUFFIX_REF, UI_FORM_TYPE, UI_FORM_VALIDATE, UI_TABLE_ATTR, UI_TABLE_CLASSES, UI_TABLE_COMPONENT, UI_TABLE_FN_ATTR, UI_TABLE_FN_CLASSES, UI_TABLE_FN_PREFIX, UI_TABLE_FN_STYLES, UI_TABLE_HIDDEN, UI_TABLE_ORDER, UI_TABLE_SELECT_WITH, UI_TABLE_STYLES, UI_TABLE_TYPE, UI_TABLE_WIDTH, UI_TYPE, ValueHelpClient, WF_ACTION_WITH_DATA, asArray, buildDescendantErrorCounts, buildFormDiff, buildGridClasses, buildUnionVariants, createFieldValidator, createFormData, createFormDef, createFormValueResolver, createTableDef, defaultResolver, detectUnionVariant, enforceScale, extractLiteralOptions, extractMeasurement, extractValueHelp, formatDecimalForDisplay, getByPath, getColumn, getCurrencyDecimals, getCurrencyDisplayParts, getDecimalSeparator, getDeclaredFormActions, getDefaultClientFactory, getDefaultValidatorPlugins, getFieldMeta, getFilterableColumns, getFormValidator, getMetaEntry, getResolver, getSortableColumns, getThousandsSeparator, getVisibleColumns, groupInteger, hasComputedAnnotations, isArrayField, isObjectField, isPureLiteralUnion, isTupleField, isUnionField, iteratePathAncestors, joinDecimalString, mergeErrorMaps, optKey, optLabel, parseColSpan, parseDecimalInput, parseRowSpan, parseStaticAttrs, parseStaticOptions, resetDefaultClientFactory, resetMetaCache, resetValueHelpCache, resolveAttrs, resolveFieldProp, resolveFormProp, resolveGridSpec, resolveOptions, resolveSingularLabel, resolveStatic, resolveValueHelp, setByPath, setDefaultClientFactory, setDefaultValidatorPlugins, setResolver, splitDecimalString, str, valueHelpDictPaths };
2269
+ export { DB_AMOUNT_CURRENCY, DB_AMOUNT_CURRENCY_REF, DB_COLUMN_PRECISION, DB_HTTP_PATH, DB_REL_FK, DB_UNIT, DB_UNIT_REF, DEFAULT_COL_SPAN, DEFAULT_ROW_SPAN, EXPECT_MAX_LENGTH, META_DEFAULT, META_DESCRIPTION, META_ID, META_LABEL, META_READONLY, META_REQUIRED, META_SENSITIVE, StaticFieldResolver, UI_DICT_ATTR, UI_DICT_DESCR, UI_DICT_FILTERABLE, UI_DICT_LABEL, UI_DICT_SEARCHABLE, UI_DICT_SORTABLE, UI_FORM_ACTION, UI_FORM_ATTR, UI_FORM_AUTOCOMPLETE, UI_FORM_CLASSES, UI_FORM_COMPONENT, UI_FORM_DISABLED, UI_FORM_FN_ATTR, UI_FORM_FN_CLASSES, UI_FORM_FN_DESCRIPTION, UI_FORM_FN_DISABLED, UI_FORM_FN_HIDDEN, UI_FORM_FN_HINT, UI_FORM_FN_LABEL, UI_FORM_FN_OPTIONS, UI_FORM_FN_PLACEHOLDER, UI_FORM_FN_PREFIX, UI_FORM_FN_READONLY, UI_FORM_FN_STYLES, UI_FORM_FN_SUBMIT_DISABLED, UI_FORM_FN_SUBMIT_TEXT, UI_FORM_FN_TITLE, UI_FORM_FN_VALUE, UI_FORM_GRID_COL_SPAN, UI_FORM_GRID_ROW_SPAN, UI_FORM_HIDDEN, UI_FORM_HINT, UI_FORM_LABEL_SINGULAR, UI_FORM_OPTIONS, UI_FORM_ORDER, UI_FORM_PLACEHOLDER, UI_FORM_PREFIX, UI_FORM_PREFIX_ICON, UI_FORM_PREFIX_REF, UI_FORM_STYLES, UI_FORM_SUBMIT_TEXT, UI_FORM_SUFFIX, UI_FORM_SUFFIX_ICON, UI_FORM_SUFFIX_REF, UI_FORM_TYPE, UI_FORM_VALIDATE, UI_TABLE_ATTR, UI_TABLE_CLASSES, UI_TABLE_COMPONENT, UI_TABLE_EXCLUDE, UI_TABLE_FN_ATTR, UI_TABLE_FN_CLASSES, UI_TABLE_FN_PREFIX, UI_TABLE_FN_STYLES, UI_TABLE_ORDER, UI_TABLE_SELECT_WITH, UI_TABLE_STYLES, UI_TABLE_TYPE, UI_TABLE_WIDTH, UI_TYPE, ValueHelpClient, WF_ACTION_WITH_DATA, applyFormChanges, asArray, buildDescendantErrorCounts, buildFormDiff, buildFormRebase, buildGridClasses, buildUnionVariants, collectDirtyPaths, createFieldValidator, createFormData, createFormDef, createFormValueResolver, createTableDef, deepClone, deepEqual, defaultResolver, deleteByPath, detectUnionVariant, enforceScale, extractLiteralOptions, extractMeasurement, extractValueHelp, formatDecimalForDisplay, getByPath, getColumn, getCurrencyDecimals, getCurrencyDisplayParts, getDecimalSeparator, getDeclaredFormActions, getDefaultClientFactory, getDefaultValidatorPlugins, getFieldMeta, getFilterableColumns, getFormValidator, getMetaEntry, getResolver, getSortableColumns, getThousandsSeparator, groupInteger, hasComputedAnnotations, isArrayField, isObjectField, isPathDirty, isPureLiteralUnion, isTupleField, isUnionField, iteratePathAncestors, joinDecimalString, mergeErrorMaps, optKey, optLabel, parseColSpan, parseDecimalInput, parseRowSpan, parseStaticAttrs, parseStaticOptions, resetDefaultClientFactory, resetMetaCache, resetValueHelpCache, resolveAttrs, resolveFieldProp, resolveFormProp, resolveGridSpec, resolveOptions, resolveSingularLabel, resolveStatic, resolveValueHelp, setByPath, setDefaultClientFactory, setDefaultValidatorPlugins, setResolver, splitDecimalString, str, unionVariantChanged, valueHelpDictPaths };
package/dist/plugin.cjs CHANGED
@@ -344,8 +344,8 @@ const uiAnnotations = { ui: {
344
344
  description: "Component name from the table components registry"
345
345
  }
346
346
  }),
347
- hidden: new _atscript_core.AnnotationSpec({
348
- description: "Hide this column by default in the table.",
347
+ exclude: new _atscript_core.AnnotationSpec({
348
+ description: "Completely removes this field from the table — not displayable, filterable, or sortable, and not shown in the config dialog. The field stays in the type and remains a valid `@ui.table.selectWith` target so its data can still be fetched for custom cells.",
349
349
  nodeType: ["prop", "type"]
350
350
  }),
351
351
  attr: new _atscript_core.AnnotationSpec({
package/dist/plugin.mjs CHANGED
@@ -344,8 +344,8 @@ const uiAnnotations = { ui: {
344
344
  description: "Component name from the table components registry"
345
345
  }
346
346
  }),
347
- hidden: new AnnotationSpec({
348
- description: "Hide this column by default in the table.",
347
+ exclude: new AnnotationSpec({
348
+ description: "Completely removes this field from the table — not displayable, filterable, or sortable, and not shown in the config dialog. The field stays in the type and remains a valid `@ui.table.selectWith` target so its data can still be fetched for custom cells.",
349
349
  nodeType: ["prop", "type"]
350
350
  }),
351
351
  attr: new AnnotationSpec({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atscript/ui",
3
- "version": "0.1.103",
3
+ "version": "0.1.104",
4
4
  "description": "Framework-agnostic runtime for form and table definitions from atscript annotated types",
5
5
  "keywords": [
6
6
  "annotations",
@@ -47,11 +47,11 @@
47
47
  "access": "public"
48
48
  },
49
49
  "dependencies": {
50
- "@atscript/db-client": "^0.1.107"
50
+ "@atscript/db-client": "^0.1.108"
51
51
  },
52
52
  "devDependencies": {
53
53
  "@atscript/core": "^0.1.77",
54
- "@atscript/db": "^0.1.107",
54
+ "@atscript/db": "^0.1.108",
55
55
  "@atscript/typescript": "^0.1.77",
56
56
  "unplugin-atscript": "^0.1.77",
57
57
  "vitest": "npm:@voidzero-dev/vite-plus-test@0.1.14"