@campfire-interactive/master-data-client 5.17.0 → 5.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  import { HttpTransport } from './http';
2
- import type { AttributeDefinition, CreateAttributeDefinitionInput, UpdateAttributeDefinitionInput, ListAttributeDefinitionsOptions, SingleResponse, ListResponse, CreateResponse, BulkArchiveResult } from './types';
2
+ import type { AttributeDefinition, CreateAttributeDefinitionInput, UpdateAttributeDefinitionInput, ListAttributeDefinitionsOptions, SingleResponse, ListResponse, CreateResponse, BulkArchiveResult, AttributeValuesTemplate, AttributeDefinitionImportReport } from './types';
3
3
  export declare class AttributeDefinitionResource {
4
4
  private readonly http;
5
5
  private readonly basePath;
@@ -25,6 +25,17 @@ export declare class AttributeDefinitionResource {
25
25
  archive(id: string): Promise<void>;
26
26
  restore(id: string): Promise<void>;
27
27
  batchArchive(ids: string[]): Promise<SingleResponse<BulkArchiveResult>>;
28
+ /** The workbook for uploading definitions: an Attributes sheet (one row per
29
+ * definition, any entity type) and a Values sheet (one row per value of a
30
+ * select attribute). Platform owner. */
31
+ template(): Promise<SingleResponse<AttributeValuesTemplate>>;
32
+ /** Check a filled-in definitions workbook (base64), or with `commit: true`
33
+ * create what it describes. A commit writes nothing unless every row
34
+ * passes; it creates, never edits — a key that already exists is an error.
35
+ * Platform owner. */
36
+ importFile(fileContent: string, options?: {
37
+ commit?: boolean;
38
+ }): Promise<SingleResponse<AttributeDefinitionImportReport>>;
28
39
  /** Switch one STANDARD attribute (a default definition) on or off for the
29
40
  * calling tenant. Tenant owner/admin. Not available on
30
41
  * `defaultAttributes.definitions`, where it has no meaning. */
@@ -1 +1 @@
1
- {"version":3,"file":"attributeDefinitions.d.ts","sourceRoot":"","sources":["../src/attributeDefinitions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,KAAK,EACV,mBAAmB,EACnB,8BAA8B,EAC9B,8BAA8B,EAC9B,+BAA+B,EAC/B,cAAc,EACd,YAAY,EACZ,cAAc,EACd,iBAAiB,EAClB,MAAM,SAAS,CAAC;AAQjB,qBAAa,2BAA2B;IAIpC,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAJ3B;4FACwF;gBAErE,IAAI,EAAE,aAAa,EACnB,QAAQ,GAAE,MAAoC;IAG3D,IAAI,CAAC,OAAO,EAAE,+BAA+B,GAAG,OAAO,CAAC,YAAY,CAAC,mBAAmB,CAAC,CAAC;IAuBhG;;;;;;;0CAOsC;IAChC,GAAG,CACP,EAAE,EAAE,MAAM,EACV,OAAO,GAAE;QAAE,cAAc,CAAC,EAAE,OAAO,CAAC;QAAC,eAAe,CAAC,EAAE,OAAO,CAAA;KAAO,GACpE,OAAO,CAAC,cAAc,CAAC,mBAAmB,CAAC,CAAC;IAczC,MAAM,CACV,KAAK,EAAE,8BAA8B,EACrC,cAAc,CAAC,EAAE,MAAM,GACtB,OAAO,CAAC,cAAc,CAAC,mBAAmB,CAAC,CAAC;IASzC,MAAM,CACV,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,8BAA8B,EACrC,cAAc,CAAC,EAAE,MAAM,GACtB,OAAO,CAAC,cAAc,CAAC,mBAAmB,CAAC,CAAC;IAS/C,mFAAmF;IAC7E,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOlC,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOlC,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,cAAc,CAAC,iBAAiB,CAAC,CAAC;IAQ7E;;oEAEgE;IAC1D,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,cAAc,CAAC,mBAAmB,CAAC,CAAC;IAQ5F;;uCAEmC;IAC7B,WAAW,CACf,EAAE,EAAE,MAAM,EACV,QAAQ,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GACrD,OAAO,CAAC,cAAc,CAAC,mBAAmB,CAAC,CAAC;CAOhD"}
1
+ {"version":3,"file":"attributeDefinitions.d.ts","sourceRoot":"","sources":["../src/attributeDefinitions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,KAAK,EACV,mBAAmB,EACnB,8BAA8B,EAC9B,8BAA8B,EAC9B,+BAA+B,EAC/B,cAAc,EACd,YAAY,EACZ,cAAc,EACd,iBAAiB,EACjB,uBAAuB,EACvB,+BAA+B,EAChC,MAAM,SAAS,CAAC;AAQjB,qBAAa,2BAA2B;IAIpC,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAJ3B;4FACwF;gBAErE,IAAI,EAAE,aAAa,EACnB,QAAQ,GAAE,MAAoC;IAG3D,IAAI,CAAC,OAAO,EAAE,+BAA+B,GAAG,OAAO,CAAC,YAAY,CAAC,mBAAmB,CAAC,CAAC;IAuBhG;;;;;;;0CAOsC;IAChC,GAAG,CACP,EAAE,EAAE,MAAM,EACV,OAAO,GAAE;QAAE,cAAc,CAAC,EAAE,OAAO,CAAC;QAAC,eAAe,CAAC,EAAE,OAAO,CAAA;KAAO,GACpE,OAAO,CAAC,cAAc,CAAC,mBAAmB,CAAC,CAAC;IAczC,MAAM,CACV,KAAK,EAAE,8BAA8B,EACrC,cAAc,CAAC,EAAE,MAAM,GACtB,OAAO,CAAC,cAAc,CAAC,mBAAmB,CAAC,CAAC;IASzC,MAAM,CACV,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,8BAA8B,EACrC,cAAc,CAAC,EAAE,MAAM,GACtB,OAAO,CAAC,cAAc,CAAC,mBAAmB,CAAC,CAAC;IAS/C,mFAAmF;IAC7E,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOlC,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOlC,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,cAAc,CAAC,iBAAiB,CAAC,CAAC;IAQ7E;;6CAEyC;IACnC,QAAQ,IAAI,OAAO,CAAC,cAAc,CAAC,uBAAuB,CAAC,CAAC;IAOlE;;;0BAGsB;IAChB,UAAU,CACd,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,OAAO,CAAA;KAAO,GACjC,OAAO,CAAC,cAAc,CAAC,+BAA+B,CAAC,CAAC;IAQ3D;;oEAEgE;IAC1D,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,cAAc,CAAC,mBAAmB,CAAC,CAAC;IAQ5F;;uCAEmC;IAC7B,WAAW,CACf,EAAE,EAAE,MAAM,EACV,QAAQ,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GACrD,OAAO,CAAC,cAAc,CAAC,mBAAmB,CAAC,CAAC;CAOhD"}
@@ -95,6 +95,26 @@ class AttributeDefinitionResource {
95
95
  body: { ids },
96
96
  });
97
97
  }
98
+ /** The workbook for uploading definitions: an Attributes sheet (one row per
99
+ * definition, any entity type) and a Values sheet (one row per value of a
100
+ * select attribute). Platform owner. */
101
+ async template() {
102
+ return this.http.request({
103
+ method: 'GET',
104
+ path: `${this.basePath}/template`,
105
+ });
106
+ }
107
+ /** Check a filled-in definitions workbook (base64), or with `commit: true`
108
+ * create what it describes. A commit writes nothing unless every row
109
+ * passes; it creates, never edits — a key that already exists is an error.
110
+ * Platform owner. */
111
+ async importFile(fileContent, options = {}) {
112
+ return this.http.request({
113
+ method: 'POST',
114
+ path: `${this.basePath}/import`,
115
+ body: { fileContent, commit: options.commit === true },
116
+ });
117
+ }
98
118
  /** Switch one STANDARD attribute (a default definition) on or off for the
99
119
  * calling tenant. Tenant owner/admin. Not available on
100
120
  * `defaultAttributes.definitions`, where it has no meaning. */
@@ -1 +1 @@
1
- {"version":3,"file":"attributeDefinitions.js","sourceRoot":"","sources":["../src/attributeDefinitions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"attributeDefinitions.js","sourceRoot":"","sources":["../src/attributeDefinitions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,157 @@
1
+ /**
2
+ * Picking a value out of a hierarchy from ANY level, not only the top.
3
+ *
4
+ * A hierarchy is OEM > Program > Platform, and the picker used to insist you
5
+ * start at OEM: every level below the chosen one was disabled and listed
6
+ * nothing. Someone who knows the Platform had to remember which OEM it sits
7
+ * under and walk down to it — the app asking the user for the answer it already
8
+ * holds.
9
+ *
10
+ * Nothing about the DATA required that. The bag stores ONE option value and the
11
+ * ancestors are derived by walking `parentId` upward, so a directly-picked
12
+ * Platform already implies its Program and OEM. Only the UI forbade it.
13
+ *
14
+ * The model here is one selected node. Every level lists the nodes CONSISTENT
15
+ * with it — its ancestors, itself, its descendants — and every node at that
16
+ * level when nothing is selected yet. So picking a Platform back-fills the two
17
+ * above it, and picking an OEM narrows the two below without forcing an order.
18
+ *
19
+ * ## Why these functions take an INDEX
20
+ *
21
+ * The rules above were first written against a plain array, which meant every
22
+ * parent lookup was a linear scan: `ancestryOf` scanned once per level climbed,
23
+ * and the descendants filter called it once per candidate node. On a 52,488-node
24
+ * tree one render of the picker spent ~490 ms here — and it paid that on EVERY
25
+ * render, because an attribute form re-renders every field when any one of
26
+ * them is typed in.
27
+ *
28
+ * So the callers build an `OptionIndex` once (memoized on the definition) and
29
+ * pass it in. Every function still accepts a bare array and indexes it on the
30
+ * spot, which keeps the unit tests — and any incidental caller — working
31
+ * unchanged. The RULES below are untouched; only how they are computed moved.
32
+ * The test suite is the proof: it was not edited for this.
33
+ */
34
+ export interface HierarchyNode {
35
+ id: string;
36
+ value: string;
37
+ label: string;
38
+ level: number;
39
+ parentId: string | null;
40
+ }
41
+ /**
42
+ * The lookups every rule below needs, built once in O(n).
43
+ *
44
+ * `childrenByParent` keys roots under `null`, which a Map handles natively.
45
+ * `byValue` exists because the selected node is found by its STORED value (the
46
+ * bag holds a value, not an id) and that lookup used to be a scan too.
47
+ */
48
+ export interface OptionIndex {
49
+ readonly nodes: readonly HierarchyNode[];
50
+ readonly byId: ReadonlyMap<string, HierarchyNode>;
51
+ readonly byValue: ReadonlyMap<string, HierarchyNode>;
52
+ readonly byLevel: readonly (readonly HierarchyNode[])[];
53
+ readonly childrenByParent: ReadonlyMap<string | null, readonly HierarchyNode[]>;
54
+ readonly maxLevel: number;
55
+ }
56
+ /** Everything the rules need, in one pass. Order within each bucket is the
57
+ * source array's order, which is what the registry curated. */
58
+ export declare function buildOptionIndex(nodes: readonly HierarchyNode[]): OptionIndex;
59
+ /** Either form a caller may hold. Arrays are indexed on the spot. */
60
+ export type OptionSource = readonly HierarchyNode[] | OptionIndex;
61
+ /** The chain from a node up to its root, indexed by level. */
62
+ export declare function ancestryOf(src: OptionSource, node: HierarchyNode | undefined): (HierarchyNode | undefined)[];
63
+ /**
64
+ * What one level should offer, given the whole option list and the current
65
+ * selection.
66
+ *
67
+ * With nothing selected, every node at that level — which is what makes a
68
+ * bottom-up pick possible at all. With a selection, the nodes on its own branch:
69
+ * above it, its ancestors (so the level reads as filled in); below it, its
70
+ * descendants (so the next choice is narrowed); at its own level, its siblings
71
+ * under the same parent, so a wrong turn is one click to correct rather than a
72
+ * reset.
73
+ */
74
+ export declare function optionsForLevel(src: OptionSource, selected: HierarchyNode | undefined, level: number): HierarchyNode[];
75
+ /**
76
+ * The node selected after picking `pickedId`.
77
+ *
78
+ * Re-affirming a node the selection already implies is a no-op: clicking the
79
+ * OEM that is already showing must not throw away the Platform beneath it,
80
+ * which is what setting the selection to the picked node every time would do.
81
+ * Picking anything else moves the selection there, because a node off the
82
+ * current branch invalidates what sat below.
83
+ *
84
+ * Returns the NODE, not just its value. Values repeat across branches (an
85
+ * "Ownership" level of "100" under every division), so a value alone cannot
86
+ * say which node was picked — see `resolveSelected`.
87
+ */
88
+ export declare function nodeAfterPick(src: OptionSource, selected: HierarchyNode | undefined, pickedId: string): HierarchyNode | undefined;
89
+ /** The stored value after picking `pickedId`. See `nodeAfterPick`. */
90
+ export declare function valueAfterPick(src: OptionSource, selected: HierarchyNode | undefined, pickedId: string): string;
91
+ /**
92
+ * The node selected after CLEARING `level`: the ancestor above it, or nothing
93
+ * at the top. Clearing a level cannot keep a selection beneath it — that node
94
+ * is the very thing being cleared.
95
+ */
96
+ export declare function nodeAfterClear(src: OptionSource, selected: HierarchyNode | undefined, level: number): HierarchyNode | undefined;
97
+ /** The stored value after clearing `level`. See `nodeAfterClear`. */
98
+ export declare function valueAfterClear(src: OptionSource, selected: HierarchyNode | undefined, level: number): string;
99
+ /**
100
+ * Walk down from `node` while there is exactly one way to go.
101
+ *
102
+ * A level with a single option under the current pick is not a question — the
103
+ * answer is already fixed — so asking the user to open a dropdown and click
104
+ * the only row in it is busywork. Descending stops at the first node with two
105
+ * or more children, or at a leaf.
106
+ *
107
+ * Only a PICK descends, never a clear: clearing a level and having it refill
108
+ * itself from its only child would make that level impossible to empty.
109
+ * Cycle-guarded like every other walk here.
110
+ */
111
+ export declare function descendOnlyChildren(src: OptionSource, node: HierarchyNode | undefined): HierarchyNode | undefined;
112
+ /**
113
+ * Which node a stored value stands for, preferring the node the user picked.
114
+ *
115
+ * The bag stores an option VALUE, and a value can sit on several branches:
116
+ * Ownership "100" under every Country, say. `byValue` answers with the first
117
+ * such node, so resolving a fresh pick through it alone moved the selection
118
+ * to a different branch — the levels above jumped to some other Division and
119
+ * Country the moment "100" was chosen.
120
+ *
121
+ * So the picker remembers the id it just chose and asks for it by id first.
122
+ * The remembered id is only trusted while it still carries the stored value;
123
+ * once the value changes from outside (a prefill, a reset), resolution falls
124
+ * back to `byValue` as before.
125
+ */
126
+ export declare function resolveSelected(src: OptionSource, value: unknown, preferredId: string | null | undefined): HierarchyNode | undefined;
127
+ /**
128
+ * How to label an option so two nodes that read alike can be told apart.
129
+ *
130
+ * Option values are NOT unique across a tree — "Explorer" can sit under Ford
131
+ * and under GM, and nothing in the schema forbids it. That was survivable while
132
+ * the picker was strictly top-down, because the parent was already chosen by
133
+ * the time you saw the child. Choosing a deep node directly is exactly when two
134
+ * identical labels appear side by side, so the ambiguous ones carry their
135
+ * parent.
136
+ *
137
+ * Only the ambiguous ones: appending a parent to every row would be noise on
138
+ * the overwhelming majority of lists, where labels are already distinct.
139
+ *
140
+ * Ambiguity is judged against `shown`, the rows actually offered — so a picker
141
+ * that renders a capped window disambiguates what the user can see, which is
142
+ * the only set the question means anything for.
143
+ *
144
+ * Rendering a whole level calls `labelsFor` instead: this is O(shown) per row
145
+ * and was the single most expensive thing in the picker before the rows were
146
+ * capped (46,080 rows meant 2.1 billion comparisons).
147
+ */
148
+ export declare function labelFor(src: OptionSource, shown: readonly HierarchyNode[], node: HierarchyNode): string;
149
+ /**
150
+ * `labelFor` for a whole set of rows, in one pass instead of one pass per row.
151
+ *
152
+ * Counts each label once, then resolves only the ambiguous rows' parents.
153
+ * Identical output to calling `labelFor` per row; the difference is O(shown)
154
+ * against O(shown squared).
155
+ */
156
+ export declare function labelsFor(src: OptionSource, shown: readonly HierarchyNode[]): Map<string, string>;
157
+ //# sourceMappingURL=attributeHierarchy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attributeHierarchy.d.ts","sourceRoot":"","sources":["../src/attributeHierarchy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,296 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildOptionIndex = buildOptionIndex;
4
+ exports.ancestryOf = ancestryOf;
5
+ exports.optionsForLevel = optionsForLevel;
6
+ exports.nodeAfterPick = nodeAfterPick;
7
+ exports.valueAfterPick = valueAfterPick;
8
+ exports.nodeAfterClear = nodeAfterClear;
9
+ exports.valueAfterClear = valueAfterClear;
10
+ exports.descendOnlyChildren = descendOnlyChildren;
11
+ exports.resolveSelected = resolveSelected;
12
+ exports.labelFor = labelFor;
13
+ exports.labelsFor = labelsFor;
14
+ /** Everything the rules need, in one pass. Order within each bucket is the
15
+ * source array's order, which is what the registry curated. */
16
+ function buildOptionIndex(nodes) {
17
+ const byId = new Map();
18
+ const byValue = new Map();
19
+ const byLevel = [];
20
+ const childrenByParent = new Map();
21
+ let maxLevel = 0;
22
+ for (const n of nodes) {
23
+ byId.set(n.id, n);
24
+ // First wins: two nodes CAN share a value across branches, and the old
25
+ // `find` returned the first, so this has to as well.
26
+ if (!byValue.has(n.value))
27
+ byValue.set(n.value, n);
28
+ (byLevel[n.level] ??= []).push(n);
29
+ const bucket = childrenByParent.get(n.parentId);
30
+ if (bucket)
31
+ bucket.push(n);
32
+ else
33
+ childrenByParent.set(n.parentId, [n]);
34
+ if (n.level > maxLevel)
35
+ maxLevel = n.level;
36
+ }
37
+ // A node whose CHILD carries the same value resolves to the child.
38
+ //
39
+ // The bag stores one option VALUE, and values are not unique down a branch:
40
+ // Magna's Product Hierarchy ends "… > ALL-WHEEL DRIVE > ALL-WHEEL DRIVE",
41
+ // the last level repeating the one above it. Taking the first match made the
42
+ // deepest node unreachable — picking it stored a string the lookup then
43
+ // resolved back to its ancestor, so the level below stayed empty and the row
44
+ // read as un-clickable. Nothing was broken except the display: the value
45
+ // written was already correct.
46
+ //
47
+ // Descending is free rather than a guess. The two nodes are indistinguishable
48
+ // to everything downstream (same stored string, so the same filters and the
49
+ // same reports), and the deeper reading is the more specific one, which
50
+ // implies its ancestors anyway. Only a value repeated PARENT-TO-CHILD moves;
51
+ // the same value on two different branches still takes the first, because
52
+ // there the choice would be a real guess.
53
+ for (const [value, node] of byValue) {
54
+ let cur = node;
55
+ const seen = new Set([cur.id]);
56
+ for (;;) {
57
+ const sameChild = (childrenByParent.get(cur.id) ?? [])
58
+ .find((c) => c.value === value && !seen.has(c.id));
59
+ if (!sameChild)
60
+ break;
61
+ seen.add(sameChild.id);
62
+ cur = sameChild;
63
+ }
64
+ if (cur !== node)
65
+ byValue.set(value, cur);
66
+ }
67
+ return { nodes, byId, byValue, byLevel, childrenByParent, maxLevel };
68
+ }
69
+ function toIndex(src) {
70
+ return Array.isArray(src) ? buildOptionIndex(src) : src;
71
+ }
72
+ /** Children of one parent, restricted to a level. The restriction is belt and
73
+ * braces: children of a node are all at one level in any well-formed tree,
74
+ * and the array version filtered by level, so this matches it exactly. */
75
+ function childrenAtLevel(ix, parentId, level) {
76
+ return (ix.childrenByParent.get(parentId) ?? []).filter((o) => o.level === level);
77
+ }
78
+ /** The ids of every node beneath `root`, exclusive. Iterative and cycle-guarded
79
+ * — the tree is parent-pointer only with no database constraint preventing a
80
+ * loop, and a bad row must not hang the form.
81
+ *
82
+ * Returns a SET rather than a list because the caller filters the level's own
83
+ * array by it: walking the subtree yields traversal order, and the rows have
84
+ * to come back in the registry's curated order, which is the order the source
85
+ * array is in. (The unit suite caught exactly this.) */
86
+ function subtreeIdsOf(ix, root) {
87
+ const seen = new Set([root.id]);
88
+ const stack = [root];
89
+ while (stack.length > 0) {
90
+ const node = stack.pop();
91
+ for (const child of ix.childrenByParent.get(node.id) ?? []) {
92
+ if (seen.has(child.id))
93
+ continue;
94
+ seen.add(child.id);
95
+ stack.push(child);
96
+ }
97
+ }
98
+ seen.delete(root.id);
99
+ return seen;
100
+ }
101
+ /** The chain from a node up to its root, indexed by level. */
102
+ function ancestryOf(src, node) {
103
+ const ix = toIndex(src);
104
+ const byLevel = [];
105
+ let cur = node;
106
+ const seen = new Set();
107
+ // Guarded against a cycle: see subtreeIdsOf.
108
+ while (cur && !seen.has(cur.id)) {
109
+ seen.add(cur.id);
110
+ byLevel[cur.level] = cur;
111
+ cur = cur.parentId ? ix.byId.get(cur.parentId) : undefined;
112
+ }
113
+ return byLevel;
114
+ }
115
+ /** True when `node` is `maybeAncestor` or sits beneath it. */
116
+ function isSelfOrDescendantOf(ix, node, maybeAncestor) {
117
+ return ancestryOf(ix, node).some((a) => a?.id === maybeAncestor.id);
118
+ }
119
+ /**
120
+ * What one level should offer, given the whole option list and the current
121
+ * selection.
122
+ *
123
+ * With nothing selected, every node at that level — which is what makes a
124
+ * bottom-up pick possible at all. With a selection, the nodes on its own branch:
125
+ * above it, its ancestors (so the level reads as filled in); below it, its
126
+ * descendants (so the next choice is narrowed); at its own level, its siblings
127
+ * under the same parent, so a wrong turn is one click to correct rather than a
128
+ * reset.
129
+ */
130
+ function optionsForLevel(src, selected, level) {
131
+ const ix = toIndex(src);
132
+ const atLevel = ix.byLevel[level] ?? [];
133
+ if (!selected)
134
+ return [...atLevel];
135
+ if (level < selected.level) {
136
+ // Shallower: its own ancestor, plus that ancestor's siblings — changing one
137
+ // has to remain possible, and listing only the ancestor would make the
138
+ // control read as frozen.
139
+ const ancestor = ancestryOf(ix, selected)[level];
140
+ if (!ancestor)
141
+ return [...atLevel];
142
+ return childrenAtLevel(ix, ancestor.parentId, level);
143
+ }
144
+ if (level === selected.level) {
145
+ return childrenAtLevel(ix, selected.parentId, level);
146
+ }
147
+ // Deeper: walk DOWN the selection's subtree once and keep the level's own
148
+ // rows that fall inside it. Same answer and same order, at the cost of the
149
+ // subtree plus one pass, rather than (rows at this level x depth x array).
150
+ const inSubtree = subtreeIdsOf(ix, selected);
151
+ return atLevel.filter((o) => inSubtree.has(o.id));
152
+ }
153
+ /**
154
+ * The node selected after picking `pickedId`.
155
+ *
156
+ * Re-affirming a node the selection already implies is a no-op: clicking the
157
+ * OEM that is already showing must not throw away the Platform beneath it,
158
+ * which is what setting the selection to the picked node every time would do.
159
+ * Picking anything else moves the selection there, because a node off the
160
+ * current branch invalidates what sat below.
161
+ *
162
+ * Returns the NODE, not just its value. Values repeat across branches (an
163
+ * "Ownership" level of "100" under every division), so a value alone cannot
164
+ * say which node was picked — see `resolveSelected`.
165
+ */
166
+ function nodeAfterPick(src, selected, pickedId) {
167
+ const ix = toIndex(src);
168
+ const picked = ix.byId.get(pickedId);
169
+ if (!picked)
170
+ return undefined;
171
+ if (selected && isSelfOrDescendantOf(ix, selected, picked)) {
172
+ // Already on this branch, at or below the pick — keep the deeper answer.
173
+ return selected;
174
+ }
175
+ return picked;
176
+ }
177
+ /** The stored value after picking `pickedId`. See `nodeAfterPick`. */
178
+ function valueAfterPick(src, selected, pickedId) {
179
+ return nodeAfterPick(src, selected, pickedId)?.value ?? '';
180
+ }
181
+ /**
182
+ * The node selected after CLEARING `level`: the ancestor above it, or nothing
183
+ * at the top. Clearing a level cannot keep a selection beneath it — that node
184
+ * is the very thing being cleared.
185
+ */
186
+ function nodeAfterClear(src, selected, level) {
187
+ if (!selected || level <= 0)
188
+ return undefined;
189
+ return ancestryOf(src, selected)[level - 1];
190
+ }
191
+ /** The stored value after clearing `level`. See `nodeAfterClear`. */
192
+ function valueAfterClear(src, selected, level) {
193
+ return nodeAfterClear(src, selected, level)?.value ?? '';
194
+ }
195
+ /**
196
+ * Walk down from `node` while there is exactly one way to go.
197
+ *
198
+ * A level with a single option under the current pick is not a question — the
199
+ * answer is already fixed — so asking the user to open a dropdown and click
200
+ * the only row in it is busywork. Descending stops at the first node with two
201
+ * or more children, or at a leaf.
202
+ *
203
+ * Only a PICK descends, never a clear: clearing a level and having it refill
204
+ * itself from its only child would make that level impossible to empty.
205
+ * Cycle-guarded like every other walk here.
206
+ */
207
+ function descendOnlyChildren(src, node) {
208
+ if (!node)
209
+ return undefined;
210
+ const ix = toIndex(src);
211
+ let cur = node;
212
+ const seen = new Set([cur.id]);
213
+ for (;;) {
214
+ const children = ix.childrenByParent.get(cur.id) ?? [];
215
+ if (children.length !== 1 || seen.has(children[0].id))
216
+ return cur;
217
+ cur = children[0];
218
+ seen.add(cur.id);
219
+ }
220
+ }
221
+ /**
222
+ * Which node a stored value stands for, preferring the node the user picked.
223
+ *
224
+ * The bag stores an option VALUE, and a value can sit on several branches:
225
+ * Ownership "100" under every Country, say. `byValue` answers with the first
226
+ * such node, so resolving a fresh pick through it alone moved the selection
227
+ * to a different branch — the levels above jumped to some other Division and
228
+ * Country the moment "100" was chosen.
229
+ *
230
+ * So the picker remembers the id it just chose and asks for it by id first.
231
+ * The remembered id is only trusted while it still carries the stored value;
232
+ * once the value changes from outside (a prefill, a reset), resolution falls
233
+ * back to `byValue` as before.
234
+ */
235
+ function resolveSelected(src, value, preferredId) {
236
+ if (typeof value !== 'string' || value === '')
237
+ return undefined;
238
+ const ix = toIndex(src);
239
+ const preferred = preferredId ? ix.byId.get(preferredId) : undefined;
240
+ if (preferred && preferred.value === value)
241
+ return preferred;
242
+ return ix.byValue.get(value);
243
+ }
244
+ /**
245
+ * How to label an option so two nodes that read alike can be told apart.
246
+ *
247
+ * Option values are NOT unique across a tree — "Explorer" can sit under Ford
248
+ * and under GM, and nothing in the schema forbids it. That was survivable while
249
+ * the picker was strictly top-down, because the parent was already chosen by
250
+ * the time you saw the child. Choosing a deep node directly is exactly when two
251
+ * identical labels appear side by side, so the ambiguous ones carry their
252
+ * parent.
253
+ *
254
+ * Only the ambiguous ones: appending a parent to every row would be noise on
255
+ * the overwhelming majority of lists, where labels are already distinct.
256
+ *
257
+ * Ambiguity is judged against `shown`, the rows actually offered — so a picker
258
+ * that renders a capped window disambiguates what the user can see, which is
259
+ * the only set the question means anything for.
260
+ *
261
+ * Rendering a whole level calls `labelsFor` instead: this is O(shown) per row
262
+ * and was the single most expensive thing in the picker before the rows were
263
+ * capped (46,080 rows meant 2.1 billion comparisons).
264
+ */
265
+ function labelFor(src, shown, node) {
266
+ const duplicated = shown.filter((o) => o.label === node.label).length > 1;
267
+ if (!duplicated)
268
+ return node.label;
269
+ const parent = node.parentId ? toIndex(src).byId.get(node.parentId) : undefined;
270
+ return parent ? `${node.label} (${parent.label})` : node.label;
271
+ }
272
+ /**
273
+ * `labelFor` for a whole set of rows, in one pass instead of one pass per row.
274
+ *
275
+ * Counts each label once, then resolves only the ambiguous rows' parents.
276
+ * Identical output to calling `labelFor` per row; the difference is O(shown)
277
+ * against O(shown squared).
278
+ */
279
+ function labelsFor(src, shown) {
280
+ const counts = new Map();
281
+ for (const o of shown)
282
+ counts.set(o.label, (counts.get(o.label) ?? 0) + 1);
283
+ const out = new Map();
284
+ let ix;
285
+ for (const o of shown) {
286
+ if ((counts.get(o.label) ?? 0) <= 1) {
287
+ out.set(o.id, o.label);
288
+ continue;
289
+ }
290
+ ix ??= toIndex(src);
291
+ const parent = o.parentId ? ix.byId.get(o.parentId) : undefined;
292
+ out.set(o.id, parent ? `${o.label} (${parent.label})` : o.label);
293
+ }
294
+ return out;
295
+ }
296
+ //# sourceMappingURL=attributeHierarchy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attributeHierarchy.js","sourceRoot":"","sources":["../src/attributeHierarchy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * A stored hierarchy value, expanded into the chain it stands for.
3
+ *
4
+ * The bag holds one option value: the node that was picked. Read views showed
5
+ * just that node's label, so "ALL-WHEEL DRIVE" arrived with none of the context
6
+ * that makes it mean anything, and the two other things a reader needs — which
7
+ * branch it sits on, and what each tier of the branch is called — were only
8
+ * visible by opening the editor and reading the dropdowns.
9
+ *
10
+ * The ancestors are derivable (that is the whole premise of storing one value),
11
+ * so this walks them and pairs each with the tier name the definition declares
12
+ * in `validation.levels`.
13
+ */
14
+ import { type HierarchyNode } from './attributeHierarchy';
15
+ export interface HierarchyTier {
16
+ /** The tier's declared name, or null when the definition names no levels. */
17
+ tier: string | null;
18
+ /** The chosen node's label at this tier. */
19
+ label: string;
20
+ /** The tier's index, which is the node's own level. Stable key for a list. */
21
+ level: number;
22
+ }
23
+ interface ChainSource {
24
+ validation?: {
25
+ levels?: string[];
26
+ } | null;
27
+ attributeOptions?: readonly HierarchyNode[] | null;
28
+ }
29
+ /**
30
+ * The chain a stored value stands for, shallowest tier first.
31
+ *
32
+ * `null` when there is nothing better to show than the raw value: no options
33
+ * loaded, an empty value, or a value that names no node (legacy data, or an
34
+ * option archived since it was chosen). Callers fall back to the ordinary
35
+ * single-value formatter in that case rather than rendering an empty list.
36
+ *
37
+ * A single-tier chain also returns null: one row captioned "Level 1" is
38
+ * strictly more furniture than the plain label it replaces.
39
+ */
40
+ export declare function hierarchyChain(def: ChainSource, value: unknown): HierarchyTier[] | null;
41
+ export {};
42
+ //# sourceMappingURL=attributeHierarchyChain.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attributeHierarchyChain.d.ts","sourceRoot":"","sources":["../src/attributeHierarchyChain.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAgC,KAAK,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAExF,MAAM,WAAW,aAAa;IAC5B,6EAA6E;IAC7E,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,4CAA4C;IAC5C,KAAK,EAAE,MAAM,CAAC;IACd,8EAA8E;IAC9E,KAAK,EAAE,MAAM,CAAC;CACf;AAED,UAAU,WAAW;IACnB,UAAU,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,GAAG,IAAI,CAAC;IAC1C,gBAAgB,CAAC,EAAE,SAAS,aAAa,EAAE,GAAG,IAAI,CAAC;CACpD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,OAAO,GAAG,aAAa,EAAE,GAAG,IAAI,CAmBvF"}
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.hierarchyChain = hierarchyChain;
4
+ /**
5
+ * A stored hierarchy value, expanded into the chain it stands for.
6
+ *
7
+ * The bag holds one option value: the node that was picked. Read views showed
8
+ * just that node's label, so "ALL-WHEEL DRIVE" arrived with none of the context
9
+ * that makes it mean anything, and the two other things a reader needs — which
10
+ * branch it sits on, and what each tier of the branch is called — were only
11
+ * visible by opening the editor and reading the dropdowns.
12
+ *
13
+ * The ancestors are derivable (that is the whole premise of storing one value),
14
+ * so this walks them and pairs each with the tier name the definition declares
15
+ * in `validation.levels`.
16
+ */
17
+ const attributeHierarchy_1 = require("./attributeHierarchy");
18
+ /**
19
+ * The chain a stored value stands for, shallowest tier first.
20
+ *
21
+ * `null` when there is nothing better to show than the raw value: no options
22
+ * loaded, an empty value, or a value that names no node (legacy data, or an
23
+ * option archived since it was chosen). Callers fall back to the ordinary
24
+ * single-value formatter in that case rather than rendering an empty list.
25
+ *
26
+ * A single-tier chain also returns null: one row captioned "Level 1" is
27
+ * strictly more furniture than the plain label it replaces.
28
+ */
29
+ function hierarchyChain(def, value) {
30
+ if (typeof value !== 'string' || value.trim() === '')
31
+ return null;
32
+ const options = def.attributeOptions;
33
+ if (!options || options.length === 0)
34
+ return null;
35
+ const index = (0, attributeHierarchy_1.buildOptionIndex)(options);
36
+ const node = index.byValue.get(value);
37
+ if (!node)
38
+ return null;
39
+ const levels = def.validation?.levels ?? [];
40
+ const chain = [];
41
+ (0, attributeHierarchy_1.ancestryOf)(index, node).forEach((ancestor, level) => {
42
+ // ancestryOf is indexed BY LEVEL and is sparse: a tree that starts at
43
+ // level 1, or a gap left by a malformed row, leaves holes to skip.
44
+ if (!ancestor)
45
+ return;
46
+ chain.push({ tier: levels[level]?.trim() || null, label: ancestor.label, level });
47
+ });
48
+ return chain.length > 1 ? chain : null;
49
+ }
50
+ //# sourceMappingURL=attributeHierarchyChain.js.map