@case-framework/survey-core 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +126 -244
- package/build/answer-types-DH0gv0Ea.d.mts +20 -0
- package/build/answer-types-DH0gv0Ea.d.mts.map +1 -0
- package/build/answer-types-Dtm6MqZQ.mjs +19 -0
- package/build/answer-types-Dtm6MqZQ.mjs.map +1 -0
- package/build/editor/colors.d.mts +11 -2
- package/build/editor/colors.d.mts.map +1 -0
- package/build/editor.d.mts +72913 -266
- package/build/editor.d.mts.map +1 -1
- package/build/editor.mjs +1268 -1001
- package/build/editor.mjs.map +1 -1
- package/build/index.d.mts +2 -3
- package/build/index.mjs +1620 -824
- package/build/index.mjs.map +1 -1
- package/build/keyed-object-D1yh_W5v.mjs +45 -0
- package/build/keyed-object-D1yh_W5v.mjs.map +1 -0
- package/build/localization-1lzNpyFX.mjs +1373 -0
- package/build/localization-1lzNpyFX.mjs.map +1 -0
- package/build/package.json +9 -3
- package/build/projection-DrLPFroE.mjs +2344 -0
- package/build/projection-DrLPFroE.mjs.map +1 -0
- package/build/registry-BOXcyWfX.d.mts +54292 -0
- package/build/registry-BOXcyWfX.d.mts.map +1 -0
- package/build/schema.d.mts +3 -0
- package/build/schema.mjs +4 -0
- package/package.json +9 -3
- package/build/index-SDbZ5WQM.d.mts +0 -2208
- package/build/index-SDbZ5WQM.d.mts.map +0 -1
- package/build/item-colors-DgoJeSCQ.d.mts +0 -12
- package/build/item-colors-DgoJeSCQ.d.mts.map +0 -1
- package/build/projection-Bl6qCinx.mjs +0 -3930
- package/build/projection-Bl6qCinx.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -1,227 +1,138 @@
|
|
|
1
1
|
# @case-framework/survey-core
|
|
2
2
|
|
|
3
|
-
Headless TypeScript
|
|
3
|
+
Headless TypeScript contracts for defining, editing, running, and exporting CASE
|
|
4
|
+
surveys. No React, network, persistence, or assistant dependency is required.
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
See [Composable architecture](https://github.com/case-framework/case-survey-toolkit/blob/main/docs/composition-architecture.md) for the
|
|
7
|
+
complete document, identity, localization, expression, and host contracts.
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
## Public entry points
|
|
8
10
|
|
|
9
|
-
-
|
|
10
|
-
|
|
11
|
-
-
|
|
12
|
-
|
|
11
|
+
- `@case-framework/survey-core`: schemas, `Survey`, element registration,
|
|
12
|
+
`SurveyStateGraph`, `SurveySession`, expressions, response values, and export.
|
|
13
|
+
- `@case-framework/survey-core/editor`: `SurveyEditor`, field/history integration,
|
|
14
|
+
copy/remapping, and response naming.
|
|
15
|
+
- `@case-framework/survey-core/editor/colors`: editor color helpers.
|
|
16
|
+
- `@case-framework/survey-core/schema`: data schemas for protocol consumers.
|
|
13
17
|
|
|
14
|
-
|
|
18
|
+
The survey format is version 3; submitted responses are version 2; retained
|
|
19
|
+
participant session snapshots are version 1. Unsupported formats are rejected.
|
|
20
|
+
There are no legacy readers or converters.
|
|
15
21
|
|
|
16
|
-
|
|
22
|
+
## Breaking changes
|
|
17
23
|
|
|
18
|
-
|
|
19
|
-
- Item contracts and registry utilities (`SurveyItemCore`, `createItemCore`, `createFullRegistry`).
|
|
20
|
-
- Runtime engine (`SurveyEngineCore`) for render order, conditions, template values, events, and responses.
|
|
21
|
-
- Expression model + evaluator (`Expression`, `FunctionExpression`, `ExpressionEvaluator`, etc.).
|
|
22
|
-
- Response models (`SurveyResponse`, `SurveyItemResponse`, `ResponseItem`).
|
|
23
|
-
- Response export tooling (`SurveyResponseExporter`, CSV helpers, codebook generation).
|
|
24
|
-
- Editor state/manipulation APIs via the `@case-framework/survey-core/editor` entrypoint.
|
|
24
|
+
The composition cutover intentionally replaces these APIs; there are no aliases:
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
- `SurveyEditor.undoRedo` is now `SurveyEditor.history`.
|
|
27
|
+
- The former `survey`, `survey/utils`, `expressions`, and `engine` source barrels
|
|
28
|
+
are consolidated in the root export. Use the public entry points above.
|
|
29
|
+
- Import `SURVEY_EDITOR_ITEM_COLORS` from `@case-framework/survey-core/editor/colors`,
|
|
30
|
+
rather than the package root.
|
|
27
31
|
|
|
28
|
-
|
|
29
|
-
- Network or persistence layers.
|
|
30
|
-
- App-level state management.
|
|
32
|
+
Survey Mode must update its imports and history access when adopting this version.
|
|
31
33
|
|
|
32
|
-
|
|
34
|
+
## Load and run
|
|
33
35
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
```ts
|
|
37
|
+
import { Survey, SurveySession } from "@case-framework/survey-core";
|
|
38
|
+
import { defaultElementRegistry } from "@case-framework/survey-items";
|
|
39
|
+
|
|
40
|
+
const survey = Survey.parse(savedDefinition, defaultElementRegistry);
|
|
41
|
+
const session = await SurveySession.create(survey, {
|
|
42
|
+
mode: "new",
|
|
43
|
+
binding: { sessionId: "session-1", definitionId: "release-1" },
|
|
44
|
+
context: { locale: "en" },
|
|
45
|
+
});
|
|
46
|
+
session.write([{ slotId: "name-slot", value: { type: "string", value: "Example" } }]);
|
|
47
|
+
const submission = session.submission("response-1");
|
|
48
|
+
const snapshot = session.snapshot();
|
|
49
|
+
const resumed = await SurveySession.create(survey, {
|
|
50
|
+
mode: "resumed",
|
|
51
|
+
binding: { sessionId: "session-1", definitionId: "release-1" },
|
|
52
|
+
snapshot,
|
|
53
|
+
});
|
|
38
54
|
```
|
|
39
55
|
|
|
40
|
-
|
|
56
|
+
The example slot must be declared by the loaded definition. Writes reject unknown
|
|
57
|
+
slots, incompatible values, computed targets, and disabled controls. Hidden answers
|
|
58
|
+
are retained but excluded from ordinary expressions and submission. Disabled
|
|
59
|
+
visible answers remain active. Prefills run only for new sessions. Resume checks
|
|
60
|
+
binding and restores retained values without reapplying prefills.
|
|
41
61
|
|
|
42
|
-
|
|
43
|
-
|
|
62
|
+
A host must authorize the participant and select the exact collecting definition.
|
|
63
|
+
`validateSurveySubmission(survey, input, trustedContext, { definitionId })` rejects a
|
|
64
|
+
response recorded against another definition, validates the envelope and domains,
|
|
65
|
+
recomputes active answers/validation, and records server-owned context.
|
|
66
|
+
Do not trust client-supplied visibility, disabling, or identity as authorization.
|
|
44
67
|
|
|
45
|
-
##
|
|
68
|
+
## Editing and extension
|
|
46
69
|
|
|
47
|
-
|
|
70
|
+
Elements are plain data with stateless definitions from `defineElement`. One
|
|
71
|
+
annotated schema declares configuration, identities, references, wording, and
|
|
72
|
+
follow-up bodies. `createElementRegistry` supplies the shared recursive body.
|
|
73
|
+
See the architecture guide for a custom definition example.
|
|
48
74
|
|
|
49
75
|
```ts
|
|
50
|
-
import {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
class SingleChoiceQuestionItemCore extends SurveyItemCore<
|
|
64
|
-
"singleChoiceQuestion",
|
|
65
|
-
SingleChoiceConfig
|
|
66
|
-
> {
|
|
67
|
-
readonly type = "singleChoiceQuestion";
|
|
68
|
-
|
|
69
|
-
parseConfig(rawConfig: unknown): SingleChoiceConfig {
|
|
70
|
-
const cfg = (rawConfig ?? {}) as Partial<SingleChoiceConfig>;
|
|
71
|
-
return {
|
|
72
|
-
id: cfg.id ?? this.id,
|
|
73
|
-
variableName: cfg.variableName ?? "answer",
|
|
74
|
-
options: cfg.options ?? [],
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
isInteractive(): boolean {
|
|
79
|
-
return true;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
getResponseSlotDefinitions(): ResponseSlotDefinition[] {
|
|
83
|
-
return [
|
|
84
|
-
{
|
|
85
|
-
slotId: this.config.id,
|
|
86
|
-
primitiveId: this.id,
|
|
87
|
-
variableName: this.config.variableName,
|
|
88
|
-
naming: { kind: "variable", path: ["variableName"] },
|
|
89
|
-
valueType: ValueType.reference,
|
|
90
|
-
allowedValues: this.config.options.map((option) => ({
|
|
91
|
-
type: ValueType.reference,
|
|
92
|
-
value: option.id,
|
|
93
|
-
})),
|
|
94
|
-
referenceValueKeys: Object.fromEntries(
|
|
95
|
-
this.config.options.map((option) => [option.id, option.code]),
|
|
96
|
-
),
|
|
97
|
-
},
|
|
98
|
-
];
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const pluginRegistry: ItemTypeRegistry = {
|
|
103
|
-
singleChoiceQuestion: SingleChoiceQuestionItemCore,
|
|
104
|
-
};
|
|
76
|
+
import { SurveyEditor } from "@case-framework/survey-core/editor";
|
|
77
|
+
import { createBuiltInElement } from "@case-framework/survey-items";
|
|
78
|
+
|
|
79
|
+
const editor = new SurveyEditor(survey);
|
|
80
|
+
const names = new Set(survey.slots.definitions.map((slot) => slot.variableName));
|
|
81
|
+
const element = createBuiltInElement("text", names);
|
|
82
|
+
const { itemId } = editor.insertElement(element);
|
|
83
|
+
editor.setLocalizedContent({ scope: "content", ownerId: itemId, role: "title" }, "en", {
|
|
84
|
+
type: "plain",
|
|
85
|
+
content: "Your name",
|
|
86
|
+
});
|
|
105
87
|
```
|
|
106
88
|
|
|
107
|
-
|
|
89
|
+
Without a layout target, `insertElement` creates a standalone content item.
|
|
90
|
+
User and assistant edits share the same transactions and undo/redo. Parsing and
|
|
91
|
+
rendering never allocate IDs. Copies allocate/remap owned identities; moves keep
|
|
92
|
+
them. `survey.slots`, `survey.owners`, and `survey.elements` are derived indexes.
|
|
108
93
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
const survey = Survey.fromJson(
|
|
119
|
-
{
|
|
120
|
-
$schema:
|
|
121
|
-
"https://github.com/case-framework/case-survey-toolkit/packages/survey-core/schemas/survey-schema.json",
|
|
122
|
-
schemaVersion: 2,
|
|
123
|
-
surveyItems: [
|
|
124
|
-
{
|
|
125
|
-
id: "root",
|
|
126
|
-
metadata: { itemLabel: "Survey" },
|
|
127
|
-
itemType: ReservedSurveyItemTypes.Group,
|
|
128
|
-
config: { isRoot: true, items: ["q1"] },
|
|
129
|
-
},
|
|
130
|
-
{
|
|
131
|
-
id: "q1",
|
|
132
|
-
metadata: { itemLabel: "Example question" },
|
|
133
|
-
itemType: "singleChoiceQuestion",
|
|
134
|
-
config: {
|
|
135
|
-
id: "q1",
|
|
136
|
-
variableName: "choice",
|
|
137
|
-
options: [
|
|
138
|
-
{ id: "yes", code: "yes", type: "option" },
|
|
139
|
-
{ id: "no", code: "no", type: "option" },
|
|
140
|
-
],
|
|
141
|
-
},
|
|
142
|
-
},
|
|
143
|
-
],
|
|
144
|
-
},
|
|
145
|
-
pluginRegistry,
|
|
146
|
-
);
|
|
147
|
-
|
|
148
|
-
const engine = new SurveyEngineCore(survey, { locale: "en" });
|
|
149
|
-
|
|
150
|
-
// Set one item response (slot id == config.id in this example).
|
|
151
|
-
engine.setResponse("q1", new ResponseItem([["q1", { type: ValueType.reference, value: "yes" }]]));
|
|
152
|
-
|
|
153
|
-
const pages = engine.getSurveyPages("large");
|
|
154
|
-
const responses = engine.getSubmissionResponses();
|
|
155
|
-
const events = engine.getEvents();
|
|
156
|
-
```
|
|
94
|
+
## Expressions
|
|
95
|
+
|
|
96
|
+
Canonical JSON nodes replace runtime expression classes. Editor builders return
|
|
97
|
+
these nodes directly with `getExpression()`. Array operand gaps are `null`.
|
|
98
|
+
`collectExpressionDependencies` includes answer and disabled-state references;
|
|
99
|
+
`visitExpression`, `transformExpression`, and `remapExpression` handle the grammar
|
|
100
|
+
exhaustively. Never find or rewrite references by matching arbitrary JSON strings.
|
|
101
|
+
Registered custom callbacks are not serialized expression behavior: hosts record
|
|
102
|
+
custom values so server validation and export can replay the same context.
|
|
157
103
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
not affect validation, follow-up conditions or computed scores.
|
|
165
|
-
|
|
166
|
-
The engine compiles visibility and answer dependencies once and memoizes active
|
|
167
|
-
values per update. Cyclic dependencies are rejected before the session starts,
|
|
168
|
-
including visibility based on the same question's answer or its own computed score.
|
|
169
|
-
Undefined/nonboolean display conditions hide their target. Visibility callbacks
|
|
170
|
-
(`customExpression`) are not reproducible or statically analyzable; use serialized
|
|
171
|
-
custom context values instead. Submitted responses save the locale and participant
|
|
172
|
-
flags alongside custom values so exports reconstruct the same evaluation context.
|
|
173
|
-
Export reconstruction does not reapply configured prefills.
|
|
174
|
-
|
|
175
|
-
Item plugins declare component-gated slots with `getConditionalResponseSlots()`.
|
|
176
|
-
Several entries for one slot require all listed components to be visible. More
|
|
177
|
-
complex projection (such as hidden choices or unselected embedded fields) uses
|
|
178
|
-
`getActiveResponseValue()` and must declare its slot/component dependencies in
|
|
179
|
-
`getActiveResponseDependencies()`. Computed values must declare their source slots;
|
|
180
|
-
they receive active source answers only. Never mutate retained answers in these hooks.
|
|
181
|
-
|
|
182
|
-
Only interactive items have response objects;
|
|
183
|
-
`getResponseItem()` returns `undefined` for groups, page breaks, and display items,
|
|
184
|
-
and `setResponse()` rejects these targets.
|
|
185
|
-
|
|
186
|
-
### 3. Export responses to CSV
|
|
104
|
+
`list_count(list, filter?)` counts array entries (a missing list counts as 0), optionally
|
|
105
|
+
only those also present in a same-typed `filter` list. `if_else(condition, then, else?)`
|
|
106
|
+
serializes as the `if` function: it evaluates only the chosen branch, returns missing for
|
|
107
|
+
a missing condition, and takes its result type from the branches, which must match.
|
|
108
|
+
|
|
109
|
+
## Export
|
|
187
110
|
|
|
188
111
|
```ts
|
|
189
112
|
import { SurveyResponse, SurveyResponseExporter } from "@case-framework/survey-core";
|
|
190
113
|
|
|
191
|
-
const response =
|
|
192
|
-
response.submittedAt = Math.floor(Date.now() / 1000);
|
|
193
|
-
|
|
194
|
-
// The host supplies the definition that collected these answers.
|
|
114
|
+
const response = SurveyResponse.deserialize(submission);
|
|
195
115
|
const exporter = new SurveyResponseExporter([
|
|
196
|
-
{ schema: survey, responses: [response], sourceId: "
|
|
116
|
+
{ schema: survey, responses: [response], sourceId: "release-1" },
|
|
197
117
|
]);
|
|
198
118
|
const csv = exporter.exportResponsesToCsv();
|
|
199
119
|
```
|
|
200
120
|
|
|
201
|
-
|
|
202
|
-
The
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
timestamps are not recorded, and hosts must call `onPageChanged()` themselves to
|
|
206
|
-
record page changes.
|
|
207
|
-
|
|
208
|
-
### 4. Edit surveys with the editor entrypoint
|
|
209
|
-
|
|
210
|
-
```ts
|
|
211
|
-
import { SurveyEditor, CommitSource } from "@case-framework/survey-core/editor";
|
|
212
|
-
|
|
213
|
-
const editor = new SurveyEditor(survey, { pluginRegistry });
|
|
121
|
+
Responses store flat `answers[slotId]`, event/metadata state, and recorded context.
|
|
122
|
+
The host supplies the exact definition in every export batch. Computed slots are
|
|
123
|
+
replayed from active stored answers, not accepted as participant writes. Events use
|
|
124
|
+
millisecond instants; calendar answers below use calendar strings.
|
|
214
125
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
config: { id: "q2", variableName: "second_choice", options: [] },
|
|
220
|
-
});
|
|
126
|
+
Names use unique lowercase snake_case (maximum 32 characters). Atomic naming edits
|
|
127
|
+
use slot IDs or `{ elementId, prefix?, rows?, columns? }` for matrices. Export codes
|
|
128
|
+
and variable names never change answer identity. Categorical values use declared
|
|
129
|
+
option IDs, including dropdowns and matrix cells.
|
|
221
130
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
131
|
+
`ExporterProfile` has `schemaVersion: 1`. Slot overrides use exact slot IDs;
|
|
132
|
+
column headers/order use stable encoded column IDs. Existing categorical modes,
|
|
133
|
+
boolean mappings, masks, date formatting, number precision, and duration units
|
|
134
|
+
remain available. Missing answers are blank; answered empty selections differ
|
|
135
|
+
from missing answers. Invalid codes, names, or profile targets are diagnostics.
|
|
225
136
|
|
|
226
137
|
## Calendar dates
|
|
227
138
|
|
|
@@ -237,67 +148,38 @@ Use `const_date("2026-09-20")`, `response_date(ref)`, and the `date_eq`, `date_g
|
|
|
237
148
|
precision; ordering/min/max require matching precision. `date_add_days`,
|
|
238
149
|
`date_add_months`, and `date_add_years` accept a date expression and signed integer
|
|
239
150
|
number expression. They preserve precision and clamp month-end dates. Nonzero offsets
|
|
240
|
-
cannot require finer precision than the input. `date_diff(a, b)` returns signed
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
151
|
+
cannot require finer precision than the input. `date_diff(a, b, unit)` returns the signed
|
|
152
|
+
number of completed `"years"`, `"months"`, or `"days"` in a-b, comparing mixed precisions at
|
|
153
|
+
the coarser one; a unit finer than that precision returns undefined. Missing values,
|
|
154
|
+
ordering precision mismatches, and arithmetic outside years 0001–9999 return undefined.
|
|
155
|
+
Calendar arithmetic uses date-fns on UTC dates, so results do not depend on the host timezone.
|
|
156
|
+
|
|
157
|
+
`ctx_current_date()` reads the session's current date. A new session defaults it to the
|
|
158
|
+
participant-local day at start (`context.currentDate`, `YYYY-MM-DD`), keeps it across
|
|
159
|
+
context refreshes and resume, and records it in the response's `evaluationContext` for
|
|
160
|
+
submission validation and export replay. Hosts may pass it explicitly. Age in years is
|
|
161
|
+
`date_diff(ctx_current_date(), response_date(birthRef), "years")`.
|
|
162
|
+
|
|
163
|
+
Presence is the element's `required` flag. Number and date inputs (and matrix fields) take
|
|
164
|
+
`min`/`max` bounds that are a literal or an expression of the input's type, with an optional
|
|
165
|
+
`fallback`. For example:
|
|
246
166
|
|
|
247
167
|
```ts
|
|
248
|
-
const
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
.serialize(),
|
|
168
|
+
const min = {
|
|
169
|
+
expression: date_add_days(
|
|
170
|
+
response_date({ slotId: "start-slot", method: "get" }),
|
|
171
|
+
const_number(2),
|
|
172
|
+
).getExpression()!,
|
|
254
173
|
};
|
|
255
174
|
```
|
|
256
175
|
|
|
257
176
|
Bounds evaluate against current responses, including after the source answer changes.
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
177
|
+
Date minimums use the start of their period, maximums the end; an answer's entire period
|
|
178
|
+
must fit. Optional unanswered fields pass. A bound whose expression has no value does not
|
|
179
|
+
apply unless it has a `fallback`; slider bounds from an expression require one. Other
|
|
180
|
+
checks (lengths, pattern, email, url, integer, must be true) are element `validations`, and
|
|
181
|
+
custom expression rules live on the content item. Registered domain validators receive an expression evaluator alongside the active-answer reader.
|
|
262
182
|
|
|
263
183
|
Default exports preserve the calendar string. Custom formatting and date templates are
|
|
264
184
|
timezone-independent; partial dates retain their canonical value even if a pattern requests
|
|
265
185
|
missing components. Use local Date adapters only at calendar-widget boundaries.
|
|
266
|
-
|
|
267
|
-
## Response identities, naming and export profiles
|
|
268
|
-
|
|
269
|
-
Surveys and flat response envelopes use `schemaVersion: 2`. Persist `answers[slotId]` separately from `itemMetadata`, context and events. A host-selected schema owns each slot; a submission does not require a publication/version ID. Old drafts and profiles must be recreated, not implicitly interpreted against this format.
|
|
270
|
-
|
|
271
|
-
Use `survey.getResponseSlotRegistry()` for typed ownership, variable names, categorical domains, computed dependencies, labels and export codes. Serialized expression nodes use `{type: "responseVariable", variableRef: {slotId, method: "get" | "isDefined"}}`. Names and item positions are never identities.
|
|
272
|
-
|
|
273
|
-
Names are unique lowercase snake_case, begin with a letter, and contain at most 32 characters. `previewResponseNaming(survey, edits)` and `editor.renameResponseVariables(edits)` support atomic renames, including swaps. Matrix edits target `{primitiveId, prefix?, rows?, columns?}`; cells remain derived. Copying creates fresh owned identities and suffixes names or matrix prefixes with `_2`, `_3`, etc. `editor.lastResponseNameChanges` reports the result. Plugins expose `naming` paths and owned component IDs to participate in copying and editing.
|
|
274
|
-
|
|
275
|
-
Categorical answers store option IDs, including ordinary form and form-matrix dropdowns. Expression constants compared with a categorical slot must be option IDs from that slot's declared domain. `survey.getExpressionResponseSlots()` reports each reference with its owning `itemId` and typed `allowedValues`; unknown references and out-of-domain constants are blocking diagnostics that stop runtime and export readiness rather than evaluating false and silently changing branching. Never infer an option ID from a matching display label or export code.
|
|
276
|
-
|
|
277
|
-
Previous-response prefills reference `{surveyKey?, slotId}`. The host resolver answers them from its own stored submissions; duplicating a draft keeps these references external instead of remapping them to the copy.
|
|
278
|
-
|
|
279
|
-
`metadata.itemLabel` is an optional **Editor name**. `survey.resolveItemEditorName(id, locale)` falls back to meaningful content and localized composition; `getItemDisplayPath` returns structural IDs plus display breadcrumbs. Items have no coding key.
|
|
280
|
-
|
|
281
|
-
`ExporterProfile` requires `schemaVersion: 1`. `slotOverrides` uses exact slot IDs; `columnHeaders` and ordering use `encodeExportColumnId(["response", slotId, "value"])` or `["response", slotId, "option", optionId]`. Unknown entries and matrix-cell header overrides are errors. `SlotTransformConfig` supports `include`, `categorical` (codes/labels/ids), `multiple` (json/delimited/indicators), explicit `delimiter`, `booleanValues`, date formatting, numeric `precision`, `durationUnit`, and explicit alternative mappings. Missing values are blank; answered empty selections become `[]` or zero indicators. Codes must be complete, nonempty and unambiguous. Delimited list entries are double-quoted with doubled internal quotes. Durations default to seconds; calendar months/years require an explicit matching unit because their lengths are not fixed.
|
|
282
|
-
|
|
283
|
-
Computed slots are excluded by default; `{mode:"default", include:true}` evaluates them against the supplied schema and response context. `generateCodebook(survey, {profile})` and `generateExportCodebook(batches, options)` describe the actual compiled projection and mappings. Combined exports require an explicit `codingPolicy`; incompatible types/domains are rejected, and changed codes need complete explicit harmonized mappings.
|
|
284
|
-
|
|
285
|
-
The editor's `previewResponseSettings` / `updateResponseSettings` combine naming, option-code and format edits before one ordinary commit. Authoring formats live in `RawSurveyItem.responseSettings[slotId]`; batch profiles can override them. Both the per-item controls and survey-wide **Response data** view use these APIs. Hosts may wrap the editor in the exported `ResponseSettingsSuggestionProvider` to supply FAIR suggestions as `{naming, settings}`; suggestions are previewed and validated by the same manual mutation path.
|
|
286
|
-
|
|
287
|
-
### Persisted naming fields
|
|
288
|
-
|
|
289
|
-
All choice, consent and form responses store `variableName`. Matrices store `prefix`, row `rowName`, and (for form matrices) column `columnName`; full variable names are derived and never stored again on cells. Categorical options store `code`, including form/dropdown options. Option IDs remain the stored answer values. Former field/row/column/option `key` and dropdown option `value` fields are rejected rather than read as aliases. Item editor names remain `metadata.itemLabel`.
|
|
290
|
-
|
|
291
|
-
Response edits emit one `responseChanged` event per changed slot, with `slotId`,
|
|
292
|
-
`timestamp` (Unix seconds), and `operation` (`set` or `clear`). Unchanged values
|
|
293
|
-
emit no event. Initial restored/prefilled values do not emit change events.
|
|
294
|
-
The survey schema resolves slot ownership; events do not duplicate item types,
|
|
295
|
-
answer values, or source information.
|
|
296
|
-
|
|
297
|
-
Visibility conditions with defined, non-boolean results fail closed and are available
|
|
298
|
-
through `engine.getEvaluationDiagnostics()`. Each diagnostic identifies the item,
|
|
299
|
-
optional component, and result type without including answer values. Diagnostics
|
|
300
|
-
describe the current evaluation, are deduplicated by condition, and omit unanswered
|
|
301
|
-
conditions and conditions skipped because a parent is hidden. They are separate from
|
|
302
|
-
response-normalization diagnostics and participant events. Context updates always
|
|
303
|
-
reevaluate conditions; only an actual locale change records a language-change event.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
//#region src/schema/answer-types.d.ts
|
|
3
|
+
/** Tuple entries preserve opaque IDs such as __proto__ across JSON/schema roundtrips. */
|
|
4
|
+
declare const answerTypeReservationsSchema: z.ZodArray<z.ZodTuple<[z.ZodString, z.ZodEnum<{
|
|
5
|
+
readonly string: "string";
|
|
6
|
+
readonly duration: "duration";
|
|
7
|
+
readonly reference: "reference";
|
|
8
|
+
readonly number: "number";
|
|
9
|
+
readonly boolean: "boolean";
|
|
10
|
+
readonly date: "date";
|
|
11
|
+
readonly stringArray: "string[]";
|
|
12
|
+
readonly durationArray: "duration[]";
|
|
13
|
+
readonly referenceArray: "reference[]";
|
|
14
|
+
readonly numberArray: "number[]";
|
|
15
|
+
readonly dateArray: "date[]";
|
|
16
|
+
}>], null>>;
|
|
17
|
+
type AnswerTypeReservations = z.infer<typeof answerTypeReservationsSchema>;
|
|
18
|
+
//#endregion
|
|
19
|
+
export { answerTypeReservationsSchema as n, AnswerTypeReservations as t };
|
|
20
|
+
//# sourceMappingURL=answer-types-DH0gv0Ea.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"answer-types-DH0gv0Ea.d.mts","names":[],"sources":["../src/schema/answer-types.ts"],"mappings":";;;cAIa,8BAA4B,EAAA,SAAA,EAAA,UAAA,EAAA,WAAA,EAAA;;;;;;;;;;;;;KAc7B,yBAAyB,EAAE,aAAa"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { B as ValueType } from "./localization-1lzNpyFX.mjs";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
//#region src/schema/answer-types.ts
|
|
4
|
+
/** Tuple entries preserve opaque IDs such as __proto__ across JSON/schema roundtrips. */
|
|
5
|
+
const answerTypeReservationsSchema = z.array(z.tuple([z.string().min(1), z.enum(ValueType)])).superRefine((entries, context) => {
|
|
6
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7
|
+
entries.forEach(([id], index) => {
|
|
8
|
+
if (seen.has(id)) context.addIssue({
|
|
9
|
+
code: "custom",
|
|
10
|
+
path: [index, 0],
|
|
11
|
+
message: `Duplicate answer type reservation '${id}'`
|
|
12
|
+
});
|
|
13
|
+
seen.add(id);
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
//#endregion
|
|
17
|
+
export { answerTypeReservationsSchema as t };
|
|
18
|
+
|
|
19
|
+
//# sourceMappingURL=answer-types-Dtm6MqZQ.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"answer-types-Dtm6MqZQ.mjs","names":[],"sources":["../src/schema/answer-types.ts"],"sourcesContent":["import { z } from \"zod\";\nimport { ValueType } from \"../survey/responses/value-types\";\n\n/** Tuple entries preserve opaque IDs such as __proto__ across JSON/schema roundtrips. */\nexport const answerTypeReservationsSchema = z\n .array(z.tuple([z.string().min(1), z.enum(ValueType)]))\n .superRefine((entries, context) => {\n const seen = new Set<string>();\n entries.forEach(([id], index) => {\n if (seen.has(id))\n context.addIssue({\n code: \"custom\",\n path: [index, 0],\n message: `Duplicate answer type reservation '${id}'`,\n });\n seen.add(id);\n });\n });\nexport type AnswerTypeReservations = z.infer<typeof answerTypeReservationsSchema>;\n"],"mappings":";;;;AAIA,MAAa,+BAA+B,EACzC,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CACtD,aAAa,SAAS,YAAY;CACjC,MAAM,uBAAO,IAAI,IAAY;CAC7B,QAAQ,SAAS,CAAC,KAAK,UAAU;EAC/B,IAAI,KAAK,IAAI,EAAE,GACb,QAAQ,SAAS;GACf,MAAM;GACN,MAAM,CAAC,OAAO,CAAC;GACf,SAAS,sCAAsC,GAAG;EACpD,CAAC;EACH,KAAK,IAAI,EAAE;CACb,CAAC;AACH,CAAC"}
|
|
@@ -1,2 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
//#region src/editor/item-colors.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Editor-only item-color swatches persisted in `metadata.editorItemColor`.
|
|
4
|
+
*
|
|
5
|
+
* This is shared survey-editor configuration: hosts may render the editor
|
|
6
|
+
* without enabling the assistant, while assistant proposals must validate
|
|
7
|
+
* against the same values.
|
|
8
|
+
*/
|
|
9
|
+
export declare const SURVEY_EDITOR_ITEM_COLORS: readonly ["#404040", "#b91c1c", "#c2410c", "#a16207", "#4d7c0f", "#047857", "#0369a1", "#4338ca", "#7e22ce", "#86198f", "#be123c"];
|
|
10
|
+
//#endregion
|
|
11
|
+
//# sourceMappingURL=colors.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"colors.d.mts","names":[],"sources":["../../src/editor/item-colors.ts"],"mappings":";;;;;;;;qBAOa"}
|