ngx-t-forms-types 0.0.30 → 0.0.34
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/interfaces/Form/formSubmissionHandleInterface.d.ts +6 -0
- package/dist/interfaces/FormBuilder/DefaultEelement.js +76 -0
- package/dist/interfaces/FormBuilder/DefaultInputConfigInterface.d.ts +13 -0
- package/dist/interfaces/FormBuilder/FormInputKeys.d.ts +6 -0
- package/dist/interfaces/FormBuilder/FormInputKeys.js +6 -0
- package/dist/interfaces/FormBuilder/inputConfig/ElementEditConfig.js +188 -3
- package/dist/interfaces/Import/ImportIdentity.d.ts +32 -0
- package/dist/interfaces/Import/ImportIdentity.js +1 -0
- package/dist/interfaces/Import/ImportProgress.d.ts +12 -1
- package/dist/interfaces/Import/ImportRowState.d.ts +52 -1
- package/dist/interfaces/Import/index.d.ts +2 -1
- package/dist/interfaces/formInput/APIDataFetchingConfigurationInterface.d.ts +34 -0
- package/dist/interfaces/formInput/BasicFormInputInterface.d.ts +1 -0
- package/dist/interfaces/formInput/BasicFormInputInterface.js +1 -0
- package/dist/interfaces/formInput/IMscoaAccount.d.ts +19 -3
- package/dist/interfaces/formInput/ISelectInputInterface.d.ts +12 -0
- package/dist/interfaces/formInput/MultipleInterface.d.ts +10 -0
- package/dist/interfaces/formInput/WorkflowDocumentPicker.d.ts +2 -0
- package/dist/schemas/FormInputSchema.js +41 -2
- package/dist/schemas/MatOptionsSchema.js +7 -0
- package/dist/schemas/MscoaConfigSchema.js +1 -0
- package/dist/schemas/index.d.ts +2 -1
- package/dist/schemas/index.js +2 -1
- package/dist/skillet/authoring-rules.d.ts +41 -0
- package/dist/skillet/authoring-rules.js +61 -0
- package/dist/skillet/extra-members.d.ts +143 -0
- package/dist/skillet/extra-members.js +79 -0
- package/dist/skillet/form.d.ts +170 -0
- package/dist/skillet/form.js +139 -0
- package/dist/skillet/index.d.ts +43 -0
- package/dist/skillet/index.js +46 -0
- package/dist/skillet/input-members.d.ts +375 -0
- package/dist/skillet/input-members.js +186 -0
- package/dist/skillet/internal/coupling.d.ts +63 -0
- package/dist/skillet/internal/coupling.js +69 -0
- package/dist/skillet/internal/editor-guidance.d.ts +64 -0
- package/dist/skillet/internal/editor-guidance.js +291 -0
- package/dist/skillet/members/calculated-field.d.ts +155 -0
- package/dist/skillet/members/calculated-field.js +105 -0
- package/dist/skillet/members/conditional.d.ts +69 -0
- package/dist/skillet/members/conditional.js +16 -0
- package/dist/skillet/members/document-picker.d.ts +182 -0
- package/dist/skillet/members/document-picker.js +111 -0
- package/dist/skillet/members/mat-options.d.ts +187 -0
- package/dist/skillet/members/mat-options.js +92 -0
- package/dist/skillet/members/mscoa.d.ts +182 -0
- package/dist/skillet/members/mscoa.js +129 -0
- package/dist/skillet/members/pagination.d.ts +59 -0
- package/dist/skillet/members/pagination.js +16 -0
- package/dist/skillet/members/table.d.ts +178 -0
- package/dist/skillet/members/table.js +105 -0
- package/dist/skillet/members/validators.d.ts +197 -0
- package/dist/skillet/members/validators.js +74 -0
- package/dist/skillet/members/value.d.ts +154 -0
- package/dist/skillet/members/value.js +97 -0
- package/dist/skillet/shared-types.d.ts +57 -0
- package/dist/skillet/shared-types.js +69 -0
- package/dist/skillet/tests/house-rules.spec.d.ts +1 -0
- package/dist/skillet/tests/house-rules.spec.js +162 -0
- package/dist/skillet/tests/skillet-coupling.spec.d.ts +1 -0
- package/dist/skillet/tests/skillet-coupling.spec.js +191 -0
- package/dist/skillet/tests/variant-assembly.spec.d.ts +1 -0
- package/dist/skillet/tests/variant-assembly.spec.js +643 -0
- package/dist/skillet/variants.d.ts +138 -0
- package/dist/skillet/variants.js +536 -0
- package/dist/skillet/workflow-context.d.ts +95 -0
- package/dist/skillet/workflow-context.js +128 -0
- package/package.json +78 -60
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { s } from '@hashbrownai/core';
|
|
2
|
+
import { DataSources } from '../../interfaces/formInput/APIDataFetchingConfigurationInterface.js';
|
|
3
|
+
import type { FormInputBasicOptionInterface } from '../../interfaces/formInput/FormInputBasicOptionInterface.js';
|
|
4
|
+
/**
|
|
5
|
+
* The `matOptions` member: where a control's value and its options come from.
|
|
6
|
+
*
|
|
7
|
+
* ## Why this is a factory, and why it produces a draft
|
|
8
|
+
*
|
|
9
|
+
* `APIDataFetchingConfigurationInterface` requires `_id`, `name`,
|
|
10
|
+
* `httpEndPoint`, `httpMethod`, `postFormData` and `backEndConfig`. A model
|
|
11
|
+
* asked for that will happily produce `httpEndPoint:
|
|
12
|
+
* 'https://api.example.com/departments'` and a plausible `_id`, and the result
|
|
13
|
+
* is a form that looks correct and is entirely broken — it points at an
|
|
14
|
+
* endpoint that does not exist. Of everything in these schemas, that is the
|
|
15
|
+
* failure most likely to reach production unnoticed, because nothing about the
|
|
16
|
+
* output looks wrong.
|
|
17
|
+
*
|
|
18
|
+
* The form builder never asks a human to do this either. The editor row for
|
|
19
|
+
* this property is `apiEndpointConfig`, backed by `postmanCollectionConfig` and
|
|
20
|
+
* `httpGetDataFunction` — the author PICKS an endpoint from a collection
|
|
21
|
+
* supplied at runtime. So the model picks too: {@link createMatOptionsSchema}
|
|
22
|
+
* takes the endpoints and workflows available in the caller's context and emits
|
|
23
|
+
* an enumeration over their ids. A source that has no candidates is not offered
|
|
24
|
+
* at all, which is why the branches are conditional rather than always present.
|
|
25
|
+
*
|
|
26
|
+
* The consequence is that this schema produces a {@link MatOptionsDraft}, not a
|
|
27
|
+
* `MatDataOptionsInterface`: an api draft carries the id of an endpoint, and
|
|
28
|
+
* the application expands it into the stored configuration before the form is
|
|
29
|
+
* validated. That expansion is the caller's job and there is no way around it —
|
|
30
|
+
* the full configuration is simply not knowable from a prompt.
|
|
31
|
+
*
|
|
32
|
+
* ## Deliberate narrowing
|
|
33
|
+
*
|
|
34
|
+
* `options` is declared as `Array<FormInputBasicOptionInterface |
|
|
35
|
+
* DynamicObjectInterface>`. The second branch is an index signature, which
|
|
36
|
+
* Skillet cannot express and a model has no basis for inventing, so only the
|
|
37
|
+
* `{ label, value }` shape is offered.
|
|
38
|
+
*
|
|
39
|
+
* A `mongoPipeline` source carries `pipeline: any[]` — a raw aggregation
|
|
40
|
+
* pipeline. Selecting the workflow is offered; authoring the pipeline is not.
|
|
41
|
+
*/
|
|
42
|
+
/** An endpoint the caller has available for the model to choose between. */
|
|
43
|
+
export interface AvailableApiEndpoint {
|
|
44
|
+
/** The persisted `_id` of the endpoint configuration. */
|
|
45
|
+
id: string;
|
|
46
|
+
/** Human name, shown to the model so it can choose on meaning, not on id. */
|
|
47
|
+
name: string;
|
|
48
|
+
/** What the endpoint returns, if known. Sharpens the choice considerably. */
|
|
49
|
+
description?: string;
|
|
50
|
+
}
|
|
51
|
+
/** A workflow whose data pipeline the caller has available. */
|
|
52
|
+
export interface AvailableWorkflow {
|
|
53
|
+
id: string;
|
|
54
|
+
name: string;
|
|
55
|
+
}
|
|
56
|
+
export interface MatOptionsSchemaOptions {
|
|
57
|
+
/**
|
|
58
|
+
* Endpoints the model may select for an `api` source. Omit, or pass an empty
|
|
59
|
+
* list, and the `api` branch is not offered — which is the safe default,
|
|
60
|
+
* because the alternative is a hallucinated URL.
|
|
61
|
+
*/
|
|
62
|
+
apiEndpoints?: readonly AvailableApiEndpoint[];
|
|
63
|
+
/** Workflows the model may select for a `mongoPipeline` source. */
|
|
64
|
+
workflows?: readonly AvailableWorkflow[];
|
|
65
|
+
}
|
|
66
|
+
export interface LocalSourceDraft {
|
|
67
|
+
source: DataSources.Local;
|
|
68
|
+
inputSourceId: string;
|
|
69
|
+
allowManualValueEntry: boolean;
|
|
70
|
+
}
|
|
71
|
+
export interface CustomOptionsSourceDraft {
|
|
72
|
+
source: DataSources.CustomOptions;
|
|
73
|
+
}
|
|
74
|
+
export interface ApiSourceDraft {
|
|
75
|
+
source: DataSources.Api;
|
|
76
|
+
endpointId: string;
|
|
77
|
+
allowManualValueEntry: boolean;
|
|
78
|
+
}
|
|
79
|
+
export interface MongoSourceDraft {
|
|
80
|
+
source: DataSources.MongoDb;
|
|
81
|
+
workflowId: string;
|
|
82
|
+
}
|
|
83
|
+
export type ValueSourceDraft = LocalSourceDraft | CustomOptionsSourceDraft | ApiSourceDraft | MongoSourceDraft;
|
|
84
|
+
export interface MatOptionsDraft {
|
|
85
|
+
optionType: 'user' | null;
|
|
86
|
+
options: FormInputBasicOptionInterface[];
|
|
87
|
+
fetch: {
|
|
88
|
+
value: ValueSourceDraft | null;
|
|
89
|
+
options: ValueSourceDraft | null;
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Builds the `matOptions` schema for the sources available to the caller.
|
|
94
|
+
*
|
|
95
|
+
* @param options endpoints and workflows the model may choose between. Both
|
|
96
|
+
* default to empty, which offers only the sources a model can author outright
|
|
97
|
+
* and cannot get factually wrong.
|
|
98
|
+
*/
|
|
99
|
+
export declare function createMatOptionsSchema(options?: MatOptionsSchemaOptions): s.ObjectType<{
|
|
100
|
+
optionType: import("@hashbrownai/core/src/schema/base").SchemaForUnion<"user" | null>;
|
|
101
|
+
options: s.ArrayType<s.ObjectType<{
|
|
102
|
+
label: s.StringType;
|
|
103
|
+
value: s.StringType;
|
|
104
|
+
}>>;
|
|
105
|
+
fetch: s.ObjectType<{
|
|
106
|
+
value: import("@hashbrownai/core/src/schema/base").SchemaForUnion<{
|
|
107
|
+
source: DataSources.Local;
|
|
108
|
+
inputSourceId: string;
|
|
109
|
+
allowManualValueEntry: boolean;
|
|
110
|
+
} | {
|
|
111
|
+
source: DataSources.CustomOptions;
|
|
112
|
+
} | {
|
|
113
|
+
source: DataSources.Api;
|
|
114
|
+
endpointId: string;
|
|
115
|
+
allowManualValueEntry: boolean;
|
|
116
|
+
} | {
|
|
117
|
+
source: DataSources.MongoDb;
|
|
118
|
+
workflowId: string;
|
|
119
|
+
} | null>;
|
|
120
|
+
options: import("@hashbrownai/core/src/schema/base").SchemaForUnion<{
|
|
121
|
+
source: DataSources.Local;
|
|
122
|
+
inputSourceId: string;
|
|
123
|
+
allowManualValueEntry: boolean;
|
|
124
|
+
} | {
|
|
125
|
+
source: DataSources.CustomOptions;
|
|
126
|
+
} | {
|
|
127
|
+
source: DataSources.Api;
|
|
128
|
+
endpointId: string;
|
|
129
|
+
allowManualValueEntry: boolean;
|
|
130
|
+
} | {
|
|
131
|
+
source: DataSources.MongoDb;
|
|
132
|
+
workflowId: string;
|
|
133
|
+
} | null>;
|
|
134
|
+
}>;
|
|
135
|
+
}>;
|
|
136
|
+
/**
|
|
137
|
+
* The default `matOptions` schema: no external endpoints or workflows, so it
|
|
138
|
+
* offers only the sources a model can author correctly from a prompt alone.
|
|
139
|
+
*
|
|
140
|
+
* Callers holding a real endpoint collection should build their own with
|
|
141
|
+
* {@link createMatOptionsSchema} rather than reaching for this one.
|
|
142
|
+
*/
|
|
143
|
+
export declare const matOptionsSchema: s.ObjectType<{
|
|
144
|
+
optionType: import("@hashbrownai/core/src/schema/base").SchemaForUnion<"user" | null>;
|
|
145
|
+
options: s.ArrayType<s.ObjectType<{
|
|
146
|
+
label: s.StringType;
|
|
147
|
+
value: s.StringType;
|
|
148
|
+
}>>;
|
|
149
|
+
fetch: s.ObjectType<{
|
|
150
|
+
value: import("@hashbrownai/core/src/schema/base").SchemaForUnion<{
|
|
151
|
+
source: DataSources.Local;
|
|
152
|
+
inputSourceId: string;
|
|
153
|
+
allowManualValueEntry: boolean;
|
|
154
|
+
} | {
|
|
155
|
+
source: DataSources.CustomOptions;
|
|
156
|
+
} | {
|
|
157
|
+
source: DataSources.Api;
|
|
158
|
+
endpointId: string;
|
|
159
|
+
allowManualValueEntry: boolean;
|
|
160
|
+
} | {
|
|
161
|
+
source: DataSources.MongoDb;
|
|
162
|
+
workflowId: string;
|
|
163
|
+
} | null>;
|
|
164
|
+
options: import("@hashbrownai/core/src/schema/base").SchemaForUnion<{
|
|
165
|
+
source: DataSources.Local;
|
|
166
|
+
inputSourceId: string;
|
|
167
|
+
allowManualValueEntry: boolean;
|
|
168
|
+
} | {
|
|
169
|
+
source: DataSources.CustomOptions;
|
|
170
|
+
} | {
|
|
171
|
+
source: DataSources.Api;
|
|
172
|
+
endpointId: string;
|
|
173
|
+
allowManualValueEntry: boolean;
|
|
174
|
+
} | {
|
|
175
|
+
source: DataSources.MongoDb;
|
|
176
|
+
workflowId: string;
|
|
177
|
+
} | null>;
|
|
178
|
+
}>;
|
|
179
|
+
}>;
|
|
180
|
+
/**
|
|
181
|
+
* Members of a generated `matOptions` the application must expand.
|
|
182
|
+
*
|
|
183
|
+
* `endpointId` and `workflowId` are draft-only keys that do not exist on
|
|
184
|
+
* `MatDataOptionsInterface` at all — Joi rejects unknown keys, so a draft that
|
|
185
|
+
* reaches the validator unexpanded fails outright rather than degrading.
|
|
186
|
+
*/
|
|
187
|
+
export declare const MAT_OPTIONS_DRAFT_COMPLETION: readonly ["matOptions.fetch.value|options.endpointId (expanded to the stored APIDataFetchingConfiguration)", "matOptions.fetch.value|options.workflowId (expanded to the stored pipeline configuration)", "matOptions.fetch.value|options.inputSourceId (resolved from formControlName to an input id)"];
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { s } from '@hashbrownai/core';
|
|
2
|
+
import { DataSources } from '../../interfaces/formInput/APIDataFetchingConfigurationInterface.js';
|
|
3
|
+
import { describe } from '../internal/editor-guidance.js';
|
|
4
|
+
// --- schema -----------------------------------------------------------------
|
|
5
|
+
const optionSchema = s.object('A selectable option.', {
|
|
6
|
+
label: s.string('The text shown to the user. It may change freely.'),
|
|
7
|
+
value: s.string("The value stored when this option is chosen: a single word with no spaces ('urgent', 'capex'), kept stable once the form is live. A decision gate compares the stored value, not the label, and splits its expression on spaces."),
|
|
8
|
+
});
|
|
9
|
+
/** Renders the endpoint list into prose so the model chooses on meaning. */
|
|
10
|
+
function describeCandidates(lead, candidates) {
|
|
11
|
+
const lines = candidates.map((c) => c.description ? `${c.id} (${c.name}: ${c.description})` : `${c.id} (${c.name})`);
|
|
12
|
+
return `${lead} Choose by meaning, not by position: ${lines.join('; ')}.`;
|
|
13
|
+
}
|
|
14
|
+
function sourceBranches(slot, options) {
|
|
15
|
+
const localLead = slot === 'value'
|
|
16
|
+
? 'Takes its value from another field on this form.'
|
|
17
|
+
: 'Takes its options from another field on this form.';
|
|
18
|
+
const endpoints = options.apiEndpoints ?? [];
|
|
19
|
+
const workflows = options.workflows ?? [];
|
|
20
|
+
// Built by conditional spread rather than by pushing onto an array: a `push`
|
|
21
|
+
// would have to widen every branch to the shape of the first two, which
|
|
22
|
+
// erases the per-branch typing that `s.Infer` needs to reconstruct the union.
|
|
23
|
+
return [
|
|
24
|
+
s.object(describe(localLead, 'matOptions', 'fetch', slot, 'source'), {
|
|
25
|
+
source: s.literal(DataSources.Local),
|
|
26
|
+
inputSourceId: s.string('The formControlName of the field on this form to read from. It must be a field that actually exists in the form being authored.'),
|
|
27
|
+
allowManualValueEntry: s.boolean('Only meaningful on the value slot of a field whose dataType is object or array (an mSCOA, a multiple-input table). An inherited value of that shape can arrive partly filled in — only some mSCOA segments, rows missing a column — and this decides whether the user may finish it. True leaves the field editable: whenever the inherited value changes, only what actually changed is applied over it and whatever the user added is kept. False locks the field. Choose false unless the field is object- or array-shaped AND the source field is expected to supply only part of it. It has no effect on a scalar field or on an options slot, where the inherited value always wins.'),
|
|
28
|
+
}),
|
|
29
|
+
s.object('Uses a fixed list of options authored into the form itself.', {
|
|
30
|
+
source: s.literal(DataSources.CustomOptions),
|
|
31
|
+
}),
|
|
32
|
+
...(endpoints.length
|
|
33
|
+
? [
|
|
34
|
+
s.object('Fetches from a configured API endpoint.', {
|
|
35
|
+
source: s.literal(DataSources.Api),
|
|
36
|
+
endpointId: s.enumeration(describeCandidates('Which configured endpoint to call.', endpoints), endpoints.map((e) => e.id)),
|
|
37
|
+
allowManualValueEntry: s.boolean('Whether the user may supply what the endpoint does not. On a scalar field: true lets a value they type stand whenever the endpoint contributes nothing (no response, an empty one, a failed request), and a non-empty response always wins over what they typed. On a field whose dataType is object or array (an mSCOA, a multiple-input table): true leaves the field editable so they can finish a response that arrives incomplete — rows missing a column, only some mSCOA segments — and each new response is applied over the live value as a delta, so what the endpoint sends updates and what the user added is kept. False locks the field, which is the right choice whenever the endpoint answers completely.'),
|
|
38
|
+
}),
|
|
39
|
+
]
|
|
40
|
+
: []),
|
|
41
|
+
...(workflows.length
|
|
42
|
+
? [
|
|
43
|
+
s.object('Fetches through a workflow data pipeline.', {
|
|
44
|
+
source: s.literal(DataSources.MongoDb),
|
|
45
|
+
workflowId: s.enumeration(describeCandidates('Which workflow supplies the data.', workflows), workflows.map((w) => w.id)),
|
|
46
|
+
}),
|
|
47
|
+
]
|
|
48
|
+
: []),
|
|
49
|
+
];
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Builds the `matOptions` schema for the sources available to the caller.
|
|
53
|
+
*
|
|
54
|
+
* @param options endpoints and workflows the model may choose between. Both
|
|
55
|
+
* default to empty, which offers only the sources a model can author outright
|
|
56
|
+
* and cannot get factually wrong.
|
|
57
|
+
*/
|
|
58
|
+
export function createMatOptionsSchema(options = {}) {
|
|
59
|
+
const valueSource = s.anyOf(sourceBranches('value', options));
|
|
60
|
+
const optionsSource = s.anyOf(sourceBranches('options', options));
|
|
61
|
+
return s.object('Where this control gets its value and the options it offers. Only set this on a control that actually reads from somewhere; a plain text input does not.', {
|
|
62
|
+
optionType: s.anyOf([
|
|
63
|
+
s.literal('user'),
|
|
64
|
+
s.nullish(),
|
|
65
|
+
]),
|
|
66
|
+
options: s.array(describe('The fixed options offered by this control, for a short list fixed by policy. Fill this in only when the options source is a custom list; leave it empty otherwise. Never retype a list a system already owns — departments, suppliers, users, cost centres — as custom options; it goes stale.', 'matOptions', 'options'), optionSchema),
|
|
67
|
+
fetch: s.object('Where the value and the options are read from.', {
|
|
68
|
+
value: s.anyOf([valueSource, s.nullish()]),
|
|
69
|
+
options: s.anyOf([optionsSource, s.nullish()]),
|
|
70
|
+
}),
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* The default `matOptions` schema: no external endpoints or workflows, so it
|
|
75
|
+
* offers only the sources a model can author correctly from a prompt alone.
|
|
76
|
+
*
|
|
77
|
+
* Callers holding a real endpoint collection should build their own with
|
|
78
|
+
* {@link createMatOptionsSchema} rather than reaching for this one.
|
|
79
|
+
*/
|
|
80
|
+
export const matOptionsSchema = createMatOptionsSchema();
|
|
81
|
+
/**
|
|
82
|
+
* Members of a generated `matOptions` the application must expand.
|
|
83
|
+
*
|
|
84
|
+
* `endpointId` and `workflowId` are draft-only keys that do not exist on
|
|
85
|
+
* `MatDataOptionsInterface` at all — Joi rejects unknown keys, so a draft that
|
|
86
|
+
* reaches the validator unexpanded fails outright rather than degrading.
|
|
87
|
+
*/
|
|
88
|
+
export const MAT_OPTIONS_DRAFT_COMPLETION = [
|
|
89
|
+
'matOptions.fetch.value|options.endpointId (expanded to the stored APIDataFetchingConfiguration)',
|
|
90
|
+
'matOptions.fetch.value|options.workflowId (expanded to the stored pipeline configuration)',
|
|
91
|
+
'matOptions.fetch.value|options.inputSourceId (resolved from formControlName to an input id)',
|
|
92
|
+
];
|
|
@@ -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"];
|