@marktiderman/genesis-forms 0.8.0 → 0.9.1
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/README.md +299 -10
- package/dist/destinations.cjs +331 -114
- package/dist/destinations.cjs.map +1 -1
- package/dist/destinations.d.cts +67 -45
- package/dist/destinations.d.ts +67 -45
- package/dist/destinations.js +327 -111
- package/dist/destinations.js.map +1 -1
- package/dist/{engine-Bj23YJlB.d.cts → engine-BaHPC2-1.d.cts} +2 -2
- package/dist/{engine-CzinTJph.d.ts → engine-Da99Tv-E.d.ts} +2 -2
- package/dist/index.cjs +996 -167
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +288 -11
- package/dist/index.d.ts +288 -11
- package/dist/index.js +975 -168
- package/dist/index.js.map +1 -1
- package/dist/server.cjs +563 -173
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +3 -474
- package/dist/server.d.ts +3 -474
- package/dist/server.js +558 -174
- package/dist/server.js.map +1 -1
- package/dist/submit-protocol-CFi-0Qd0.d.ts +840 -0
- package/dist/submit-protocol-D0cOckmK.d.cts +840 -0
- package/dist/testing.cjs +250 -154
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +4 -4
- package/dist/testing.d.ts +4 -4
- package/dist/testing.js +250 -154
- package/dist/testing.js.map +1 -1
- package/dist/{types-jqDKcmP4.d.cts → types-C0pUfew3.d.cts} +7 -1
- package/dist/{types-jqDKcmP4.d.ts → types-C0pUfew3.d.ts} +7 -1
- package/dist/{validate-DvfQHYnu.d.ts → validate-DpmBssFk.d.ts} +258 -140
- package/dist/{validate-irIPubxc.d.cts → validate-U5LoW3eR.d.cts} +258 -140
- package/package.json +3 -2
- package/dist/upload-protocol-BOe018kL.d.cts +0 -122
- package/dist/upload-protocol-BUO74WkS.d.ts +0 -122
|
@@ -1,146 +1,32 @@
|
|
|
1
|
-
import { J as Json,
|
|
1
|
+
import { J as Json, e as FormEvent, F as FormDefinition, a as FieldBlock, b as ContentBlock, g as Operator, B as Block, O as OptionsSource } from './types-C0pUfew3.cjs';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Checking a block's `props` for `parseForm`: the shared helpers every content and field kind's
|
|
5
|
+
* `checkProps` uses, so each kind states its own settings once and they are reported the same way.
|
|
6
6
|
*/
|
|
7
|
-
interface FieldTypeManifest {
|
|
8
|
-
type: string;
|
|
9
|
-
label: string;
|
|
10
|
-
category: "text" | "choice" | "scale" | "date" | "media" | "advanced";
|
|
11
|
-
/** `json: false` types must convert, so drafts and server replay never lose an answer. */
|
|
12
|
-
value: {
|
|
13
|
-
json: true;
|
|
14
|
-
} | {
|
|
15
|
-
json: false;
|
|
16
|
-
serialize: (value: unknown) => Json;
|
|
17
|
-
deserialize: (json: Json) => unknown;
|
|
18
|
-
};
|
|
19
|
-
operators: Operator[];
|
|
20
|
-
a11y: {
|
|
21
|
-
semantics: "control" | "group";
|
|
22
|
-
/** The HTML `autocomplete` token, such as `"email"` or `"tel"`. */
|
|
23
|
-
autocomplete?: string;
|
|
24
|
-
/** The HTML `inputmode` hint for on-screen keyboards, such as `"email"` or `"tel"`. */
|
|
25
|
-
inputMode?: "text" | "email" | "tel" | "numeric" | "decimal" | "url" | "search";
|
|
26
|
-
};
|
|
27
|
-
isEmpty?: (value: Json | undefined) => boolean;
|
|
28
|
-
/** Return an error message, or null when the (non-empty) value is valid. */
|
|
29
|
-
validate?: (value: Json, field: FieldBlock) => string | null;
|
|
30
|
-
/** Plain text for recall, exports and emails. */
|
|
31
|
-
summarize?: (value: Json, field: FieldBlock) => string;
|
|
32
|
-
/**
|
|
33
|
-
* The form a (valid, non-empty) value is stored in, such as an email
|
|
34
|
-
* without surrounding spaces. Applied by `engine.answers()` and the server.
|
|
35
|
-
*/
|
|
36
|
-
normalize?: (value: Json, field: FieldBlock) => Json;
|
|
37
|
-
/** Valid example answers, used by `coverage()` and other test tools to explore a path. */
|
|
38
|
-
samples?: (field: FieldBlock) => Json[];
|
|
39
|
-
}
|
|
40
|
-
declare function registerFieldType(manifest: FieldTypeManifest): void;
|
|
41
|
-
declare function getFieldType(type: string): FieldTypeManifest | undefined;
|
|
42
|
-
declare function listFieldTypes(): FieldTypeManifest[];
|
|
43
|
-
declare function isEmptyValue(value: Json | undefined, field?: FieldBlock): boolean;
|
|
44
|
-
/** A value in its stored form (see `FieldTypeManifest.normalize`). */
|
|
45
|
-
declare function normalizeValue(value: Json, field: FieldBlock): Json;
|
|
46
|
-
/** Whether a block holds personal or secret data: `sensitive: true`, or the `props.sensitive: true` alias. */
|
|
47
|
-
declare function isSensitive(block: Block): boolean;
|
|
48
|
-
declare function summarizeValue(value: Json | undefined, field: FieldBlock): string;
|
|
49
|
-
interface ChoiceOption {
|
|
50
|
-
value: string;
|
|
51
|
-
label?: string;
|
|
52
|
-
/** A muted line under the label, such as a location's address. At most `OPTION_LIMITS.description` characters. */
|
|
53
|
-
description?: string;
|
|
54
|
-
/** A small picture beside the label: an `https://` URL, at most `OPTION_LIMITS.image` characters. */
|
|
55
|
-
image?: string;
|
|
56
|
-
/**
|
|
57
|
-
* Facts about the option for filtering and recall, such as `{ location: ["riverside", "hilltop"] }`
|
|
58
|
-
* for a coach who works at two locations (see `props.optionsFrom.filter`). Names and values are
|
|
59
|
-
* text; a value is one text or a list of them.
|
|
60
|
-
*/
|
|
61
|
-
attributes?: Record<string, string | string[]>;
|
|
62
|
-
}
|
|
63
|
-
/** Bounds on an option's extra details. A longer or malformed detail is dropped when options are read. */
|
|
64
|
-
declare const OPTION_LIMITS: {
|
|
65
|
-
/** Characters in a description. */
|
|
66
|
-
readonly description: 500;
|
|
67
|
-
/** Characters in an image URL. */
|
|
68
|
-
readonly image: 2048;
|
|
69
|
-
/** Attributes on one option. */
|
|
70
|
-
readonly attributes: 20;
|
|
71
|
-
/** Characters in an attribute's name. */
|
|
72
|
-
readonly attributeName: 64;
|
|
73
|
-
/** Characters in one attribute value. */
|
|
74
|
-
readonly attributeValue: 200;
|
|
75
|
-
/** Values in one attribute's list. */
|
|
76
|
-
readonly attributeItems: 50;
|
|
77
|
-
};
|
|
78
7
|
/**
|
|
79
|
-
*
|
|
80
|
-
* `
|
|
8
|
+
* A kind's check of its `props` (an object, `{}` when the block has none). Report through
|
|
9
|
+
* `check`, with paths under `props` (`"max"`, `"columns[0].key"`); `parseForm` prefixes each
|
|
10
|
+
* with the block's (`blocks[3].props.`).
|
|
81
11
|
*/
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
*/
|
|
88
|
-
|
|
89
|
-
/** An
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
*/
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
} | null;
|
|
101
|
-
/** Whether a value is a typed "Other" answer: text that isn't one of the question's options. */
|
|
102
|
-
declare function isOtherValue(field: FieldBlock, value: unknown): value is string;
|
|
103
|
-
declare function choiceOptions(field: FieldBlock): ChoiceOption[];
|
|
104
|
-
/**
|
|
105
|
-
* Option lists the app showing a form supplies, by key (`OptionsSource.key`):
|
|
106
|
-
* `{ coaches: [{ value: "8c1…", label: "Ada Lovelace" }] }`.
|
|
107
|
-
*/
|
|
108
|
-
type HostOptions = Readonly<Record<string, readonly ChoiceOption[]>>;
|
|
109
|
-
/**
|
|
110
|
-
* A choice field's `props.optionsFrom`, when it is a well-formed `{ source: "host", key }`, with its
|
|
111
|
-
* `filter` when that is well formed too (`{ attribute, field }`, both non-empty text).
|
|
112
|
-
*/
|
|
113
|
-
declare function optionsSource(field: FieldBlock): OptionsSource | undefined;
|
|
114
|
-
/**
|
|
115
|
-
* The definition with each `optionsFrom: { source: "host" }` field's options
|
|
116
|
-
* filled in from `hostOptions`: its own `props.options` first, then the host's
|
|
117
|
-
* list for its key (a value the field already has is not added twice). A key
|
|
118
|
-
* the host does not supply adds nothing, so the field offers its own options
|
|
119
|
-
* only. `createForm` and `validateSubmission` call it with their `hostOptions`,
|
|
120
|
-
* so a respondent sees, and a submit accepts, exactly these. Returns
|
|
121
|
-
* `definition` itself when no field takes options from the host.
|
|
122
|
-
*/
|
|
123
|
-
declare function withHostOptions(definition: FormDefinition, hostOptions: HostOptions | undefined): FormDefinition;
|
|
124
|
-
/** The optional words under the ends of a rating or NPS scale ("Poor", "Great"). */
|
|
125
|
-
declare function scaleLabels(field: FieldBlock): {
|
|
126
|
-
low?: string;
|
|
127
|
-
high?: string;
|
|
128
|
-
};
|
|
129
|
-
/** How a rating is drawn: `"numbers"` (default) or `"stars"`. */
|
|
130
|
-
declare function ratingDisplay(field: FieldBlock): "numbers" | "stars";
|
|
131
|
-
/** A `number` field's limits: `min`, `max` and `step` (each optional; `step` is above 0). */
|
|
132
|
-
declare function numberLimits(field: FieldBlock): {
|
|
133
|
-
min?: number;
|
|
134
|
-
max?: number;
|
|
135
|
-
step?: number;
|
|
136
|
-
};
|
|
137
|
-
/** `YYYY-MM-DD`, a real calendar day (no 2026-02-30), years 0001 to 9999. */
|
|
138
|
-
declare function isIsoDate(value: string): boolean;
|
|
139
|
-
/** A `date` field's earliest and latest days (`YYYY-MM-DD`), when set. */
|
|
140
|
-
declare function dateLimits(field: FieldBlock): {
|
|
141
|
-
min?: string;
|
|
142
|
-
max?: string;
|
|
143
|
-
};
|
|
12
|
+
type PropsCheck = (props: Readonly<Record<string, unknown>>, check: PropsChecker) => void;
|
|
13
|
+
/** The helpers a `PropsCheck` reports through. Paths are relative to `props`. */
|
|
14
|
+
interface PropsChecker {
|
|
15
|
+
/** Report a problem with the setting at `path`. */
|
|
16
|
+
issue(path: string, message: string): void;
|
|
17
|
+
/** A required string (non-empty with `nonEmpty`). */
|
|
18
|
+
text(v: unknown, path: string, nonEmpty?: boolean): void;
|
|
19
|
+
/** An optional string. */
|
|
20
|
+
optionalText(v: unknown, path: string): void;
|
|
21
|
+
/** An optional true or false. */
|
|
22
|
+
optionalBoolean(v: unknown, path: string): void;
|
|
23
|
+
/** An optional whole number of at least `floor`. */
|
|
24
|
+
optionalInt(v: unknown, path: string, floor: number): void;
|
|
25
|
+
/** `low` may not exceed `high` when both are whole numbers. */
|
|
26
|
+
ordered(low: unknown, high: unknown, path: string, what: string): void;
|
|
27
|
+
/** An identifier: a non-empty string that is not an `Object.prototype` name. */
|
|
28
|
+
name(v: unknown, path: string, what: string): v is string;
|
|
29
|
+
}
|
|
144
30
|
|
|
145
31
|
/**
|
|
146
32
|
* Which options a choice question shows, and how they are picked: lists filtered by another
|
|
@@ -149,7 +35,10 @@ declare function dateLimits(field: FieldBlock): {
|
|
|
149
35
|
* decide the same way.
|
|
150
36
|
*/
|
|
151
37
|
|
|
152
|
-
/**
|
|
38
|
+
/**
|
|
39
|
+
* The built-in field types whose answer is one or more of their options. A host's own choice
|
|
40
|
+
* kinds (`FieldTypeManifest.choice`) aren't in it: ask `isChoiceField` or `choiceKind`.
|
|
41
|
+
*/
|
|
153
42
|
declare const CHOICE_FIELD_TYPES: ReadonlySet<string>;
|
|
154
43
|
/** A choice field's `props.optionsFrom.filter` (`{ attribute, field }`), when it is well formed. */
|
|
155
44
|
declare function optionFilter(field: FieldBlock): {
|
|
@@ -292,6 +181,235 @@ interface RestoredAnswers {
|
|
|
292
181
|
*/
|
|
293
182
|
declare function answersFromSnapshots(definition: FormDefinition, snapshots: Readonly<Record<string, unknown>>): RestoredAnswers;
|
|
294
183
|
|
|
184
|
+
/** The JSON type a comparison literal must have to match an answer (`parseForm` warns otherwise). */
|
|
185
|
+
type LiteralType = "string" | "number" | "boolean";
|
|
186
|
+
/**
|
|
187
|
+
* A field kind, without any UI: the one definition the engine, `parseForm`, validation, the
|
|
188
|
+
* server and snapshots all read, so a kind is described once instead of in a switch in each.
|
|
189
|
+
* Renderers are registered separately per platform (`genesis-forms-react`), so a manifest runs
|
|
190
|
+
* anywhere. Every hook is optional except where noted; a missing one means "nothing special".
|
|
191
|
+
* `validate`, `checkProps`, `literal`, `snapshot` and `fromSnapshot` may throw (each fails safe,
|
|
192
|
+
* as its doc says); `isEmpty`, `normalize` and `summarize` must not.
|
|
193
|
+
*/
|
|
194
|
+
interface FieldTypeManifest {
|
|
195
|
+
type: string;
|
|
196
|
+
label: string;
|
|
197
|
+
category: "text" | "choice" | "scale" | "date" | "media" | "advanced";
|
|
198
|
+
/** `json: false` types must convert, so drafts and server replay never lose an answer. */
|
|
199
|
+
value: {
|
|
200
|
+
json: true;
|
|
201
|
+
} | {
|
|
202
|
+
json: false;
|
|
203
|
+
serialize: (value: unknown) => Json;
|
|
204
|
+
deserialize: (json: Json) => unknown;
|
|
205
|
+
};
|
|
206
|
+
operators: Operator[];
|
|
207
|
+
a11y: {
|
|
208
|
+
semantics: "control" | "group";
|
|
209
|
+
/** The HTML `autocomplete` token, such as `"email"` or `"tel"`. */
|
|
210
|
+
autocomplete?: string;
|
|
211
|
+
/** The HTML `inputmode` hint for on-screen keyboards, such as `"email"` or `"tel"`. */
|
|
212
|
+
inputMode?: "text" | "email" | "tel" | "numeric" | "decimal" | "url" | "search";
|
|
213
|
+
};
|
|
214
|
+
/**
|
|
215
|
+
* The props schema: `parseForm` calls it with the field's `props` (`{}` when it has none) and
|
|
216
|
+
* reports what it finds as issues at `blocks[i].props.<path>`. A check that throws is reported
|
|
217
|
+
* as one issue, never thrown.
|
|
218
|
+
*/
|
|
219
|
+
checkProps?: PropsCheck;
|
|
220
|
+
/**
|
|
221
|
+
* The answer's shape. `choice`: the answer is one of `props.options` (`multiple: false`, a
|
|
222
|
+
* text) or several (`multiple: true`, a list of texts), so the field takes `optionsFrom`,
|
|
223
|
+
* `prefill`, `other` and filters, and its answers are snapshotted with their labels. Without
|
|
224
|
+
* its own `validate` (or `summarize`) it gets the built-in one for its shape, so an answer is
|
|
225
|
+
* always one of its options (or a typed "Other" when `props.other` allows it).
|
|
226
|
+
*/
|
|
227
|
+
choice?: {
|
|
228
|
+
multiple: boolean;
|
|
229
|
+
};
|
|
230
|
+
/**
|
|
231
|
+
* The JSON type a logic literal compared with this field must have, or a function of its props
|
|
232
|
+
* (`undefined`: not known). `parseForm` warns about a literal that can never match.
|
|
233
|
+
*/
|
|
234
|
+
literal?: LiteralType | ((props: Readonly<Record<string, unknown>>) => LiteralType | undefined);
|
|
235
|
+
/** The empty-check: whether a value counts as no answer. Default: nothing, null, blank text or `[]`. */
|
|
236
|
+
isEmpty?: (value: Json | undefined) => boolean;
|
|
237
|
+
/**
|
|
238
|
+
* The validator: an error message, or null when the (non-empty) value is valid. A validator
|
|
239
|
+
* that throws refuses the answer (`VALIDATOR_FAILED_MESSAGE`) rather than throwing.
|
|
240
|
+
*/
|
|
241
|
+
validate?: (value: Json, field: FieldBlock) => string | null;
|
|
242
|
+
/** The display text: plain text for recall, exports and emails. */
|
|
243
|
+
summarize?: (value: Json, field: FieldBlock) => string;
|
|
244
|
+
/**
|
|
245
|
+
* The form a (valid, non-empty) value is stored in, such as an email
|
|
246
|
+
* without surrounding spaces. Applied by `engine.answers()` and the server.
|
|
247
|
+
*/
|
|
248
|
+
normalize?: (value: Json, field: FieldBlock) => Json;
|
|
249
|
+
/**
|
|
250
|
+
* The snapshot of a (non-empty) answer as it was when stored, for `answerSnapshots`: the
|
|
251
|
+
* labels it showed, so history keeps them when the source changes. Choice kinds have one
|
|
252
|
+
* built in (for fields with `optionsFrom`); give another kind one here.
|
|
253
|
+
*/
|
|
254
|
+
snapshot?: (value: Json, field: FieldBlock) => AnswerSnapshot | undefined;
|
|
255
|
+
/**
|
|
256
|
+
* The reverse of `snapshot`, for `answersFromSnapshots`: the answer to prefill from a stored
|
|
257
|
+
* snapshot (or none), and the picks that no longer exist.
|
|
258
|
+
*/
|
|
259
|
+
fromSnapshot?: (snapshot: unknown, field: FieldBlock) => {
|
|
260
|
+
value?: Json;
|
|
261
|
+
missing: OptionSnapshot[];
|
|
262
|
+
};
|
|
263
|
+
/** Valid example answers, used by `coverage()` and other test tools to explore a path. */
|
|
264
|
+
samples?: (field: FieldBlock) => Json[];
|
|
265
|
+
}
|
|
266
|
+
/** The message an answer gets when its kind's validator throws. */
|
|
267
|
+
declare const VALIDATOR_FAILED_MESSAGE = "This answer couldn't be checked.";
|
|
268
|
+
interface RegisterOptions {
|
|
269
|
+
/**
|
|
270
|
+
* Replace a built-in kind (`email`, `single-choice`…). Off by default: the registry is shared
|
|
271
|
+
* by every form in the process, the server's checks included, so changing what a built-in
|
|
272
|
+
* accepts must be deliberate.
|
|
273
|
+
*/
|
|
274
|
+
replace?: boolean;
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Register a field kind, or replace one a host registered before (re-registering is allowed,
|
|
278
|
+
* so hot reload works). Throws on a malformed manifest, and on a built-in kind's name unless
|
|
279
|
+
* `{ replace: true }`. Registered kinds are frozen copies.
|
|
280
|
+
*/
|
|
281
|
+
declare function registerFieldType(manifest: FieldTypeManifest, options?: RegisterOptions): void;
|
|
282
|
+
declare function getFieldType(type: string): FieldTypeManifest | undefined;
|
|
283
|
+
declare function listFieldTypes(): FieldTypeManifest[];
|
|
284
|
+
/** Whether `type` is one of the field kinds the package ships. */
|
|
285
|
+
declare function isBuiltInFieldType(type: string): boolean;
|
|
286
|
+
/** A choice kind's answer shape (`FieldTypeManifest.choice`), or undefined for any other kind. */
|
|
287
|
+
declare function choiceKind(type: string): {
|
|
288
|
+
multiple: boolean;
|
|
289
|
+
} | undefined;
|
|
290
|
+
/** Whether a block is a field of a choice kind: its answer is one or more of its options. */
|
|
291
|
+
declare function isChoiceField(block: unknown): block is FieldBlock;
|
|
292
|
+
/**
|
|
293
|
+
* A content kind (a block that shows something and takes no answer): `heading` and `text` are
|
|
294
|
+
* built in; a host registers others (a message screen, a media block) with
|
|
295
|
+
* `registerContentType`, plus a renderer per platform.
|
|
296
|
+
*/
|
|
297
|
+
interface ContentTypeManifest {
|
|
298
|
+
type: string;
|
|
299
|
+
label: string;
|
|
300
|
+
/** The props schema, as for a field kind; the block's `props`, `{}` when it has none. */
|
|
301
|
+
checkProps?: PropsCheck;
|
|
302
|
+
/** The display text (recall, exports, a renderer with no special drawing). Default: `block.text`. */
|
|
303
|
+
text?: (block: ContentBlock) => string;
|
|
304
|
+
}
|
|
305
|
+
/** Register a content kind. Throws on a malformed manifest or a built-in kind's name (unless `{ replace: true }`). */
|
|
306
|
+
declare function registerContentType(manifest: ContentTypeManifest, options?: RegisterOptions): void;
|
|
307
|
+
declare function getContentType(type: string): ContentTypeManifest | undefined;
|
|
308
|
+
declare function listContentTypes(): ContentTypeManifest[];
|
|
309
|
+
/** A content block's display text: its kind's `text` (when it gives a string without throwing), or `block.text`. */
|
|
310
|
+
declare function contentText(block: ContentBlock): string;
|
|
311
|
+
declare function isEmptyValue(value: Json | undefined, field?: FieldBlock): boolean;
|
|
312
|
+
/** A value in its stored form (see `FieldTypeManifest.normalize`). */
|
|
313
|
+
declare function normalizeValue(value: Json, field: FieldBlock): Json;
|
|
314
|
+
/** Whether a block holds personal or secret data: `sensitive: true`, or the `props.sensitive: true` alias. */
|
|
315
|
+
declare function isSensitive(block: Block): boolean;
|
|
316
|
+
declare function summarizeValue(value: Json | undefined, field: FieldBlock): string;
|
|
317
|
+
interface ChoiceOption {
|
|
318
|
+
value: string;
|
|
319
|
+
label?: string;
|
|
320
|
+
/** A muted line under the label, such as a location's address. At most `OPTION_LIMITS.description` characters. */
|
|
321
|
+
description?: string;
|
|
322
|
+
/** A small picture beside the label: an `https://` URL, at most `OPTION_LIMITS.image` characters. */
|
|
323
|
+
image?: string;
|
|
324
|
+
/**
|
|
325
|
+
* Facts about the option for filtering and recall, such as `{ location: ["riverside", "hilltop"] }`
|
|
326
|
+
* for a coach who works at two locations (see `props.optionsFrom.filter`). Names and values are
|
|
327
|
+
* text; a value is one text or a list of them.
|
|
328
|
+
*/
|
|
329
|
+
attributes?: Record<string, string | string[]>;
|
|
330
|
+
}
|
|
331
|
+
/** Bounds on an option's extra details. A longer or malformed detail is dropped when options are read. */
|
|
332
|
+
declare const OPTION_LIMITS: {
|
|
333
|
+
/** Characters in a description. */
|
|
334
|
+
readonly description: 500;
|
|
335
|
+
/** Characters in an image URL. */
|
|
336
|
+
readonly image: 2048;
|
|
337
|
+
/** Attributes on one option. */
|
|
338
|
+
readonly attributes: 20;
|
|
339
|
+
/** Characters in an attribute's name. */
|
|
340
|
+
readonly attributeName: 64;
|
|
341
|
+
/** Characters in one attribute value. */
|
|
342
|
+
readonly attributeValue: 200;
|
|
343
|
+
/** Values in one attribute's list. */
|
|
344
|
+
readonly attributeItems: 50;
|
|
345
|
+
};
|
|
346
|
+
/**
|
|
347
|
+
* Whether `url` is an `https://` link with a host and no spaces or control characters, within
|
|
348
|
+
* `OPTION_LIMITS.image`. A pattern rather than `new URL`, so it behaves the same in React Native.
|
|
349
|
+
*/
|
|
350
|
+
declare function isHttpsUrl(url: unknown): url is string;
|
|
351
|
+
/**
|
|
352
|
+
* An option from untrusted JSON (a definition, the host's list): its string `value`, and its
|
|
353
|
+
* `label`, `description`, `image` and `attributes` when each is well formed and within
|
|
354
|
+
* `OPTION_LIMITS`; anything else is left out. Null when there is no string value.
|
|
355
|
+
*/
|
|
356
|
+
declare function readOption(raw: unknown): ChoiceOption | null;
|
|
357
|
+
/** An option as JSON, with only the details it has. */
|
|
358
|
+
declare function optionJson(option: ChoiceOption): Json;
|
|
359
|
+
/** The longest answer an "Other" option takes. */
|
|
360
|
+
declare const OTHER_MAX_LENGTH = 200;
|
|
361
|
+
/**
|
|
362
|
+
* A choice question's "Other" option (`props.other: true` or `{ label }`): the respondent types
|
|
363
|
+
* their own answer, stored as the answer's value itself (single choice, dropdown) or as one more
|
|
364
|
+
* item (multiple choice). Null when the question has none.
|
|
365
|
+
*/
|
|
366
|
+
declare function otherOption(field: FieldBlock): {
|
|
367
|
+
label: string;
|
|
368
|
+
} | null;
|
|
369
|
+
/** Whether a value is a typed "Other" answer: text that isn't one of the question's options. */
|
|
370
|
+
declare function isOtherValue(field: FieldBlock, value: unknown): value is string;
|
|
371
|
+
declare function choiceOptions(field: FieldBlock): ChoiceOption[];
|
|
372
|
+
/**
|
|
373
|
+
* Option lists the app showing a form supplies, by key (`OptionsSource.key`):
|
|
374
|
+
* `{ coaches: [{ value: "8c1…", label: "Ada Lovelace" }] }`.
|
|
375
|
+
*/
|
|
376
|
+
type HostOptions = Readonly<Record<string, readonly ChoiceOption[]>>;
|
|
377
|
+
/**
|
|
378
|
+
* A choice field's `props.optionsFrom`, when it is a well-formed `{ source: "host", key }`, with its
|
|
379
|
+
* `filter` when that is well formed too (`{ attribute, field }`, both non-empty text).
|
|
380
|
+
*/
|
|
381
|
+
declare function optionsSource(field: FieldBlock): OptionsSource | undefined;
|
|
382
|
+
/**
|
|
383
|
+
* The definition with each `optionsFrom: { source: "host" }` field's options
|
|
384
|
+
* filled in from `hostOptions`: its own `props.options` first, then the host's
|
|
385
|
+
* list for its key (a value the field already has is not added twice). A key
|
|
386
|
+
* the host does not supply adds nothing, so the field offers its own options
|
|
387
|
+
* only. `createForm` and `validateSubmission` call it with their `hostOptions`,
|
|
388
|
+
* so a respondent sees, and a submit accepts, exactly these. Returns
|
|
389
|
+
* `definition` itself when no field takes options from the host.
|
|
390
|
+
*/
|
|
391
|
+
declare function withHostOptions(definition: FormDefinition, hostOptions: HostOptions | undefined): FormDefinition;
|
|
392
|
+
/** The optional words under the ends of a rating or NPS scale ("Poor", "Great"). */
|
|
393
|
+
declare function scaleLabels(field: FieldBlock): {
|
|
394
|
+
low?: string;
|
|
395
|
+
high?: string;
|
|
396
|
+
};
|
|
397
|
+
/** How a rating is drawn: `"numbers"` (default) or `"stars"`. */
|
|
398
|
+
declare function ratingDisplay(field: FieldBlock): "numbers" | "stars";
|
|
399
|
+
/** A `number` field's limits: `min`, `max` and `step` (each optional; `step` is above 0). */
|
|
400
|
+
declare function numberLimits(field: FieldBlock): {
|
|
401
|
+
min?: number;
|
|
402
|
+
max?: number;
|
|
403
|
+
step?: number;
|
|
404
|
+
};
|
|
405
|
+
/** `YYYY-MM-DD`, a real calendar day (no 2026-02-30), years 0001 to 9999. */
|
|
406
|
+
declare function isIsoDate(value: string): boolean;
|
|
407
|
+
/** A `date` field's earliest and latest days (`YYYY-MM-DD`), when set. */
|
|
408
|
+
declare function dateLimits(field: FieldBlock): {
|
|
409
|
+
min?: string;
|
|
410
|
+
max?: string;
|
|
411
|
+
};
|
|
412
|
+
|
|
295
413
|
/** The Standard Schema interface (standardschema.dev), as far as validation needs it. */
|
|
296
414
|
interface StandardSchemaV1<T = unknown> {
|
|
297
415
|
readonly "~standard": {
|
|
@@ -319,4 +437,4 @@ declare function validateField(field: FieldBlock, value: Json | undefined, optio
|
|
|
319
437
|
custom?: FieldValidator;
|
|
320
438
|
}): string | null;
|
|
321
439
|
|
|
322
|
-
export {
|
|
440
|
+
export { otherOption as $, type AnswerSnapshot as A, isChoiceField as B, CHOICE_FIELD_TYPES as C, isEmptyValue as D, isHttpsUrl as E, type FieldValidator as F, isIsoDate as G, type HostOptions as H, isOtherValue as I, isPrefillParam as J, isSearchable as K, type LinkParams as L, MAX_PREFILL_PARAM_LENGTH as M, isSensitive as N, OPTION_LIMITS as O, type PrefillSetting as P, listContentTypes as Q, REQUIRED_MESSAGE as R, SEARCHABLE_OPTION_COUNT as S, listFieldTypes as T, normalizeValue as U, VALIDATOR_FAILED_MESSAGE as V, numberLimits as W, optionFilter as X, optionJson as Y, optionMatches as Z, optionsSource as _, type ChoiceOption as a, prefillFromLink as a0, prefillSetting as a1, ratingDisplay as a2, readOption as a3, registerContentType as a4, registerFieldType as a5, respondentPath as a6, scaleLabels as a7, searchOptions as a8, summarizeValue as a9, validateField as aa, visibleOptions as ab, withHostOptions as ac, type ContentTypeManifest as b, type FieldTypeManifest as c, type LiteralType as d, OTHER_MAX_LENGTH as e, type OptionSnapshot as f, type PrefillState as g, type PropsCheck as h, type PropsChecker as i, type RegisterOptions as j, type RestoredAnswers as k, type StandardSchemaV1 as l, answerSnapshots as m, answersFromSnapshots as n, answersOnPath as o, choiceKind as p, choiceOptions as q, clearFilteredAnswers as r, contentText as s, dateLimits as t, filteredChoiceError as u, getContentType as v, getFieldType as w, hasOptionFilters as x, hiddenByPrefill as y, isBuiltInFieldType as z };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@marktiderman/genesis-forms",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": [
|
|
@@ -78,9 +78,10 @@
|
|
|
78
78
|
],
|
|
79
79
|
"devDependencies": {
|
|
80
80
|
"@fast-check/vitest": "^0.2.4",
|
|
81
|
+
"@marktiderman/genesis-notifications": "^0.1.1",
|
|
81
82
|
"fast-check": "^4.10.2",
|
|
82
83
|
"tsup": "^8.3.0",
|
|
83
|
-
"typescript": "
|
|
84
|
+
"typescript": "~6.0.3",
|
|
84
85
|
"vitest": "^4.1.12"
|
|
85
86
|
},
|
|
86
87
|
"scripts": {
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
import { a as FieldBlock, J as Json } from './types-jqDKcmP4.cjs';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* The `photo` field type: a respondent attaches up to a few photos, such as a
|
|
5
|
-
* picture of broken equipment. The answer holds references the host's storage
|
|
6
|
-
* handed back (`PhotoRef`), never the bytes: the renderer uploads through the
|
|
7
|
-
* host (`onUpload` on genesis-forms-web's `Form`), so the engine and the
|
|
8
|
-
* server stay storage-agnostic. A host's server should check that each
|
|
9
|
-
* reference is a file it stored for this respondent before saving.
|
|
10
|
-
*
|
|
11
|
-
* Opt-in in a builder palette (`UPLOAD_FIELD_TYPES`), since a host must be
|
|
12
|
-
* able to store the files.
|
|
13
|
-
*/
|
|
14
|
-
declare const PHOTO_TYPE = "photo";
|
|
15
|
-
/** Field types that need the host to store files; a builder offers them only when the host names them. */
|
|
16
|
-
declare const UPLOAD_FIELD_TYPES: readonly ["photo"];
|
|
17
|
-
/** One stored photo, as the host's upload returned it. */
|
|
18
|
-
type PhotoRef = {
|
|
19
|
-
/** The host's id for the stored file. */
|
|
20
|
-
id: string;
|
|
21
|
-
/** The file's name on the respondent's device. */
|
|
22
|
-
name: string;
|
|
23
|
-
/** Its MIME type, such as `image/jpeg`. */
|
|
24
|
-
type: string;
|
|
25
|
-
/** Bytes. */
|
|
26
|
-
size: number;
|
|
27
|
-
};
|
|
28
|
-
type PhotoFormat = "jpeg" | "png" | "heic" | "webp";
|
|
29
|
-
/** The MIME types each format covers. */
|
|
30
|
-
declare const PHOTO_FORMATS: Record<PhotoFormat, readonly string[]>;
|
|
31
|
-
declare const DEFAULT_PHOTO_FILES = 3;
|
|
32
|
-
declare const MAX_PHOTO_FILES = 5;
|
|
33
|
-
declare const DEFAULT_PHOTO_SIZE_MB = 10;
|
|
34
|
-
declare const MAX_PHOTO_SIZE_MB = 25;
|
|
35
|
-
/** The longest reference id and file name a photo answer may carry. */
|
|
36
|
-
declare const MAX_PHOTO_ID_LENGTH = 200;
|
|
37
|
-
declare const MAX_PHOTO_NAME_LENGTH = 255;
|
|
38
|
-
declare const isPhotoFormat: (v: unknown) => v is PhotoFormat;
|
|
39
|
-
interface PhotoLimits {
|
|
40
|
-
maxFiles: number;
|
|
41
|
-
maxSizeMb: number;
|
|
42
|
-
maxBytes: number;
|
|
43
|
-
formats: PhotoFormat[];
|
|
44
|
-
/** Every MIME type the formats cover. */
|
|
45
|
-
mimeTypes: string[];
|
|
46
|
-
}
|
|
47
|
-
/** A photo field's limits; a prop that is missing or out of range uses its default. */
|
|
48
|
-
declare function photoLimits(field: FieldBlock): PhotoLimits;
|
|
49
|
-
/** Whether `v` has a photo reference's shape (any type and size). */
|
|
50
|
-
declare function isPhotoRef(v: unknown): v is PhotoRef;
|
|
51
|
-
/** The well-formed references in a photo answer, as `{ id, name, type, size }` only. */
|
|
52
|
-
declare function photoRefs(value: Json | undefined): PhotoRef[];
|
|
53
|
-
/** "JPEG, PNG or WebP". */
|
|
54
|
-
declare function photoFormatList(formats: readonly PhotoFormat[]): string;
|
|
55
|
-
/** The message for a file of a type the field doesn't take. */
|
|
56
|
-
declare const photoTypeMessage: (field: FieldBlock) => string;
|
|
57
|
-
/** The message for a file over the field's size limit. */
|
|
58
|
-
declare const photoSizeMessage: (field: FieldBlock) => string;
|
|
59
|
-
/** The message for more photos than the field takes. */
|
|
60
|
-
declare const photoCountMessage: (field: FieldBlock) => string;
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* The upload handshake's JSON, shared by the server (`createUploadHandler` in
|
|
64
|
-
* `/server`) and the browser (genesis-forms-web's `signedUploadHost`). A
|
|
65
|
-
* respondent's browser asks the app's server for one signed upload per file,
|
|
66
|
-
* then sends the bytes straight to storage with it; the answer keeps only the
|
|
67
|
-
* reference the server handed back.
|
|
68
|
-
*
|
|
69
|
-
* Moved up from the host app's feedback photo uploads; the field names and
|
|
70
|
-
* status codes are the ones it used, so its browsers and server keep talking.
|
|
71
|
-
*/
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* What the browser POSTs (`content-type: application/json`, at most
|
|
75
|
-
* `MAX_UPLOAD_REQUEST_BYTES`). Exactly one of `attemptId` (a new response: the
|
|
76
|
-
* visit's attempt id, a UUID) and `edit` (an edit link's token, while changing
|
|
77
|
-
* a sent response).
|
|
78
|
-
*/
|
|
79
|
-
interface UploadRequest {
|
|
80
|
-
/** The upload field's id. */
|
|
81
|
-
fieldId: string;
|
|
82
|
-
/** The file's name on the respondent's device. */
|
|
83
|
-
name: string;
|
|
84
|
-
/** Its MIME type. */
|
|
85
|
-
type: string;
|
|
86
|
-
/** Bytes. */
|
|
87
|
-
size: number;
|
|
88
|
-
attemptId?: string | undefined;
|
|
89
|
-
edit?: string | undefined;
|
|
90
|
-
}
|
|
91
|
-
/**
|
|
92
|
-
* Where the browser sends the bytes, as the app's storage signed it. Supabase
|
|
93
|
-
* Storage hands back `path` and `token` (with `bucket`, for
|
|
94
|
-
* `uploadToSignedUrl`); a store that signs a plain URL sets `url`, which
|
|
95
|
-
* `signedUploadHost` PUTs the file to when no `send` is given.
|
|
96
|
-
*/
|
|
97
|
-
interface SignedUpload {
|
|
98
|
-
path: string;
|
|
99
|
-
token: string;
|
|
100
|
-
bucket?: string | undefined;
|
|
101
|
-
url?: string | undefined;
|
|
102
|
-
}
|
|
103
|
-
/** 200: the reference the answer keeps, and where to send the bytes. */
|
|
104
|
-
interface UploadGrant {
|
|
105
|
-
ref: PhotoRef;
|
|
106
|
-
upload: SignedUpload;
|
|
107
|
-
}
|
|
108
|
-
/**
|
|
109
|
-
* Any other status: words to show the respondent, and for some a `code`:
|
|
110
|
-
* `closed` (409, an edit while the form takes no changes), `bad_link` (403, an
|
|
111
|
-
* edit link that doesn't check out) or `too_many_photos` (429, the attempt or
|
|
112
|
-
* edit has as many uploads as the store allows; the name the host app's
|
|
113
|
-
* browsers already know, kept for every upload field type).
|
|
114
|
-
*/
|
|
115
|
-
interface UploadRefusal {
|
|
116
|
-
error: string;
|
|
117
|
-
code?: string | undefined;
|
|
118
|
-
}
|
|
119
|
-
/** The longest upload request body: a few fields of JSON. */
|
|
120
|
-
declare const MAX_UPLOAD_REQUEST_BYTES = 4000;
|
|
121
|
-
|
|
122
|
-
export { DEFAULT_PHOTO_FILES as D, MAX_PHOTO_FILES as M, type PhotoFormat as P, type SignedUpload as S, UPLOAD_FIELD_TYPES as U, type PhotoRef as a, DEFAULT_PHOTO_SIZE_MB as b, MAX_PHOTO_ID_LENGTH as c, MAX_PHOTO_NAME_LENGTH as d, MAX_PHOTO_SIZE_MB as e, MAX_UPLOAD_REQUEST_BYTES as f, PHOTO_FORMATS as g, PHOTO_TYPE as h, type PhotoLimits as i, type UploadGrant as j, type UploadRefusal as k, type UploadRequest as l, isPhotoFormat as m, isPhotoRef as n, photoFormatList as o, photoCountMessage as p, photoLimits as q, photoRefs as r, photoSizeMessage as s, photoTypeMessage as t };
|