ngx-t-forms-types 0.0.30 → 0.0.33

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.
Files changed (63) hide show
  1. package/dist/interfaces/Form/formSubmissionHandleInterface.d.ts +6 -0
  2. package/dist/interfaces/FormBuilder/DefaultEelement.js +76 -0
  3. package/dist/interfaces/FormBuilder/DefaultInputConfigInterface.d.ts +13 -0
  4. package/dist/interfaces/FormBuilder/FormInputKeys.d.ts +6 -0
  5. package/dist/interfaces/FormBuilder/FormInputKeys.js +6 -0
  6. package/dist/interfaces/FormBuilder/inputConfig/ElementEditConfig.js +188 -3
  7. package/dist/interfaces/formInput/APIDataFetchingConfigurationInterface.d.ts +34 -0
  8. package/dist/interfaces/formInput/BasicFormInputInterface.d.ts +1 -0
  9. package/dist/interfaces/formInput/BasicFormInputInterface.js +1 -0
  10. package/dist/interfaces/formInput/IMscoaAccount.d.ts +19 -3
  11. package/dist/interfaces/formInput/ISelectInputInterface.d.ts +12 -0
  12. package/dist/interfaces/formInput/MultipleInterface.d.ts +10 -0
  13. package/dist/interfaces/formInput/WorkflowDocumentPicker.d.ts +2 -0
  14. package/dist/schemas/FormInputSchema.js +41 -2
  15. package/dist/schemas/MatOptionsSchema.js +7 -0
  16. package/dist/schemas/MscoaConfigSchema.js +1 -0
  17. package/dist/schemas/index.d.ts +2 -1
  18. package/dist/schemas/index.js +2 -1
  19. package/dist/skillet/authoring-rules.d.ts +41 -0
  20. package/dist/skillet/authoring-rules.js +61 -0
  21. package/dist/skillet/extra-members.d.ts +143 -0
  22. package/dist/skillet/extra-members.js +79 -0
  23. package/dist/skillet/form.d.ts +170 -0
  24. package/dist/skillet/form.js +139 -0
  25. package/dist/skillet/index.d.ts +43 -0
  26. package/dist/skillet/index.js +46 -0
  27. package/dist/skillet/input-members.d.ts +375 -0
  28. package/dist/skillet/input-members.js +186 -0
  29. package/dist/skillet/internal/coupling.d.ts +63 -0
  30. package/dist/skillet/internal/coupling.js +69 -0
  31. package/dist/skillet/internal/editor-guidance.d.ts +64 -0
  32. package/dist/skillet/internal/editor-guidance.js +291 -0
  33. package/dist/skillet/members/calculated-field.d.ts +155 -0
  34. package/dist/skillet/members/calculated-field.js +105 -0
  35. package/dist/skillet/members/conditional.d.ts +69 -0
  36. package/dist/skillet/members/conditional.js +16 -0
  37. package/dist/skillet/members/document-picker.d.ts +182 -0
  38. package/dist/skillet/members/document-picker.js +111 -0
  39. package/dist/skillet/members/mat-options.d.ts +187 -0
  40. package/dist/skillet/members/mat-options.js +92 -0
  41. package/dist/skillet/members/mscoa.d.ts +182 -0
  42. package/dist/skillet/members/mscoa.js +129 -0
  43. package/dist/skillet/members/pagination.d.ts +59 -0
  44. package/dist/skillet/members/pagination.js +16 -0
  45. package/dist/skillet/members/table.d.ts +178 -0
  46. package/dist/skillet/members/table.js +105 -0
  47. package/dist/skillet/members/validators.d.ts +197 -0
  48. package/dist/skillet/members/validators.js +74 -0
  49. package/dist/skillet/members/value.d.ts +154 -0
  50. package/dist/skillet/members/value.js +97 -0
  51. package/dist/skillet/shared-types.d.ts +57 -0
  52. package/dist/skillet/shared-types.js +69 -0
  53. package/dist/skillet/tests/house-rules.spec.d.ts +1 -0
  54. package/dist/skillet/tests/house-rules.spec.js +162 -0
  55. package/dist/skillet/tests/skillet-coupling.spec.d.ts +1 -0
  56. package/dist/skillet/tests/skillet-coupling.spec.js +191 -0
  57. package/dist/skillet/tests/variant-assembly.spec.d.ts +1 -0
  58. package/dist/skillet/tests/variant-assembly.spec.js +643 -0
  59. package/dist/skillet/variants.d.ts +138 -0
  60. package/dist/skillet/variants.js +536 -0
  61. package/dist/skillet/workflow-context.d.ts +95 -0
  62. package/dist/skillet/workflow-context.js +128 -0
  63. package/package.json +78 -60
