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.
- 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/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,186 @@
|
|
|
1
|
+
import { s } from '@hashbrownai/core';
|
|
2
|
+
import { AllFormInputPrimaryKeys } from '../interfaces/FormBuilder/FormInputKeys.js';
|
|
3
|
+
import { matOptionsSchema } from './members/mat-options.js';
|
|
4
|
+
import { calculatedFieldRulesDraftSchema, } from './members/calculated-field.js';
|
|
5
|
+
import { validatorsDraftSchema, } from './members/validators.js';
|
|
6
|
+
import { conditionalInputConfigDraftSchema, } from './members/conditional.js';
|
|
7
|
+
import { mscoaConfigSchema } from './members/mscoa.js';
|
|
8
|
+
import { paginationSelectionConfigDraftSchema, } from './members/pagination.js';
|
|
9
|
+
import { calculateListFunctionsSchema, scriptSchema, valueSchema, } from './members/value.js';
|
|
10
|
+
import { enumValues } from './internal/coupling.js';
|
|
11
|
+
import { CONTROL_NAME_PATTERN } from './authoring-rules.js';
|
|
12
|
+
import { describe, rangeFor } from './internal/editor-guidance.js';
|
|
13
|
+
import { adjudicationStepSchema, appearanceSchema, autocapitalizeSchema, elementTypeSchema, inputDataTypeSchema, inputFileTypeSchema, inputPipeTypeSchema, inputTypeSchema, inputViewTypeSchema, labelPositionSchema, optionSelectTypeSchema, richTextEditorTypeSchema, textareaWrapSchema, } from './shared-types.js';
|
|
14
|
+
/**
|
|
15
|
+
* Members a model must never author: identifiers the builder assigns and
|
|
16
|
+
* runtime state the form owns while it is open. These are excluded from
|
|
17
|
+
* generation schemas entirely and stitched in afterwards.
|
|
18
|
+
*/
|
|
19
|
+
export const SYSTEM_OWNED_INPUT_MEMBERS = [
|
|
20
|
+
AllFormInputPrimaryKeys.Id,
|
|
21
|
+
AllFormInputPrimaryKeys.SectionId,
|
|
22
|
+
AllFormInputPrimaryKeys.SystemDefault,
|
|
23
|
+
AllFormInputPrimaryKeys.Status,
|
|
24
|
+
AllFormInputPrimaryKeys.ValueUpdatedAt,
|
|
25
|
+
AllFormInputPrimaryKeys.TemporaryHint,
|
|
26
|
+
AllFormInputPrimaryKeys.FormIsOpen,
|
|
27
|
+
AllFormInputPrimaryKeys.MultipleInputInEditId,
|
|
28
|
+
AllFormInputPrimaryKeys.CanReload,
|
|
29
|
+
];
|
|
30
|
+
/**
|
|
31
|
+
* Members kept only so stored forms keep deserialising. They are excluded from
|
|
32
|
+
* generation entirely: a model must never author a property the codebase has
|
|
33
|
+
* already moved on from, and leaving one in the schema would see it reappear on
|
|
34
|
+
* every newly generated input.
|
|
35
|
+
*/
|
|
36
|
+
export const DEPRECATED_INPUT_MEMBERS = [
|
|
37
|
+
AllFormInputPrimaryKeys.IServeInputWithRequisitionItems,
|
|
38
|
+
];
|
|
39
|
+
/**
|
|
40
|
+
* Members whose shape is a nested configuration object deserving its own
|
|
41
|
+
* module. Listed rather than stubbed: a placeholder node would compile and then
|
|
42
|
+
* quietly feed the model a wrong shape, which is worse than an absent one.
|
|
43
|
+
*/
|
|
44
|
+
export const PENDING_INPUT_MEMBERS = [
|
|
45
|
+
AllFormInputPrimaryKeys.AdjudicationReviewFeedbackTemplate,
|
|
46
|
+
];
|
|
47
|
+
/**
|
|
48
|
+
* Members whose node cannot be built until assembly, because its shape depends
|
|
49
|
+
* on something only the caller knows.
|
|
50
|
+
*
|
|
51
|
+
* These differ from {@link DRAFT_INPUT_MEMBERS} in that there is no sensible
|
|
52
|
+
* default node to publish at all:
|
|
53
|
+
*
|
|
54
|
+
* - `workflowPickerConfig` needs the workflows available in the caller's
|
|
55
|
+
* context. With none, the member is not offered — the same reasoning that
|
|
56
|
+
* withholds the `api` source in `members/mat-options.ts`, and for the same
|
|
57
|
+
* reason: a fabricated `workflowId` produces a picker that looks right and
|
|
58
|
+
* points at nothing.
|
|
59
|
+
* - `formInputs` is the contents of a repeatable list, which is itself a list
|
|
60
|
+
* of columns. It can only be built once the remaining nesting depth is known,
|
|
61
|
+
* because Skillet has no way to express a recursive schema and the recursion
|
|
62
|
+
* has to be unrolled to a fixed depth instead.
|
|
63
|
+
*
|
|
64
|
+
* `variants.ts` resolves both while it walks an element's properties.
|
|
65
|
+
*/
|
|
66
|
+
export const ASSEMBLED_INPUT_MEMBERS = [
|
|
67
|
+
AllFormInputPrimaryKeys.WorkflowPickerConfig,
|
|
68
|
+
AllFormInputPrimaryKeys.FormInputs,
|
|
69
|
+
];
|
|
70
|
+
/**
|
|
71
|
+
* Members whose schema produces an AUTHORING DRAFT rather than the stored
|
|
72
|
+
* shape, because some part of the stored shape is not knowable from a prompt.
|
|
73
|
+
*
|
|
74
|
+
* `matOptions` is the first of these: an api source is stored as a full
|
|
75
|
+
* endpoint configuration — `_id`, `httpEndPoint`, `httpMethod`, headers — and a
|
|
76
|
+
* model asked for that invents a URL. So the draft carries the id of an
|
|
77
|
+
* endpoint the caller supplied, and the application expands it before the form
|
|
78
|
+
* is validated.
|
|
79
|
+
*
|
|
80
|
+
* These are checked against their declared draft type rather than against the
|
|
81
|
+
* interface member, so the compiler still holds them to something. What it
|
|
82
|
+
* cannot check is the expansion itself; that contract lives with the caller.
|
|
83
|
+
*/
|
|
84
|
+
export const DRAFT_INPUT_MEMBERS = [
|
|
85
|
+
AllFormInputPrimaryKeys.MatOptions,
|
|
86
|
+
AllFormInputPrimaryKeys.CalculatedFieldRules,
|
|
87
|
+
AllFormInputPrimaryKeys.Validators,
|
|
88
|
+
AllFormInputPrimaryKeys.ConditionalInputConfig,
|
|
89
|
+
AllFormInputPrimaryKeys.MscoaConfig,
|
|
90
|
+
AllFormInputPrimaryKeys.PaginationSelectionConfig,
|
|
91
|
+
AllFormInputPrimaryKeys.CalculateListFunctions,
|
|
92
|
+
];
|
|
93
|
+
/**
|
|
94
|
+
* Members whose model output must be expanded before it satisfies the
|
|
95
|
+
* interfaces. Kept apart from {@link inputMemberSchemas} so that consuming code
|
|
96
|
+
* cannot treat a draft as a finished value by accident.
|
|
97
|
+
*/
|
|
98
|
+
export const draftMemberSchemas = {
|
|
99
|
+
[AllFormInputPrimaryKeys.MatOptions]: matOptionsSchema,
|
|
100
|
+
[AllFormInputPrimaryKeys.CalculatedFieldRules]: calculatedFieldRulesDraftSchema,
|
|
101
|
+
[AllFormInputPrimaryKeys.Validators]: validatorsDraftSchema,
|
|
102
|
+
[AllFormInputPrimaryKeys.ConditionalInputConfig]: conditionalInputConfigDraftSchema,
|
|
103
|
+
[AllFormInputPrimaryKeys.MscoaConfig]: mscoaConfigSchema,
|
|
104
|
+
[AllFormInputPrimaryKeys.PaginationSelectionConfig]: paginationSelectionConfigDraftSchema,
|
|
105
|
+
[AllFormInputPrimaryKeys.CalculateListFunctions]: calculateListFunctionsSchema,
|
|
106
|
+
};
|
|
107
|
+
// Descriptions on the members every element shares are kept SHORT on purpose:
|
|
108
|
+
// each is repeated once per element per nesting level in the emitted schema,
|
|
109
|
+
// because Hashbrown names `$defs` entries by their description. The
|
|
110
|
+
// definition and the one rule that belongs at the member stay here; the
|
|
111
|
+
// form-wide rules live in `authoring-rules.ts`, emitted once at the root.
|
|
112
|
+
export const inputMemberSchemas = {
|
|
113
|
+
// --- identity and layout --------------------------------------------------
|
|
114
|
+
[AllFormInputPrimaryKeys.Element]: elementTypeSchema,
|
|
115
|
+
[AllFormInputPrimaryKeys.Label]: s.string(describe("The visible field label: a noun phrase of one to three words in sentence case, in the words of the people who fill the form in ('Cost centre', not 'cc_code'), with no colon or trailing punctuation. It is also the heading the answer is printed under, so it must make sense on its own.", AllFormInputPrimaryKeys.Label)),
|
|
116
|
+
[AllFormInputPrimaryKeys.FormControlName]: s.string(describe("The key this value is stored under, derived from the label so that it identifies the field on its own: 'budgetReview', not 'toggle1'. Unique across the whole form; never a reserved name; never changed once the form is live.", AllFormInputPrimaryKeys.FormControlName), { pattern: CONTROL_NAME_PATTERN }),
|
|
117
|
+
[AllFormInputPrimaryKeys.ColSize]: s.integer(describe('How many of the 12 grid columns this control spans. Size it to the data: 12 for long text, a textarea, an item list, a picker, an uploader or a signature; 6, 4 or 3 for a date, an amount, a quantity, a short code or a toggle.', AllFormInputPrimaryKeys.ColSize), rangeFor(AllFormInputPrimaryKeys.ColSize) ?? { minimum: 1, maximum: 12 }),
|
|
118
|
+
[AllFormInputPrimaryKeys.Appearance]: appearanceSchema,
|
|
119
|
+
[AllFormInputPrimaryKeys.DataType]: inputDataTypeSchema,
|
|
120
|
+
[AllFormInputPrimaryKeys.Type]: inputTypeSchema,
|
|
121
|
+
[AllFormInputPrimaryKeys.ViewType]: inputViewTypeSchema,
|
|
122
|
+
// --- obligation and state -------------------------------------------------
|
|
123
|
+
[AllFormInputPrimaryKeys.Required]: s.boolean(describe('Whether the user must supply a value before the form can be submitted. Required when the next step cannot proceed without it; if in doubt, not required; if required only sometimes, use a validator or a conditional display rule instead.', AllFormInputPrimaryKeys.Required)),
|
|
124
|
+
[AllFormInputPrimaryKeys.Readonly]: s.boolean(describe("Whether the value is displayed but cannot be edited: for an earlier step's answer, a profile value, a fetched figure. The value is still submitted.", AllFormInputPrimaryKeys.Readonly)),
|
|
125
|
+
[AllFormInputPrimaryKeys.ShowAsLabel]: s.boolean(describe('Renders the value as plain text instead of a form control.', AllFormInputPrimaryKeys.ShowAsLabel)),
|
|
126
|
+
[AllFormInputPrimaryKeys.OnlySetTempErrorOnTouch]: s.boolean(describe('Withholds validation errors until the user has interacted with the field. True on nearly every field.', AllFormInputPrimaryKeys.OnlySetTempErrorOnTouch)),
|
|
127
|
+
[AllFormInputPrimaryKeys.IsCalculatedField]: s.boolean(describe('Whether this value is computed from other fields rather than entered by the user.', AllFormInputPrimaryKeys.IsCalculatedField)),
|
|
128
|
+
// --- guidance -------------------------------------------------------------
|
|
129
|
+
[AllFormInputPrimaryKeys.HintLabel]: s.string(describe('Short helper text shown under the field permanently: one sentence with no full stop, giving the format, the source, or the reason for asking. On a sectionTitle it is the guidance for the whole group that follows and may run to two sentences.', AllFormInputPrimaryKeys.HintLabel)),
|
|
130
|
+
[AllFormInputPrimaryKeys.TourContent]: s.string(describe('Guided-tour copy shown when the form is first walked through. Only for a genuinely unfamiliar control such as an account picker or a document picker: two sentences, what it is and what to do.', AllFormInputPrimaryKeys.TourContent)),
|
|
131
|
+
// --- text behaviour -------------------------------------------------------
|
|
132
|
+
[AllFormInputPrimaryKeys.Spellcheck]: s.boolean(describe('Whether the browser spell-checks the typed text. Enable for prose, disable for codes and identifiers.', AllFormInputPrimaryKeys.Spellcheck)),
|
|
133
|
+
[AllFormInputPrimaryKeys.Autocapitalize]: autocapitalizeSchema,
|
|
134
|
+
[AllFormInputPrimaryKeys.Rows]: s.integer(describe('Visible height of a textarea, in lines.', AllFormInputPrimaryKeys.Rows), { minimum: 1 }),
|
|
135
|
+
[AllFormInputPrimaryKeys.Wrap]: textareaWrapSchema,
|
|
136
|
+
[AllFormInputPrimaryKeys.RichTextEditorLibrary]: richTextEditorTypeSchema,
|
|
137
|
+
// --- constraints ----------------------------------------------------------
|
|
138
|
+
// A union node has no description slot of its own, so the applicability rule
|
|
139
|
+
// the editor declares for these — they mean nothing unless the input is
|
|
140
|
+
// numeric or a date — is composed onto each branch instead. Dropping it would
|
|
141
|
+
// lose the single most useful thing the editor config knows about them.
|
|
142
|
+
[AllFormInputPrimaryKeys.Min]: s.anyOf([
|
|
143
|
+
s.number(describe('A numeric lower bound: 1 for a quantity, 0 for an amount or a percentage.', AllFormInputPrimaryKeys.Min)),
|
|
144
|
+
s.string(describe('A lower bound as an ISO date, for date controls.', AllFormInputPrimaryKeys.Min)),
|
|
145
|
+
]),
|
|
146
|
+
[AllFormInputPrimaryKeys.Max]: s.anyOf([
|
|
147
|
+
s.number(describe('A numeric upper bound: 100 for a percentage.', AllFormInputPrimaryKeys.Max)),
|
|
148
|
+
s.string(describe('An upper bound as an ISO date, for date controls.', AllFormInputPrimaryKeys.Max)),
|
|
149
|
+
]),
|
|
150
|
+
[AllFormInputPrimaryKeys.MinLength]: s.integer(describe('The fewest characters the value may contain.', AllFormInputPrimaryKeys.MinLength), { minimum: 0 }),
|
|
151
|
+
[AllFormInputPrimaryKeys.MaxLength]: s.integer(describe('The most characters the value may contain. Match the limit of the system the value is stored in, and say so in the hint when it matters.', AllFormInputPrimaryKeys.MaxLength), { minimum: 1 }),
|
|
152
|
+
// --- options and selection ------------------------------------------------
|
|
153
|
+
[AllFormInputPrimaryKeys.OptionSelectType]: optionSelectTypeSchema,
|
|
154
|
+
[AllFormInputPrimaryKeys.AllowMultipleSelection]: s.boolean(describe('Whether more than one option may be chosen at once.', AllFormInputPrimaryKeys.AllowMultipleSelection)),
|
|
155
|
+
[AllFormInputPrimaryKeys.SingleSelect]: s.boolean(describe('Restricts the control to exactly one selection.', AllFormInputPrimaryKeys.SingleSelect)),
|
|
156
|
+
[AllFormInputPrimaryKeys.GroupBy]: s.array(describe('Option properties to group the list under, outermost first. Leave empty for a flat list.', AllFormInputPrimaryKeys.GroupBy), s.string('An option property name to group by.')),
|
|
157
|
+
// --- toggle ---------------------------------------------------------------
|
|
158
|
+
[AllFormInputPrimaryKeys.LabelPosition]: labelPositionSchema,
|
|
159
|
+
[AllFormInputPrimaryKeys.TrueLabel]: s.string(describe("Text shown when a toggle is on. Name the real state — 'Approve', 'VAT registered' — never a bare 'Yes', which forces the user to re-read the label.", AllFormInputPrimaryKeys.TrueLabel)),
|
|
160
|
+
[AllFormInputPrimaryKeys.FalseLabel]: s.string(describe("Text shown when a toggle is off: the direct opposite of the true label — 'Reject', 'Not VAT registered' — never a bare 'No'.", AllFormInputPrimaryKeys.FalseLabel)),
|
|
161
|
+
// --- dates ----------------------------------------------------------------
|
|
162
|
+
[AllFormInputPrimaryKeys.StartDateControl]: s.string(describe('The formControlName holding the start of a date range. A range stores two values, so both this and endDateControl must be set, or the field is dropped from prefill, export and print.', AllFormInputPrimaryKeys.StartDateControl)),
|
|
163
|
+
[AllFormInputPrimaryKeys.EndDateControl]: s.string(describe('The formControlName holding the end of a date range. Both this and startDateControl must be set.', AllFormInputPrimaryKeys.EndDateControl)),
|
|
164
|
+
// --- files ----------------------------------------------------------------
|
|
165
|
+
[AllFormInputPrimaryKeys.FileType]: inputFileTypeSchema,
|
|
166
|
+
[AllFormInputPrimaryKeys.Accept]: s.string(describe('Accepted upload types as an HTML accept attribute, for example ".pdf,.docx" or "image/*". Match it to the documents actually expected, and name the accepted types in the hint as well.', AllFormInputPrimaryKeys.Accept)),
|
|
167
|
+
[AllFormInputPrimaryKeys.HandleLabel]: s.string(describe("The text of the button that adds a row to an item list. Name the action — 'Add item', 'Add attendee' — not the default 'Add to list'.", AllFormInputPrimaryKeys.HandleLabel)),
|
|
168
|
+
// --- display transform ----------------------------------------------------
|
|
169
|
+
[AllFormInputPrimaryKeys.Pipe]: s.object(describe('A display transform applied when the value is shown back to the user: currency for an amount, percent for a rate, date for a date. Presentation only; the stored value is unchanged.', AllFormInputPrimaryKeys.Pipe), {
|
|
170
|
+
pipeType: inputPipeTypeSchema,
|
|
171
|
+
param: s.string('The pipe argument, such as a date format like "dd MMM yyyy" or a currency code like "ZAR".'),
|
|
172
|
+
}),
|
|
173
|
+
// --- domain ---------------------------------------------------------------
|
|
174
|
+
[AllFormInputPrimaryKeys.LinkedSegmentId]: s.string(describe('Identifier of the mSCOA segment this control resolves against.', AllFormInputPrimaryKeys.LinkedSegmentId)),
|
|
175
|
+
[AllFormInputPrimaryKeys.AdjudicationStep]: adjudicationStepSchema,
|
|
176
|
+
[AllFormInputPrimaryKeys.AdjudicationPointTypes]: s.array(describe('The scoring categories available at this adjudication step.', AllFormInputPrimaryKeys.AdjudicationPointTypes), s.string('A scoring category name.')),
|
|
177
|
+
[AllFormInputPrimaryKeys.WorkflowFormForScoreSheet]: s.string(describe('Identifier of the form supplying the score sheet for this adjudication.', AllFormInputPrimaryKeys.WorkflowFormForScoreSheet)),
|
|
178
|
+
// --- prefilled value and behaviour ---------------------------------------
|
|
179
|
+
// Both are modelled rather than drafted: they carry no builder-assigned
|
|
180
|
+
// identifier, so what the model emits is what gets stored. See
|
|
181
|
+
// `members/value.ts` for the narrowing each one applies.
|
|
182
|
+
[AllFormInputPrimaryKeys.Value]: valueSchema,
|
|
183
|
+
[AllFormInputPrimaryKeys.Script]: scriptSchema,
|
|
184
|
+
};
|
|
185
|
+
/** Runtime access to the full key list, derived from the enum. */
|
|
186
|
+
export const ALL_INPUT_MEMBER_KEYS = enumValues(AllFormInputPrimaryKeys);
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { s } from '@hashbrownai/core';
|
|
2
|
+
/**
|
|
3
|
+
* Compile-time coupling primitives that bind the Skillet schemas to the
|
|
4
|
+
* interfaces they describe.
|
|
5
|
+
*
|
|
6
|
+
* The rule these enforce, stated once:
|
|
7
|
+
*
|
|
8
|
+
* `s.Infer<node>` must be ASSIGNABLE TO the interface member's type.
|
|
9
|
+
*
|
|
10
|
+
* Assignability — not equality — is deliberate. A Skillet node is allowed to be
|
|
11
|
+
* NARROWER than the interface (we routinely refuse to let a model emit a `Date`
|
|
12
|
+
* object or a custom-script sentinel even where the interface permits one), but
|
|
13
|
+
* it may never be WIDER. Widening is precisely the drift that produces a value
|
|
14
|
+
* the rest of the codebase cannot hold, so that is the direction we fail on.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Guards a Skillet node against the type it is meant to produce.
|
|
18
|
+
*
|
|
19
|
+
* Curried so the expected type is written explicitly and the node type is
|
|
20
|
+
* inferred:
|
|
21
|
+
*
|
|
22
|
+
* ```ts
|
|
23
|
+
* const colSize = matches<number>()(s.integer('Grid width', { minimum: 1 }));
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* If the node's inferred output stops being assignable to `Expected` — because
|
|
27
|
+
* someone retyped the interface member, or loosened the node — the argument
|
|
28
|
+
* collapses to `never` and the call fails to compile.
|
|
29
|
+
*/
|
|
30
|
+
export declare function matches<Expected>(): <Node extends s.HashbrownType>(node: [s.Infer<Node>] extends [Expected] ? Node : never) => Node;
|
|
31
|
+
/**
|
|
32
|
+
* Derives Skillet enum entries from a TypeScript string enum at runtime, so the
|
|
33
|
+
* two can never disagree: add a member to the enum and it appears in the schema
|
|
34
|
+
* on the next evaluation, with `s.Infer` widening to match.
|
|
35
|
+
*
|
|
36
|
+
* The `Record<string, string>` constraint rejects numeric and mixed enums —
|
|
37
|
+
* `Object.values` on those also yields the reverse-mapped numeric keys, which
|
|
38
|
+
* would silently poison the entry list. Skillet enumerations are string-only.
|
|
39
|
+
*/
|
|
40
|
+
export declare function enumValues<E extends Record<string, string>>(enumObject: E): E[keyof E][];
|
|
41
|
+
/**
|
|
42
|
+
* Asserts a literal tuple covers a string union exactly.
|
|
43
|
+
*
|
|
44
|
+
* Needed for union *types* (`'hard' | 'soft'`), which — unlike enums — have no
|
|
45
|
+
* runtime representation to derive from, so the members must be restated. This
|
|
46
|
+
* makes restating them safe: drop one and the argument becomes `never`.
|
|
47
|
+
*
|
|
48
|
+
* ```ts
|
|
49
|
+
* const WRAP = exhaustive<'hard' | 'soft'>()(['hard', 'soft'] as const);
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
export declare function exhaustive<Union extends string>(): <Tuple extends readonly Union[]>(entries: [Exclude<Union, Tuple[number]>] extends [never] ? Tuple : never) => Tuple;
|
|
53
|
+
/**
|
|
54
|
+
* Wraps a node as an absent-able member.
|
|
55
|
+
*
|
|
56
|
+
* Skillet has no `optional()`: every declared key must appear in the model's
|
|
57
|
+
* response. Optionality is therefore modelled as an explicit null branch — the
|
|
58
|
+
* key is always emitted, and `null` is how the model says "not set". Strip the
|
|
59
|
+
* nulls when converting a generated column into a `FormColumnInputs`.
|
|
60
|
+
*/
|
|
61
|
+
export declare function optional<Node extends s.HashbrownType>(node: Node): import("@hashbrownai/core/src/schema/base").SchemaForUnion<(Node | s.NullType)["~schema"]["result"]>;
|
|
62
|
+
/** Resolves to `never` unless `T` is `never`; used to prove a set is empty. */
|
|
63
|
+
export type AssertNever<T extends never> = T;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { s } from '@hashbrownai/core';
|
|
2
|
+
/**
|
|
3
|
+
* Compile-time coupling primitives that bind the Skillet schemas to the
|
|
4
|
+
* interfaces they describe.
|
|
5
|
+
*
|
|
6
|
+
* The rule these enforce, stated once:
|
|
7
|
+
*
|
|
8
|
+
* `s.Infer<node>` must be ASSIGNABLE TO the interface member's type.
|
|
9
|
+
*
|
|
10
|
+
* Assignability — not equality — is deliberate. A Skillet node is allowed to be
|
|
11
|
+
* NARROWER than the interface (we routinely refuse to let a model emit a `Date`
|
|
12
|
+
* object or a custom-script sentinel even where the interface permits one), but
|
|
13
|
+
* it may never be WIDER. Widening is precisely the drift that produces a value
|
|
14
|
+
* the rest of the codebase cannot hold, so that is the direction we fail on.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Guards a Skillet node against the type it is meant to produce.
|
|
18
|
+
*
|
|
19
|
+
* Curried so the expected type is written explicitly and the node type is
|
|
20
|
+
* inferred:
|
|
21
|
+
*
|
|
22
|
+
* ```ts
|
|
23
|
+
* const colSize = matches<number>()(s.integer('Grid width', { minimum: 1 }));
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* If the node's inferred output stops being assignable to `Expected` — because
|
|
27
|
+
* someone retyped the interface member, or loosened the node — the argument
|
|
28
|
+
* collapses to `never` and the call fails to compile.
|
|
29
|
+
*/
|
|
30
|
+
export function matches() {
|
|
31
|
+
return (node) => node;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Derives Skillet enum entries from a TypeScript string enum at runtime, so the
|
|
35
|
+
* two can never disagree: add a member to the enum and it appears in the schema
|
|
36
|
+
* on the next evaluation, with `s.Infer` widening to match.
|
|
37
|
+
*
|
|
38
|
+
* The `Record<string, string>` constraint rejects numeric and mixed enums —
|
|
39
|
+
* `Object.values` on those also yields the reverse-mapped numeric keys, which
|
|
40
|
+
* would silently poison the entry list. Skillet enumerations are string-only.
|
|
41
|
+
*/
|
|
42
|
+
export function enumValues(enumObject) {
|
|
43
|
+
return Object.values(enumObject);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Asserts a literal tuple covers a string union exactly.
|
|
47
|
+
*
|
|
48
|
+
* Needed for union *types* (`'hard' | 'soft'`), which — unlike enums — have no
|
|
49
|
+
* runtime representation to derive from, so the members must be restated. This
|
|
50
|
+
* makes restating them safe: drop one and the argument becomes `never`.
|
|
51
|
+
*
|
|
52
|
+
* ```ts
|
|
53
|
+
* const WRAP = exhaustive<'hard' | 'soft'>()(['hard', 'soft'] as const);
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
export function exhaustive() {
|
|
57
|
+
return (entries) => entries;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Wraps a node as an absent-able member.
|
|
61
|
+
*
|
|
62
|
+
* Skillet has no `optional()`: every declared key must appear in the model's
|
|
63
|
+
* response. Optionality is therefore modelled as an explicit null branch — the
|
|
64
|
+
* key is always emitted, and `null` is how the model says "not set". Strip the
|
|
65
|
+
* nulls when converting a generated column into a `FormColumnInputs`.
|
|
66
|
+
*/
|
|
67
|
+
export function optional(node) {
|
|
68
|
+
return s.anyOf([node, s.nullish()]);
|
|
69
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Expressions this module could not read, exposed so a test can assert the set
|
|
3
|
+
* is empty.
|
|
4
|
+
*
|
|
5
|
+
* The grammar in use is closed — `key === value`, joined only by `||` — but it
|
|
6
|
+
* is a string, so nothing stops a future edit reaching for `&&` or `!==`.
|
|
7
|
+
* Rather than throw at import time and take a consumer's app down over editor
|
|
8
|
+
* copy, unreadable expressions are skipped and recorded here, which turns a
|
|
9
|
+
* silent loss of guidance into a failing test.
|
|
10
|
+
*/
|
|
11
|
+
export declare const UNREADABLE_EXPRESSIONS: string[];
|
|
12
|
+
/**
|
|
13
|
+
* Composes the description for a property: the authored sentence, then the
|
|
14
|
+
* editor's hint where it adds something, then when the property applies.
|
|
15
|
+
*
|
|
16
|
+
* @param base authored, model-facing description of the property
|
|
17
|
+
* @param path the property's `deepBind` path — a single key for a top-level
|
|
18
|
+
* member, a longer path for a member of a nested config
|
|
19
|
+
*/
|
|
20
|
+
export declare function describe(base: string, ...path: string[]): string;
|
|
21
|
+
/**
|
|
22
|
+
* Composes a description for a member of a record edited by a SECONDARY
|
|
23
|
+
* editor — a preset filter within `workflowPickerConfig.presetFilters`, say.
|
|
24
|
+
*
|
|
25
|
+
* @param base authored, model-facing description of the property
|
|
26
|
+
* @param opensAt the `deepBind` path of the row that opens the editor
|
|
27
|
+
* @param path the property's path within the record being edited
|
|
28
|
+
*/
|
|
29
|
+
export declare function describeIn(base: string, opensAt: readonly string[], ...path: string[]): string;
|
|
30
|
+
/**
|
|
31
|
+
* Numeric bounds the editor enforces on a property, ready to spread into a
|
|
32
|
+
* Skillet numeric node so the schema constrains what the settings panel does.
|
|
33
|
+
*/
|
|
34
|
+
export declare function rangeFor(...path: string[]): {
|
|
35
|
+
minimum?: number;
|
|
36
|
+
maximum?: number;
|
|
37
|
+
} | undefined;
|
|
38
|
+
/** Property paths the editor config carries guidance for. Used by tests. */
|
|
39
|
+
export declare const GUIDED_PATHS: readonly string[];
|
|
40
|
+
/**
|
|
41
|
+
* The top-level members the settings panel edits on EVERY element.
|
|
42
|
+
*
|
|
43
|
+
* A row named `SpecialElementKeys.Default` is not looked up in an element's
|
|
44
|
+
* `properties` at all: it is rendered for every element, gated only by its own
|
|
45
|
+
* `additionalTest` and `disabled` tests. So the members those rows edit are
|
|
46
|
+
* part of every element's surface in the builder while appearing in no
|
|
47
|
+
* element's property list — `readonly`, `onlySetTempErrorOnTouch` and
|
|
48
|
+
* `tourContent` among them. Read here so variant assembly can offer what the
|
|
49
|
+
* builder offers, from the same source the builder reads.
|
|
50
|
+
*
|
|
51
|
+
* Only ungated rows qualify. A gated row is conditional on a VALUE (`min`
|
|
52
|
+
* needs a numeric `type`; `calculatedFieldRules` needs the flag), which is not
|
|
53
|
+
* "always shown" and is handled elsewhere. Nested bindings (`script.onChange`,
|
|
54
|
+
* `matOptions.fetch.value.source`) are members of a nested object, not of the
|
|
55
|
+
* column, and are left to the modules that own those objects.
|
|
56
|
+
*/
|
|
57
|
+
export declare const ALWAYS_SHOWN_MEMBERS: readonly string[];
|
|
58
|
+
/**
|
|
59
|
+
* Secondary-editor paths, as `openingPath -> propertyPath`. Used by tests to
|
|
60
|
+
* assert the nested editors are actually being reached: before these were
|
|
61
|
+
* collected, every hint inside a record-list editor was invisible here, and
|
|
62
|
+
* nothing failed to say so.
|
|
63
|
+
*/
|
|
64
|
+
export declare const SECONDARY_GUIDED_PATHS: ReadonlyMap<string, readonly string[]>;
|