@@ -0,0 +1,182 @@
1
+ import { s } from '@hashbrownai/core';
2
+ import type { AccountingBasis, DualCashExclusionMatchMode } from '../../interfaces/formInput/IMscoaAccount.js';
3
+ import { AssertNever } from '../internal/coupling.js';
4
+ /**
5
+ * The `mscoaConfig` member: how a municipal SCOA account picker is set up.
6
+ *
7
+ * One `mscoaSelection` element asks a user to pick accounts out of the chart of
8
+ * accounts, segment by segment. This object says which segments it asks for,
9
+ * how each account is labelled back to them, which accounting basis is being
10
+ * captured, and — for a dual basis — when the cash side is not actually
11
+ * required.
12
+ *
13
+ * ## Why this produces a draft
14
+ *
15
+ * `IScoaInputConfig` requires `inputs: ScoaInnerInput[]`, and a
16
+ * `ScoaInnerInput` is a whole `FormColumnInputs` carrying its own `id` plus a
17
+ * `linkedSegmentId` pointing at a segment row that does not exist until the
18
+ * segments themselves have been assigned ids. Asking a model for that is asking
19
+ * it to invent two sets of identifiers and then cross-reference them, which it
20
+ * will do, plausibly and wrongly. So `inputs` is not offered here at all; the
21
+ * application supplies `inputs: []` when it completes the draft, and the
22
+ * builder's own "additional inputs" editor is where those are authored.
23
+ *
24
+ * The same reasoning retires `id` from segments and from exclusion rules. Both
25
+ * are row keys stamped by the builder's record editors (`uuidv4()` at the point
26
+ * of save), never typed by a human — which is exactly why `id` and `sectionId`
27
+ * sit in `SYSTEM_OWNED_INPUT_MEMBERS` one level up. A model-authored id is at
28
+ * best noise and at worst a collision with a row that already exists.
29
+ *
30
+ * ## Draft-completion obligations
31
+ *
32
+ * Before this output can be handed to `validateMscoaInputConfig`, the caller
33
+ * must:
34
+ *
35
+ * 1. add `inputs: []` — required by `MscoaInputConfigSchema` and deliberately
36
+ * absent here;
37
+ * 2. stamp an `id` on every entry of `segments`, `cashSegments` and
38
+ * `dualCashExclusion.rules`;
39
+ * 3. strip the nulls standing in for absent optional members — Joi rejects a
40
+ * `null` where it declared an optional string.
41
+ *
42
+ * None of the three is optional, and none of them is knowable from a prompt.
43
+ *
44
+ * ## Deliberate narrowing
45
+ *
46
+ * `IScoaInputConfig` also declares `label` and `hint`. `MscoaInputConfigSchema`
47
+ * does not, and a Joi object rejects unknown keys by default — so emitting
48
+ * either would fail the very validation this schema exists to pass. The visible
49
+ * label of the control is the input's own `label` member, one level up. Joi
50
+ * wins, and they are omitted.
51
+ *
52
+ * `accountValueLabel` is narrowed from `string` to the pool the settings panel
53
+ * offers, derived from {@link MSCOA_ACCOUNT_VALUE_LABEL_OPTIONS} at evaluation
54
+ * rather than retyped — the same treatment the enum-backed nodes get, for the
55
+ * same reason: a hand-copied list is a list that drifts.
56
+ *
57
+ * `segment` is asked for on every segment row, although the interface types it
58
+ * `string | undefined`. The builder's segment form declares it
59
+ * `Validators.required`, so a row without one cannot be saved by hand either.
60
+ * Narrower than the interface is the permitted direction; wider is not.
61
+ */
62
+ export interface MscoaSegmentDraft {
63
+ segment: string;
64
+ customSegment: string | null;
65
+ label: string;
66
+ readOnly: boolean;
67
+ singleSelect: boolean;
68
+ additionalAccounts: string[];
69
+ inheritValueFromAccrual: boolean | null;
70
+ vatSelectonActive: boolean | null;
71
+ segmentExtension: boolean;
72
+ }
73
+ export interface MscoaDualCashExclusionRuleDraft {
74
+ pattern: string;
75
+ flags: string | null;
76
+ matchField: string | null;
77
+ segment: string | null;
78
+ description: string | null;
79
+ }
80
+ export interface MscoaDualCashExclusionDraft {
81
+ rules: MscoaDualCashExclusionRuleDraft[] | null;
82
+ matchMode: DualCashExclusionMatchMode | null;
83
+ }
84
+ export interface MscoaConfigDraft {
85
+ segments: MscoaSegmentDraft[];
86
+ cashSegments: MscoaSegmentDraft[] | null;
87
+ accountValueLabel: string;
88
+ accountingBasis: AccountingBasis | null;
89
+ extensionAccountsForSegments: string[];
90
+ showAllSegments: boolean;
91
+ dualCashExclusion: MscoaDualCashExclusionDraft | null;
92
+ }
93
+ /**
94
+ * `DualCashExclusionMatchMode` is a string-union type with no runtime object to
95
+ * read, so its members are restated under {@link exhaustive} rather than
96
+ * derived — drop one and this stops compiling.
97
+ */
98
+ export declare const DUAL_CASH_MATCH_MODE_VALUES: readonly ["any", "all"];
99
+ /**
100
+ * The `mscoaConfig` schema.
101
+ *
102
+ * A plain const, not a factory: nothing in here is chosen from candidates the
103
+ * caller holds. The one member that draws on a fixed pool — `accountValueLabel`
104
+ * — reads that pool out of this package at evaluation.
105
+ *
106
+ * ## Where the editor guidance is attached, and where it is not
107
+ *
108
+ * There is exactly ONE editor row for this whole object, at path
109
+ * `['mscoaConfig']`: a composite `MscoaConfig` editor that replaced eight
110
+ * separate rows (accountingBasis, accountValueLabel, showAllSegments, segments,
111
+ * cashSegments, the two dual-cash members and extensionAccountsForSegments).
112
+ * So {@link describe} is called once, on the object the row actually edits, and
113
+ * the members below carry authored descriptions only.
114
+ *
115
+ * That is a deliberate choice rather than an oversight. Calling
116
+ * `describe(base, 'mscoaConfig')` on each member compiles and would append the
117
+ * same forty-word hint — ending in "Opens a guided setup", which is advice for
118
+ * someone looking at a settings panel — to all seven of them. Layering it once,
119
+ * on the composite, says the same thing in one place.
120
+ */
121
+ export declare const mscoaConfigSchema: s.ObjectType<{
122
+ accountingBasis: import("@hashbrownai/core/src/schema/base").SchemaForUnion<AccountingBasis | null>;
123
+ segments: s.ArrayType<s.ObjectType<{
124
+ segment: s.StringType;
125
+ customSegment: import("@hashbrownai/core/src/schema/base").SchemaForUnion<string | null>;
126
+ label: s.StringType;
127
+ readOnly: s.BooleanType;
128
+ singleSelect: s.BooleanType;
129
+ additionalAccounts: s.ArrayType<s.StringType>;
130
+ inheritValueFromAccrual: import("@hashbrownai/core/src/schema/base").SchemaForUnion<boolean | null>;
131
+ vatSelectonActive: import("@hashbrownai/core/src/schema/base").SchemaForUnion<boolean | null>;
132
+ segmentExtension: s.BooleanType;
133
+ }>>;
134
+ cashSegments: import("@hashbrownai/core/src/schema/base").SchemaForUnion<{
135
+ segment: string;
136
+ customSegment: string | null;
137
+ label: string;
138
+ readOnly: boolean;
139
+ singleSelect: boolean;
140
+ additionalAccounts: string[];
141
+ inheritValueFromAccrual: boolean | null;
142
+ vatSelectonActive: boolean | null;
143
+ segmentExtension: boolean;
144
+ }[] | null>;
145
+ accountValueLabel: s.EnumType<string[]>;
146
+ showAllSegments: s.BooleanType;
147
+ extensionAccountsForSegments: s.ArrayType<s.StringType>;
148
+ dualCashExclusion: import("@hashbrownai/core/src/schema/base").SchemaForUnion<{
149
+ rules: {
150
+ pattern: string;
151
+ flags: string | null;
152
+ matchField: string | null;
153
+ segment: string | null;
154
+ description: string | null;
155
+ }[] | null;
156
+ matchMode: "any" | "all" | null;
157
+ } | null>;
158
+ }>;
159
+ /**
160
+ * The schema produces the draft it declares.
161
+ *
162
+ * Same shape of check as `_NoDraftDrift` in `input-members.ts`, and there for
163
+ * the same reason: the expansion from draft to `IScoaInputConfig` belongs to
164
+ * the caller, but the draft itself stays pinned to the node that generates it.
165
+ * Loosen a member below and this fails here rather than three layers away.
166
+ */
167
+ export type _NoMscoaDraftDrift = AssertNever<[
168
+ s.Infer<typeof mscoaConfigSchema>
169
+ ] extends [MscoaConfigDraft] ? never : 'mscoaConfig'>;
170
+ /**
171
+ * Members of a generated `mscoaConfig` the application must supply before Joi
172
+ * will accept it.
173
+ *
174
+ * `inputs` is the notable one: `MscoaInputConfigSchema` REQUIRES it, and the
175
+ * model is never asked for it, so a draft that is not expanded fails
176
+ * validation rather than merely losing a detail. An empty array satisfies it —
177
+ * the segments' inner inputs are generated from the segments themselves.
178
+ *
179
+ * `dualCashExclusion.rules[].id` is deliberately absent from this list: Joi
180
+ * marks that one optional, so leaving it unset is valid.
181
+ */
182
+ export declare const MSCOA_DRAFT_COMPLETION: readonly ["mscoaConfig.inputs (application supplies; an empty array is valid)", "mscoaConfig.segments[].id", "mscoaConfig.cashSegments[].id"];
@@ -0,0 +1,129 @@
1
+ import { s } from '@hashbrownai/core';
2
+ import { MSCOA_ACCOUNT_VALUE_LABEL_OPTIONS } from '../../interfaces/FormBuilder/inputConfig/ElementEditConfig.js';
3
+ import { exhaustive, optional } from '../internal/coupling.js';
4
+ import { describe, describeIn } from '../internal/editor-guidance.js';
5
+ import { accountingBasisSchema } from '../shared-types.js';
6
+ // --- the account-label pool -------------------------------------------------
7
+ /**
8
+ * The fields an account may be displayed by, read off the option pool the
9
+ * unified MSCOA setup editor renders for `accountValueLabel`.
10
+ */
11
+ const ACCOUNT_VALUE_LABEL_ENTRIES = MSCOA_ACCOUNT_VALUE_LABEL_OPTIONS.map((option) => option.value);
12
+ /** Compares a label to its value ignoring case, spacing and punctuation. */
13
+ const flatten = (text) => text.replace(/[^a-z0-9]/gi, '').toLowerCase();
14
+ /**
15
+ * The handful of entries whose label says something their value does not.
16
+ *
17
+ * `internal/editor-guidance` rejects editor `options` as a description source
18
+ * on the evidence that the labels merely restate the values. That holds for
19
+ * most of this pool too — `SCOAAccount` is labelled "SCOA Account", `SA34B` is
20
+ * labelled "SA34B" — and those are dropped here rather than repeated. What is
21
+ * left is the residue that genuinely disambiguates: `AccountNumber` is the
22
+ * FULL account number and `AccountNumberShortened` is the one an administrator
23
+ * calls "the account number", which is not a distinction a model can recover
24
+ * from the two identifiers alone.
25
+ */
26
+ const ACCOUNT_VALUE_LABEL_GLOSS = MSCOA_ACCOUNT_VALUE_LABEL_OPTIONS.filter((option) => flatten(option.label) !== flatten(option.value))
27
+ .map((option) => `${option.value} is "${option.label}"`)
28
+ .join('; ');
29
+ // --- schema -----------------------------------------------------------------
30
+ /**
31
+ * `DualCashExclusionMatchMode` is a string-union type with no runtime object to
32
+ * read, so its members are restated under {@link exhaustive} rather than
33
+ * derived — drop one and this stops compiling.
34
+ */
35
+ export const DUAL_CASH_MATCH_MODE_VALUES = exhaustive()(['any', 'all']);
36
+ /**
37
+ * One segment row: which slice of the chart of accounts the user picks from,
38
+ * and how that pick behaves.
39
+ *
40
+ * Shared by `segments` and `cashSegments` rather than split into two nodes.
41
+ * Two members are basis-specific (`inheritValueFromAccrual` is meaningless off
42
+ * the cash side, `vatSelectonActive` off the ITEM segment), and Skillet cannot
43
+ * express conditional applicability structurally — so, as with `min` and `max`
44
+ * on the input members, the condition is stated in the description where it can
45
+ * still stop a model setting a flag that cannot mean anything.
46
+ */
47
+ const segmentSchema = s.object('One segment of the chart of accounts the user picks an account from.', {
48
+ segment: s.string('The segment key exactly as the chart of accounts spells it, upper case — ITEM, FUNCTION, FUND, PROJECT, REGION and so on. The real list comes from the loaded account tree, so use the key the municipality actually publishes rather than inventing a plausible one. On an extension row this names the segment being extended.'),
49
+ customSegment: optional(s.string('The upper-cased key an extension row stores its accounts under, derived from the label (a row labelled "Retention" is keyed RETENTION). Leave null on an ordinary segment, which is keyed by its segment key instead.')),
50
+ label: s.string('The heading shown above this row in the account chart, in title case. Name what the user is choosing, not the segment code.'),
51
+ readOnly: s.boolean('Whether the row is displayed but cannot be changed by the user. Set this on a segment whose account is fixed by policy or copied from elsewhere.'),
52
+ singleSelect: s.boolean('True when the row captures one account only. False adds a second account to every row in this table, so the user picks a debit and a credit pair. ITEM is the only segment that carries two legs; every other segment holds the same account on both sides. So set it false on ITEM alone, and only when the form genuinely captures both sides.'),
53
+ additionalAccounts: s.array('Extra account columns shown on this row, named exactly as they appear in extensionAccountsForSegments on this same configuration. A name that is not in that list renders nothing. Leave empty when the row needs no extra columns.', s.string('An extension-account name declared in extensionAccountsForSegments.')),
54
+ inheritValueFromAccrual: optional(s.boolean('Cash rows only: copies the account selected on the matching accrual row instead of asking again. Leave null on an accrual segment.')),
55
+ // The member really is spelled `vatSelectonActive` in the stored shape. It
56
+ // is not corrected here: renaming it would silently drop the flag on every
57
+ // form already persisted with it.
58
+ vatSelectonActive: optional(s.boolean('Adds the VAT-status column to the account chart. It applies to the ITEM segment, which is the only one carrying a VAT treatment; leave null elsewhere.')),
59
+ segmentExtension: s.boolean('True for an extra level authored under a segment rather than a segment of the published chart. An extension row is keyed by customSegment and may reuse a segment key another row already claims; an ordinary row may not.'),
60
+ });
61
+ /**
62
+ * One cash-exclusion rule.
63
+ *
64
+ * These are the only members here with per-property guidance to draw on: the
65
+ * `mscoaConfig` row opens a composite editor, and its `secondaryElementEditorConfig`
66
+ * carries a hand-written hint for each of `pattern`, `flags`, `matchField`,
67
+ * `segment` and `description`. {@link describeIn} reads them from that secondary editor
68
+ * rather than from the top-level index — see `internal/editor-guidance` for why
69
+ * those rows are indexed under the row that opens them instead of being
70
+ * prefixed with its path.
71
+ */
72
+ const dualCashExclusionRuleSchema = s.object('One condition under which the cash side is not required.', {
73
+ pattern: s.string(describeIn('A JavaScript regular expression, written without the surrounding slashes, tested against each selected accrual account.', ['mscoaConfig'], 'pattern')),
74
+ flags: optional(s.string(describeIn('Regular-expression flags for the pattern above. Leave null for an exact-case match.', ['mscoaConfig'], 'flags'))),
75
+ matchField: optional(s.enumeration(describeIn('Overrides, for THIS rule only, the labelled field the pattern is tested against; accountValueLabel above is the default. Set it when the condition keys off something the control does not display — a description field, say, on a control that shows account numbers. Leave null otherwise, which is the usual case.', ['mscoaConfig'], 'matchField'), ACCOUNT_VALUE_LABEL_ENTRIES)),
76
+ segment: optional(s.string(describeIn('Restricts the test to the accounts chosen for one accrual segment, named by its upper-cased segment key. It must be a segment this configuration actually declares — a scope nothing declares can never match. Leave null to test every accrual segment.', ['mscoaConfig'], 'segment'))),
77
+ description: optional(s.string(describeIn('A note for administrators explaining the business intent of this condition. It is never shown to the person filling in the form.', ['mscoaConfig'], 'description'))),
78
+ });
79
+ /**
80
+ * The `mscoaConfig` schema.
81
+ *
82
+ * A plain const, not a factory: nothing in here is chosen from candidates the
83
+ * caller holds. The one member that draws on a fixed pool — `accountValueLabel`
84
+ * — reads that pool out of this package at evaluation.
85
+ *
86
+ * ## Where the editor guidance is attached, and where it is not
87
+ *
88
+ * There is exactly ONE editor row for this whole object, at path
89
+ * `['mscoaConfig']`: a composite `MscoaConfig` editor that replaced eight
90
+ * separate rows (accountingBasis, accountValueLabel, showAllSegments, segments,
91
+ * cashSegments, the two dual-cash members and extensionAccountsForSegments).
92
+ * So {@link describe} is called once, on the object the row actually edits, and
93
+ * the members below carry authored descriptions only.
94
+ *
95
+ * That is a deliberate choice rather than an oversight. Calling
96
+ * `describe(base, 'mscoaConfig')` on each member compiles and would append the
97
+ * same forty-word hint — ending in "Opens a guided setup", which is advice for
98
+ * someone looking at a settings panel — to all seven of them. Layering it once,
99
+ * on the composite, says the same thing in one place.
100
+ */
101
+ export const mscoaConfigSchema = s.object(describe('How this control asks the user to select a municipal SCOA account.', 'mscoaConfig'), {
102
+ accountingBasis: optional(accountingBasisSchema),
103
+ segments: s.array('The accrual segments the user picks an account for, in the order they should appear. At least one is required, and this is the list the control is built from — an empty one gives the user nothing to choose. A budget capture usually asks for all seven segments of the chart.', segmentSchema, { minItems: 1 }),
104
+ cashSegments: optional(s.array('The segments captured on the cash side. Only meaningful when the accounting basis is cash or dual; leave null for an accrual-only input. A cash row that mirrors an accrual one should normally set inheritValueFromAccrual rather than ask the user twice.', segmentSchema)),
105
+ accountValueLabel: s.enumeration(`Which field of the account identifies it to the user, everywhere this control shows one. Choose the field an administrator would read back in the chart of accounts. ${ACCOUNT_VALUE_LABEL_GLOSS}.`, ACCOUNT_VALUE_LABEL_ENTRIES),
106
+ showAllSegments: s.boolean('True reflects every segment of the loaded account tree, read-only, instead of only the ones listed here — the application reconciles the list against the tree, so segments authored here are kept but not exclusive. False shows exactly the segments listed and nothing else, which is the usual choice.'),
107
+ extensionAccountsForSegments: s.array('Names of extra account columns this control offers on top of the ordinary account, each becoming a column on the chart. A segment opts into one by naming it in its own additionalAccounts. Leave empty when the control captures ordinary accounts only.', s.string('An extension-account name, such as a counter-account this form also captures.')),
108
+ dualCashExclusion: optional(s.object('Dual basis only: when the cash side is NOT required, despite the basis asking for both. Leave null to require both sides always, which is the default behaviour and the right answer unless the form has a stated exception.', {
109
+ rules: optional(s.array('The conditions tested against the accounts the user selected on the accrual side. A rule whose pattern does not compile never matches, so it can never suppress the cash side by accident.', dualCashExclusionRuleSchema)),
110
+ matchMode: optional(s.enumeration("How the rules combine into one verdict: 'any' suppresses cash as soon as one rule matches, 'all' only when every rule does. Absent behaves as 'any'.", [...DUAL_CASH_MATCH_MODE_VALUES])),
111
+ })),
112
+ });
113
+ /**
114
+ * Members of a generated `mscoaConfig` the application must supply before Joi
115
+ * will accept it.
116
+ *
117
+ * `inputs` is the notable one: `MscoaInputConfigSchema` REQUIRES it, and the
118
+ * model is never asked for it, so a draft that is not expanded fails
119
+ * validation rather than merely losing a detail. An empty array satisfies it —
120
+ * the segments' inner inputs are generated from the segments themselves.
121
+ *
122
+ * `dualCashExclusion.rules[].id` is deliberately absent from this list: Joi
123
+ * marks that one optional, so leaving it unset is valid.
124
+ */
125
+ export const MSCOA_DRAFT_COMPLETION = [
126
+ 'mscoaConfig.inputs (application supplies; an empty array is valid)',
127
+ 'mscoaConfig.segments[].id',
128
+ 'mscoaConfig.cashSegments[].id',
129
+ ];
@@ -0,0 +1,59 @@
1
+ import { s } from '@hashbrownai/core';
2
+ /**
3
+ * The `paginationSelectionConfig` member: how a paged selection table lists and
4
+ * identifies its records.
5
+ *
6
+ * ## This member has no interface behind it
7
+ *
8
+ * Unusually, nothing in this package declares `paginationSelectionConfig` on a
9
+ * form input. It exists as a key in `AllFormInputPrimaryKeys`, and as
10
+ * `paginationSelectionConfigSchema` in `schemas/FormInputSchema.ts`, but no
11
+ * member of the `FormColumnInputs` intersection has that name — which is why it
12
+ * falls under `UnbackedInputMember` in `input-members.ts`.
13
+ *
14
+ * That matters here rather than being trivia: the `_NoValueDrift` assertion
15
+ * resolves an unbacked key to `unknown`, and everything is assignable to
16
+ * `unknown`, so the compiler cannot check this node against anything. The Joi
17
+ * schema is therefore the ONLY thing holding it, and it is followed literally.
18
+ * A description that drifts from `paginationSelectionConfigSchema` will not
19
+ * fail the build the way the backed members do; it will fail validation at
20
+ * runtime.
21
+ *
22
+ * ## Why this is a draft
23
+ *
24
+ * Each column carries a required `id`, a builder-assigned row key, handled as
25
+ * every other assigned identifier is: omitted here and stamped on expansion.
26
+ *
27
+ * `primaryIdentifierKey` is omitted outright. It is an array of key-tree nodes
28
+ * built by the settings panel from a real sample document, and the editor row
29
+ * says an unset value means `_id` is used — so an absent one is both authorable
30
+ * and correct, whereas a model inventing tree nodes is neither.
31
+ */
32
+ export interface PaginationColumnDraft {
33
+ key: string;
34
+ label: string;
35
+ }
36
+ export interface PaginationSelectionConfigDraft {
37
+ useLocalPagination: boolean | null;
38
+ columns: PaginationColumnDraft[];
39
+ }
40
+ export declare const paginationSelectionConfigDraftSchema: s.ObjectType<{
41
+ useLocalPagination: import("@hashbrownai/core/src/schema/base").SchemaForUnion<boolean | null>;
42
+ columns: s.ArrayType<s.ObjectType<{
43
+ key: s.StringType;
44
+ label: s.StringType;
45
+ }>>;
46
+ }>;
47
+ /**
48
+ * The node produces the draft it declares.
49
+ *
50
+ * This is the only compile-time check available for this member — see the note
51
+ * above about it having no interface to be checked against.
52
+ */
53
+ export type _NoPaginationDraftDrift = [
54
+ s.Infer<typeof paginationSelectionConfigDraftSchema>
55
+ ] extends [PaginationSelectionConfigDraft] ? never : [
56
+ 'paginationSelectionConfigDraftSchema drifted from PaginationSelectionConfigDraft'
57
+ ];
58
+ /** Members of a generated config the application must supply. */
59
+ export declare const PAGINATION_DRAFT_COMPLETION: readonly ["paginationSelectionConfig.columns[].id"];
@@ -0,0 +1,16 @@
1
+ import { s } from '@hashbrownai/core';
2
+ import { AllFormInputPrimaryKeys } from '../../interfaces/FormBuilder/FormInputKeys.js';
3
+ import { optional } from '../internal/coupling.js';
4
+ import { describe } from '../internal/editor-guidance.js';
5
+ const paginationColumnSchema = s.object('One column shown in the selection table.', {
6
+ key: s.string('The property on each record whose value fills this column.'),
7
+ label: s.string('The column heading, in sentence case. Name what the column shows.'),
8
+ });
9
+ export const paginationSelectionConfigDraftSchema = s.object('How the selection table pages through records and which of their properties it shows.', {
10
+ useLocalPagination: optional(s.boolean(describe('Whether paging is done in the browser over an already-fetched list.', AllFormInputPrimaryKeys.PaginationSelectionConfig, 'useLocalPagination'))),
11
+ columns: s.array('The columns shown in the table, in order. Pick the few properties a user needs to tell one record from another.', paginationColumnSchema),
12
+ });
13
+ /** Members of a generated config the application must supply. */
14
+ export const PAGINATION_DRAFT_COMPLETION = [
15
+ 'paginationSelectionConfig.columns[].id',
16
+ ];
@@ -0,0 +1,178 @@
1
+ import { s } from '@hashbrownai/core';
2
+ import type { IMatrixInput } from '../../interfaces/formInput/MatrixInputInterface.js';
3
+ import type { TableColumnConfigInterface } from '../../interfaces/formInput/TableConfigurationsInterface.js';
4
+ import { AssertNever } from '../internal/coupling.js';
5
+ import { calculatedFieldRulesDraftSchema } from './calculated-field.js';
6
+ /**
7
+ * The two members that configure a table-shaped control: where a matrix reads
8
+ * its rows from, and how a table's columns are laid out.
9
+ *
10
+ * `matrixTableConfig` is a single string and models directly. `tableConfig`
11
+ * does not, and the reason is worth stating up front: its `columnsConfig` is a
12
+ * record keyed by column name, and Skillet has no record node. That one fact
13
+ * decides the shape of everything below.
14
+ */
15
+ /**
16
+ * The `matrixTableConfig` member: which data set a matrix control renders.
17
+ *
18
+ * `{ dataSource: string }` — the whole type. Nothing here is builder-assigned
19
+ * and nothing is unexpressible, so this is one of the few nested configs that
20
+ * models as itself, with no draft and no narrowing.
21
+ */
22
+ export declare const matrixTableConfigSchema: s.ObjectType<{
23
+ dataSource: s.StringType;
24
+ }>;
25
+ /**
26
+ * The four values `TableColumnConfigInterface['type']` admits.
27
+ *
28
+ * A string union, not an enum, so there is no runtime object to derive from and
29
+ * the members have to be restated. {@link exhaustive} makes restating them
30
+ * safe: drop one, or add a fifth to the union, and the tuple collapses to
31
+ * `never` and this file stops compiling.
32
+ *
33
+ * `'caluculated'` is spelled that way in the source interface. It is NOT
34
+ * corrected here. It is the value stored on every table already in the
35
+ * database, so the misspelling is the contract; "fixing" it would emit a
36
+ * literal nothing reads and quietly break every calculated column generated
37
+ * from this point on.
38
+ */
39
+ export declare const TABLE_COLUMN_TYPE_VALUES: readonly ["caluculated", "property", "checkBox", "primaryKey"];
40
+ /**
41
+ * One column, as the model authors it.
42
+ *
43
+ * Identical to `TableColumnConfigInterface` but for `key`, which the stored
44
+ * shape does not carry because there it IS the record key. Optional members
45
+ * surface as `| null`: Skillet has no `optional()`, so every declared key is
46
+ * emitted and `null` is how the model says "not set".
47
+ */
48
+ export interface TableColumnDraft {
49
+ /**
50
+ * The name this column is filed under in `columnsConfig`, and the name
51
+ * `displayedColumnsInOrder` refers to it by.
52
+ */
53
+ key: string;
54
+ type: TableColumnConfigInterface['type'];
55
+ property: string;
56
+ label: string;
57
+ inEdit: boolean;
58
+ checkUpto: string;
59
+ calculatedFieldRules: s.Infer<typeof calculatedFieldRulesDraftSchema>;
60
+ reductionFunction: 'sum' | null;
61
+ colorScale: string | null;
62
+ }
63
+ /**
64
+ * What the model produces in place of a `TableConfigurationsInterface`.
65
+ *
66
+ * The columns arrive as a LIST, each carrying its own key. The application
67
+ * folds that list into the `columnsConfig` record — `key` becomes the record
68
+ * key and the rest of the entry becomes the value — before the input is
69
+ * validated. Until it does, this is not a `TableConfigurationsInterface` and
70
+ * must not be treated as one.
71
+ */
72
+ export interface TableConfigDraft {
73
+ displayedColumnsInOrder: string[];
74
+ columns: TableColumnDraft[];
75
+ }
76
+ /**
77
+ * The `tableConfig` member: the columns of a table control.
78
+ *
79
+ * ## Why this produces a draft
80
+ *
81
+ * `columnsConfig` is declared `{ [key: string]: TableColumnConfigInterface | any }`
82
+ * — a record keyed by the column's own name, decided per table by whoever is
83
+ * authoring it. Skillet has no record or map node, so there is no way to say
84
+ * "an object whose keys I do not know yet and whose values all look like this".
85
+ * The only expressible alternatives are both bad: a fixed set of column names,
86
+ * which would be wrong for every table but the one it was written for, or an
87
+ * open object, which a model fills with structure nothing reads.
88
+ *
89
+ * So the record is INVERTED into a list. Each entry carries the key it is to be
90
+ * filed under, and the application folds the list back into the record before
91
+ * validation. A list is also the shape the second half of this config already
92
+ * wants: `displayedColumnsInOrder` is an ordered list of those same keys, and
93
+ * asking a model to keep an array and an object's key set in agreement is
94
+ * asking for the two to disagree.
95
+ *
96
+ * ## What is not defined here
97
+ *
98
+ * `calculatedFieldRules` belongs to `./calculated-field` and is composed in.
99
+ * The interface declares it REQUIRED on every column, not just calculated ones,
100
+ * so the node is present on every column too; the description says what to do
101
+ * with it on a column that computes nothing.
102
+ */
103
+ export declare const tableConfigSchema: s.ObjectType<{
104
+ displayedColumnsInOrder: s.ArrayType<s.StringType>;
105
+ columns: s.ArrayType<s.ObjectType<{
106
+ key: s.StringType;
107
+ type: s.EnumType<["caluculated", "property", "checkBox", "primaryKey"]>;
108
+ property: s.StringType;
109
+ label: s.StringType;
110
+ inEdit: s.BooleanType;
111
+ checkUpto: s.StringType;
112
+ calculatedFieldRules: s.ObjectType<{
113
+ formula: s.StringType;
114
+ variables: s.ArrayType<import("@hashbrownai/core/src/schema/base").SchemaForUnion<{
115
+ bindingType: "field";
116
+ variable: string;
117
+ label: string;
118
+ formControlName: string;
119
+ } | {
120
+ bindingType: "listAggregate";
121
+ variable: string;
122
+ label: string;
123
+ formControlName: string;
124
+ parentInputFormControl: string;
125
+ function: import("../../index.js").CalculationFunctions;
126
+ applyFunctionToCol: string;
127
+ applyFunctionToLabel: string | null;
128
+ filterValuesByCol: string | null;
129
+ filterValuesByThisColLabel: string | null;
130
+ tableCell: boolean | null;
131
+ }>>;
132
+ decimalPlaces: import("@hashbrownai/core/src/schema/base").SchemaForUnion<number | null>;
133
+ roundingMode: import("@hashbrownai/core/src/schema/base").SchemaForUnion<"FLOOR" | "CEIL" | "ROUND" | null>;
134
+ getFormulaFromAFormInput: import("@hashbrownai/core/src/schema/base").SchemaForUnion<boolean | null>;
135
+ formControlWithFormula: import("@hashbrownai/core/src/schema/base").SchemaForUnion<string | null>;
136
+ }>;
137
+ reductionFunction: import("@hashbrownai/core/src/schema/base").SchemaForUnion<"sum" | null>;
138
+ colorScale: import("@hashbrownai/core/src/schema/base").SchemaForUnion<string | null>;
139
+ }>>;
140
+ }>;
141
+ /**
142
+ * `matrixTableConfig` models as itself, so it is held to the real member type.
143
+ */
144
+ export type _NoMatrixTableConfigDrift = AssertNever<[
145
+ s.Infer<typeof matrixTableConfigSchema>
146
+ ] extends [
147
+ NonNullable<IMatrixInput['matrixTableConfig']>
148
+ ] ? never : 'matrixTableConfig'>;
149
+ /**
150
+ * `tableConfig` is held to its DRAFT, not to `TableConfigurationsInterface`.
151
+ *
152
+ * The two differ in shape on purpose — a list of keyed columns against a record
153
+ * of columns — so asserting against the interface would fail by design and tell
154
+ * us nothing. What is worth checking is that the node keeps producing the draft
155
+ * the application's folding step expects, and that is what this checks. The
156
+ * fold itself is the caller's contract and no compiler here can see it.
157
+ */
158
+ export type _NoTableConfigDraftDrift = AssertNever<[
159
+ s.Infer<typeof tableConfigSchema>
160
+ ] extends [TableConfigDraft] ? never : 'tableConfig'>;
161
+ /**
162
+ * The draft column still covers every member of the stored column.
163
+ *
164
+ * This is the assertion the draft would otherwise lose. Because
165
+ * {@link _NoTableConfigDraftDrift} checks the node against a type declared in
166
+ * this file, both could drift away from `TableColumnConfigInterface` together
167
+ * without anything failing. A field added to the stored column surfaces here
168
+ * instead, by name.
169
+ */
170
+ export type _TableColumnShapeCovered = AssertNever<Exclude<keyof TableColumnConfigInterface, keyof Omit<TableColumnDraft, 'key'>>>;
171
+ /**
172
+ * What the application must do to a generated `tableConfig`.
173
+ *
174
+ * The first entry is not a missing value but a change of shape: the column LIST
175
+ * must be folded into the `columnsConfig` record before anything will accept
176
+ * it. See {@link TableConfigDraft}.
177
+ */
178
+ export declare const TABLE_DRAFT_COMPLETION: readonly ["tableConfig.columns[] folded into columnsConfig, keyed by each entry's `key`", "tableConfig.columnsConfig[].calculatedFieldRules (see CALCULATED_FIELD_DRAFT_COMPLETION)"];