@case-framework/survey-assistant 0.7.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 +58 -3
- package/dist/{attachments-B0hg3vpT.mjs → attachments-BuNni5vB.mjs} +1 -1
- package/dist/{attachments-B0hg3vpT.mjs.map → attachments-BuNni5vB.mjs.map} +1 -1
- package/dist/{authoring-references-BJQ_1nMX.mjs → authoring-references-CmwLc_C8.mjs} +49 -32
- package/dist/authoring-references-CmwLc_C8.mjs.map +1 -0
- package/dist/capabilities-D55dq-fO.mjs +582 -0
- package/dist/capabilities-D55dq-fO.mjs.map +1 -0
- package/dist/capabilities-default.d.mts +5 -12
- package/dist/capabilities-default.d.mts.map +1 -1
- package/dist/capabilities-default.mjs +2 -411
- package/dist/capabilities-z95e1mti.d.mts +117 -0
- package/dist/capabilities-z95e1mti.d.mts.map +1 -0
- package/dist/{constants-0UHGFcRJ.mjs → constants-Ct-vv9cu.mjs} +1 -1
- package/dist/{constants-0UHGFcRJ.mjs.map → constants-Ct-vv9cu.mjs.map} +1 -1
- package/dist/{constants-Be91Gay9.d.mts → constants-HL_klgJl.d.mts} +7 -28
- package/dist/constants-HL_klgJl.d.mts.map +1 -0
- package/dist/{controller-proxy-BH05e-C2.mjs → controller-proxy-BQ1YsVxJ.mjs} +12 -21
- package/dist/controller-proxy-BQ1YsVxJ.mjs.map +1 -0
- package/dist/controller-proxy-Cmzdr9tV.d.mts +65 -0
- package/dist/controller-proxy-Cmzdr9tV.d.mts.map +1 -0
- package/dist/default-D22AKTqE.mjs +233 -0
- package/dist/default-D22AKTqE.mjs.map +1 -0
- package/dist/{digest-SJmoYVaK.d.mts → digest-FnTTDBq2.d.mts} +2 -3
- package/dist/digest-FnTTDBq2.d.mts.map +1 -0
- package/dist/digest.d.mts +1 -1
- package/dist/digest.mjs.map +1 -1
- package/dist/engine-D950TfvC.mjs +3098 -0
- package/dist/engine-D950TfvC.mjs.map +1 -0
- package/dist/engine.d.mts +4 -3
- package/dist/engine.mjs +3 -3
- package/dist/{fair-guidance-CRYCRxQm.mjs → fair-guidance-BmO4PswW.mjs} +1 -1
- package/dist/{fair-guidance-CRYCRxQm.mjs.map → fair-guidance-BmO4PswW.mjs.map} +1 -1
- package/dist/{index-l_EK4yK7.d.mts → index-B8TLBd1G.d.mts} +18 -72
- package/dist/index-B8TLBd1G.d.mts.map +1 -0
- package/dist/index-CaX3hZmr.d.mts +23813 -0
- package/dist/index-CaX3hZmr.d.mts.map +1 -0
- package/dist/{index-x0qprHgK.d.mts → index-CeUlGkoU.d.mts} +58 -28
- package/dist/index-CeUlGkoU.d.mts.map +1 -0
- package/dist/{index-nhrgbgO5.d.mts → index-DYHdvSE8.d.mts} +347 -88
- package/dist/index-DYHdvSE8.d.mts.map +1 -0
- package/dist/lifecycle-DFzxVxpq.d.mts +54346 -0
- package/dist/lifecycle-DFzxVxpq.d.mts.map +1 -0
- package/dist/{memory-thread-repository-KWHQWFEW.mjs → memory-thread-repository-DmLadygY.mjs} +60 -30
- package/dist/memory-thread-repository-DmLadygY.mjs.map +1 -0
- package/dist/protocol-BQ7uIDYT.mjs +569 -0
- package/dist/protocol-BQ7uIDYT.mjs.map +1 -0
- package/dist/protocol.d.mts +4 -4
- package/dist/protocol.mjs +4 -3
- package/dist/{react-B3ybjJ-X.mjs → react-DybJ5pWw.mjs} +159 -46
- package/dist/react-DybJ5pWw.mjs.map +1 -0
- package/dist/react-integration.d.mts +2 -2
- package/dist/react-integration.mjs +2 -2
- package/dist/react.d.mts +3 -3
- package/dist/react.mjs +3 -3
- package/dist/references/assistant-operations.md +224 -180
- package/dist/references/core-rules.md +6 -3
- package/dist/references/element-types.md +189 -0
- package/dist/references/expressions.md +307 -168
- package/dist/references/fair-by-design.md +9 -9
- package/dist/references/follow-ups.md +151 -0
- package/dist/references/localization.md +26 -12
- package/dist/references/response-variables.md +38 -0
- package/dist/references/rich-text-content.md +22 -43
- package/dist/references/source-material-surveys.md +20 -24
- package/dist/references/survey-data-model.md +36 -102
- package/dist/{request-context-CG4SWijt.mjs → request-context-KVh8VjgH.mjs} +4 -10
- package/dist/request-context-KVh8VjgH.mjs.map +1 -0
- package/dist/{request-guardrails-TmgQtqp4.mjs → request-guardrails-C0ugBOp0.mjs} +6 -5
- package/dist/request-guardrails-C0ugBOp0.mjs.map +1 -0
- package/dist/server-agent.d.mts +62 -43
- package/dist/server-agent.d.mts.map +1 -1
- package/dist/server-agent.mjs +73 -40
- package/dist/server-agent.mjs.map +1 -1
- package/dist/server-runtime.d.mts +24 -42
- package/dist/server-runtime.d.mts.map +1 -1
- package/dist/server-runtime.mjs +12 -12
- package/dist/server-runtime.mjs.map +1 -1
- package/dist/server-tasks.d.mts +2 -2
- package/dist/server-tasks.mjs +2 -313
- package/dist/server-tools.d.mts +1 -1
- package/dist/server-tools.mjs +1 -1
- package/dist/server.d.mts +14 -36
- package/dist/server.d.mts.map +1 -1
- package/dist/server.mjs +5 -5
- package/dist/server.mjs.map +1 -1
- package/dist/storage-postgres.d.mts +9 -25
- package/dist/storage-postgres.d.mts.map +1 -1
- package/dist/storage-postgres.mjs +5 -4
- package/dist/storage-postgres.mjs.map +1 -1
- package/dist/tasks-Coh5N027.mjs +397 -0
- package/dist/tasks-Coh5N027.mjs.map +1 -0
- package/dist/{tasks-CYqrcaRO.d.mts → tasks-wb9M-bak.d.mts} +25 -41
- package/dist/tasks-wb9M-bak.d.mts.map +1 -0
- package/dist/thread-documents-DrHbaXqP.d.mts +119 -0
- package/dist/thread-documents-DrHbaXqP.d.mts.map +1 -0
- package/dist/{thread-handlers-DBAaU7BA.d.mts → thread-handlers-C3Iya92v.d.mts} +9 -40
- package/dist/thread-handlers-C3Iya92v.d.mts.map +1 -0
- package/dist/{tools-BvSovehT.mjs → tools-D0KBDolR.mjs} +677 -459
- package/dist/tools-D0KBDolR.mjs.map +1 -0
- package/dist/turn-survey-draft-B34lggSG.d.mts +185 -0
- package/dist/turn-survey-draft-B34lggSG.d.mts.map +1 -0
- package/dist/ui.css +1 -1
- package/dist/ui.d.mts +15 -54
- package/dist/ui.d.mts.map +1 -1
- package/dist/ui.mjs +203 -156
- package/dist/ui.mjs.map +1 -1
- package/docs/integration.md +11 -1
- package/package.json +41 -36
- package/dist/authoring-references-BJQ_1nMX.mjs.map +0 -1
- package/dist/capabilities-1KNnNO1_.mjs +0 -73
- package/dist/capabilities-1KNnNO1_.mjs.map +0 -1
- package/dist/capabilities-DVTfogjv.d.mts +0 -193
- package/dist/capabilities-DVTfogjv.d.mts.map +0 -1
- package/dist/capabilities-default.mjs.map +0 -1
- package/dist/constants-Be91Gay9.d.mts.map +0 -1
- package/dist/controller-proxy-BH05e-C2.mjs.map +0 -1
- package/dist/controller-proxy-Bd0ZHBPF.d.mts +0 -85
- package/dist/controller-proxy-Bd0ZHBPF.d.mts.map +0 -1
- package/dist/digest-SJmoYVaK.d.mts.map +0 -1
- package/dist/engine-Bo85Oyj0.mjs +0 -6723
- package/dist/engine-Bo85Oyj0.mjs.map +0 -1
- package/dist/index-Tnqv-yKW.d.mts +0 -777
- package/dist/index-Tnqv-yKW.d.mts.map +0 -1
- package/dist/index-l_EK4yK7.d.mts.map +0 -1
- package/dist/index-nhrgbgO5.d.mts.map +0 -1
- package/dist/index-x0qprHgK.d.mts.map +0 -1
- package/dist/lifecycle-Bx0aq_4S.d.mts +0 -529
- package/dist/lifecycle-Bx0aq_4S.d.mts.map +0 -1
- package/dist/memory-thread-repository-KWHQWFEW.mjs.map +0 -1
- package/dist/protocol-BhzQbx81.mjs +0 -1173
- package/dist/protocol-BhzQbx81.mjs.map +0 -1
- package/dist/react-B3ybjJ-X.mjs.map +0 -1
- package/dist/references/embedded-forms.md +0 -126
- package/dist/references/item-types.md +0 -407
- package/dist/request-context-CG4SWijt.mjs.map +0 -1
- package/dist/request-guardrails-TmgQtqp4.mjs.map +0 -1
- package/dist/server-tasks.mjs.map +0 -1
- package/dist/tasks-CYqrcaRO.d.mts.map +0 -1
- package/dist/thread-documents-rmd6nyX9.d.mts +0 -107
- package/dist/thread-documents-rmd6nyX9.d.mts.map +0 -1
- package/dist/thread-handlers-DBAaU7BA.d.mts.map +0 -1
- package/dist/tools-BvSovehT.mjs.map +0 -1
|
@@ -10,16 +10,17 @@ Use `survey_expression` for expression work. It is the expression capability bun
|
|
|
10
10
|
|
|
11
11
|
## JsonExpression
|
|
12
12
|
|
|
13
|
-
Expression JSON has
|
|
13
|
+
Expression JSON has five node types:
|
|
14
14
|
|
|
15
15
|
```json
|
|
16
16
|
{ "type": "const", "value": { "type": "boolean", "value": true } }
|
|
17
|
-
{ "type": "responseVariable", "variableRef": "<
|
|
17
|
+
{ "type": "responseVariable", "variableRef": { "slotId": "<slotId>", "method": "get" } }
|
|
18
18
|
{ "type": "contextVariable", "contextType": "locale" }
|
|
19
19
|
{ "type": "function", "functionName": "and", "arguments": [] }
|
|
20
|
+
{ "type": "ownerState", "state": "disabled", "targetRef": { "ownerId": "<ownerId>" } }
|
|
20
21
|
```
|
|
21
22
|
|
|
22
|
-
Optional `editorConfig`
|
|
23
|
+
Optional `editorConfig` contains only `usedTemplate`, `label`, and `description`; unknown keys are rejected. Missing argument-array operands are `null`, preserving their positions across JSON round trips. Required gaps are unfinished expressions, not false values. Optional single children such as a context key are omitted properties.
|
|
23
24
|
|
|
24
25
|
## Response Values And Variables
|
|
25
26
|
|
|
@@ -28,34 +29,37 @@ Response values are typed objects:
|
|
|
28
29
|
- string: `{ "type": "string", "value": "abc" }`
|
|
29
30
|
- number: `{ "type": "number", "value": 42 }`
|
|
30
31
|
- boolean: `{ "type": "boolean", "value": true }`
|
|
31
|
-
- date: `{ "type": "date", "value":
|
|
32
|
+
- date: `{ "type": "date", "value": "2026-01-01" }`
|
|
32
33
|
- duration: `{ "type": "duration", "value": 3, "unit": "days" }`
|
|
33
34
|
- reference: `{ "type": "reference", "value": "<optionId>" }`
|
|
34
35
|
- arrays use `string[]`, `number[]`, `date[]`, `duration[]`, `reference[]`.
|
|
35
36
|
|
|
36
|
-
Response variable refs
|
|
37
|
-
`<itemId>...<method>...<slotId>`
|
|
37
|
+
Response variable refs are structured objects: `{ "slotId": "<persistent-slot-id>", "method": "get" }`. There is no item ID, dotted path, variable name, or encoded string in a reference.
|
|
38
38
|
|
|
39
39
|
Methods:
|
|
40
40
|
|
|
41
|
-
- `get`: returns the current slot value.
|
|
42
|
-
- `isDefined`: returns boolean true when
|
|
41
|
+
- `get`: returns the current active slot value.
|
|
42
|
+
- `isDefined`: returns boolean true when an active response exists.
|
|
43
43
|
|
|
44
|
-
`isDefined` means only "
|
|
44
|
+
`isDefined` means only "this slot currently has an active answer." It never means a particular answer
|
|
45
45
|
such as Yes, selected, eligible, consented, or has children. When the user's condition names or
|
|
46
46
|
implies a choice, inspect the source choice item's exact option ids and labels, then compare the
|
|
47
47
|
`get` response with that option's reference using `eq` (single choice) or `list_contains`
|
|
48
48
|
(multiple choice). Do not substitute `isDefined` merely because the exact option id is not yet
|
|
49
49
|
known.
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
dependencies,
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
51
|
+
Visibility dependencies must be acyclic. The runtime rejects self-reference, mutual
|
|
52
|
+
question dependencies, parent groups controlled by their descendants, and cycles through
|
|
53
|
+
conditional components or computed scores. Prefills do not make these cycles valid.
|
|
54
|
+
Use an independent controlling answer or serialized custom context value. A component may
|
|
55
|
+
reference another slot in the same item only when its active value does not depend on that
|
|
56
|
+
component's visibility. Repair rejected changes using the returned retry linkage.
|
|
57
|
+
|
|
58
|
+
Hidden answers remain stored for the session and return when shown again, but are unavailable
|
|
59
|
+
to expressions, validation, scoring and submission while hidden. `get` returns undefined and
|
|
60
|
+
`isDefined` returns false for an inactive answer. This includes hidden groups, matrix rows,
|
|
61
|
+
semantic sections, hidden choice options and unselected follow-up inputs. With no visibility condition the target is visible. An authored visibility expression returning missing or false makes it inactive; a defined non-boolean result is inactive with a diagnostic. Visibility cannot use `customExpression`
|
|
62
|
+
callbacks; supply serialized `customValue` context instead.
|
|
59
63
|
|
|
60
64
|
Never invent response refs. Use the current context response-slots scope or item details.
|
|
61
65
|
This rule is item-type independent: preserve every item id, slot id, component/validation key,
|
|
@@ -73,27 +77,76 @@ in its `create-item.item` object in one `survey_change` call. When a genuine dep
|
|
|
73
77
|
separate calls, inspect the updated request-scoped draft and use `survey_expression` against the
|
|
74
78
|
newly available ids and response slots.
|
|
75
79
|
|
|
76
|
-
Each response slot
|
|
77
|
-
`<sourceItemId>...get...<slotId>`, with a boolean presence check at
|
|
78
|
-
`<sourceItemId>...isDefined...<slotId>`. Use the slot's actual value type to choose the
|
|
80
|
+
Each declared response slot is available as `{ "slotId": "<slotId>", "method": "get" }`, with a presence check using the same slot ID and `method: "isDefined"`. Use the slot's actual value type to choose the
|
|
79
81
|
function and constant. For example, a single-choice `reference` slot uses `eq` with a
|
|
80
82
|
`reference` const; this is only one typed case, not a restriction on source item types.
|
|
81
83
|
Always check the response slot value type before choosing a function:
|
|
82
84
|
|
|
85
|
+
- Consent items return `boolean` in their persistent `config.slotId`; compare with `eq` and boolean `true` for agreement or `false` for refusal. Unanswered is neither. `isDefined` means either decision was recorded. Display conditions hide dependent content while retaining session answers; hidden items are omitted from submission.
|
|
83
86
|
- Single-choice choice items return `reference`; compare them with `eq` and a `reference` const.
|
|
84
87
|
- Multiple-choice choice items return `reference[]`; check selected options with `list_contains` and a `reference` const.
|
|
85
88
|
- `list_contains` must receive `string[]` + `string` or `reference[]` + `reference`. Never use it with a single `reference` response.
|
|
86
89
|
|
|
87
|
-
Context variable types are `locale`, `
|
|
90
|
+
Context variable types are `locale`, `currentDate`, `participantFlag`, and `customValue`.
|
|
91
|
+
`{ "type": "contextVariable", "contextType": "currentDate" }` returns the participant's current
|
|
92
|
+
day as a full `date`. It takes no key; the host fixes it when the session starts and records it with
|
|
93
|
+
the response, so submission validation and export replay see the same day. Opaque callbacks (`customExpression`) are not part of the grammar; use recorded custom values so player, submission validation and export replay agree.
|
|
88
94
|
|
|
89
95
|
## Functions
|
|
90
96
|
|
|
91
97
|
Boolean: `and`, `or`, `not`.
|
|
92
98
|
List containment: `list_contains`.
|
|
99
|
+
List counting: `list_count(list)` returns the number of entries in any array value;
|
|
100
|
+
`list_count(list, filter)` counts only entries that also appear in `filter`, a list of the same
|
|
101
|
+
type. A missing list (for example an unanswered multiple choice) counts as `0`; use `isDefined`
|
|
102
|
+
to tell unanswered from none selected. For "at least two of these options selected":
|
|
103
|
+
`gte(list_count(<reference[] ref>, const(reference[], ["<optionId>", ...])), const(number, 2))`.
|
|
104
|
+
Filter option ids must be declared option identities, as with `list_contains`.
|
|
93
105
|
Equality/comparison: `eq`, `gt`, `gte`, `lt`, `lte`, `in_range`.
|
|
94
106
|
Numeric aggregation: `sum`, `min`, `max`.
|
|
95
107
|
String equality: `str_eq`.
|
|
96
|
-
|
|
108
|
+
Conditional value: `if(condition, then, else)` returns `then` when the boolean condition is true
|
|
109
|
+
and `else` when it is false; only the chosen branch is evaluated. `else` is optional (missing when
|
|
110
|
+
omitted), and a missing condition returns missing rather than `else`. The result type is the
|
|
111
|
+
branches' type: both branches must have the same type, and the usage checks it like any other
|
|
112
|
+
result (e.g. a score uses number branches, a visibility condition boolean branches). Nest `if`
|
|
113
|
+
calls for more than two cases, e.g. `if(c1, a, if(c2, b, c))`.
|
|
114
|
+
Calendar dates use `YYYY`, `YYYY-MM`, or `YYYY-MM-DD`; numeric timestamps and date-time strings are invalid. Preserve the answer's precision.
|
|
115
|
+
|
|
116
|
+
Date comparisons: `date_eq`, `date_gt`, `date_gte`, `date_lt`, `date_lte` (two date arguments).
|
|
117
|
+
Equality includes precision; ordering requires matching precision and returns undefined otherwise.
|
|
118
|
+
`date_min` / `date_max` take one or more dates of matching precision and return a date.
|
|
119
|
+
`date_add_days`, `date_add_months`, `date_add_years` take a date and a signed integer number.
|
|
120
|
+
Offsets retain precision; month/year offsets clamp to the last day of the destination month.
|
|
121
|
+
Nonzero day offsets require full dates, nonzero month offsets require at least month precision.
|
|
122
|
+
`date_diff(a, b, unit)` returns the signed number of completed units in `a - b`. `unit` is a
|
|
123
|
+
required string const: `"years"`, `"months"`, or `"days"`. Mixed precisions are compared at the
|
|
124
|
+
coarser one (a year-only birth date gives calendar-year differences), and a unit finer than that
|
|
125
|
+
precision returns undefined. Missing values propagate undefined; do not use timestamps or divide
|
|
126
|
+
days to calculate ages. Age in years:
|
|
127
|
+
`date_diff(contextVariable(currentDate), responseVariable(<birth date>), const(string, "years"))`.
|
|
128
|
+
For an "at least N years old" check, use `gte(date_diff(...), const(number, N))`, which works
|
|
129
|
+
for every birth-date precision. The anniversary form
|
|
130
|
+
`date_lte(date_add_years(<birth date>, N), currentDate)` only works for full `YYYY-MM-DD` birth
|
|
131
|
+
dates: for a year or month birth date it compares mismatched precisions and returns undefined.
|
|
132
|
+
For full dates the two agree except on February 29 birthdays, which reach an anniversary on
|
|
133
|
+
February 28 with the anniversary form and on March 1 with `date_diff`.
|
|
134
|
+
|
|
135
|
+
Number and date inputs (and form-matrix number/date fields) take `min` / `max` bounds that are a
|
|
136
|
+
literal or `{expression, fallback?}`. Use a response reference or arithmetic expression of the
|
|
137
|
+
input's type. For example, a latest date seven days after another answer uses
|
|
138
|
+
`date_add_days(responseVariable(...), const(number, 7))`. When the expression has no value (for
|
|
139
|
+
example the referenced answer is empty), the bound does not apply, unless it has a `fallback`.
|
|
140
|
+
Slider bounds from an expression require a `fallback`.
|
|
141
|
+
Use set-config-expression with path `/min/expression` or `/max/expression` relative to the element
|
|
142
|
+
config; an existing literal bound becomes the fallback. clear-config-expression on the same path
|
|
143
|
+
restores the fallback, or removes a bound without one. The normal expression operations remain
|
|
144
|
+
appropriate for item validations, prefills, and display conditions.
|
|
145
|
+
Date bounds are inclusive: minimum uses the start of its calendar period, maximum uses its end.
|
|
146
|
+
The entire answer period must fit within the bound. An unanswered optional date remains valid.
|
|
147
|
+
A bound whose expression has no value does not apply, unless it has a `fallback`; then the
|
|
148
|
+
fallback applies. Fixed bounds and fallbacks must leave at least one valid answer at the input's
|
|
149
|
+
precision.
|
|
97
150
|
|
|
98
151
|
Example: show an item when option "yes" is selected on choice item q1:
|
|
99
152
|
|
|
@@ -102,8 +155,20 @@ Example: show an item when option "yes" is selected on choice item q1:
|
|
|
102
155
|
"type": "function",
|
|
103
156
|
"functionName": "eq",
|
|
104
157
|
"arguments": [
|
|
105
|
-
{
|
|
106
|
-
|
|
158
|
+
{
|
|
159
|
+
"type": "responseVariable",
|
|
160
|
+
"variableRef": {
|
|
161
|
+
"slotId": "q1",
|
|
162
|
+
"method": "get"
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
"type": "const",
|
|
167
|
+
"value": {
|
|
168
|
+
"type": "reference",
|
|
169
|
+
"value": "yes"
|
|
170
|
+
}
|
|
171
|
+
}
|
|
107
172
|
]
|
|
108
173
|
}
|
|
109
174
|
```
|
|
@@ -115,209 +180,277 @@ Example: multiple-select contains option "apple" when the inspected response slo
|
|
|
115
180
|
"type": "function",
|
|
116
181
|
"functionName": "list_contains",
|
|
117
182
|
"arguments": [
|
|
118
|
-
{
|
|
119
|
-
|
|
183
|
+
{
|
|
184
|
+
"type": "responseVariable",
|
|
185
|
+
"variableRef": {
|
|
186
|
+
"slotId": "q2",
|
|
187
|
+
"method": "get"
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
"type": "const",
|
|
192
|
+
"value": {
|
|
193
|
+
"type": "reference",
|
|
194
|
+
"value": "apple"
|
|
195
|
+
}
|
|
196
|
+
}
|
|
120
197
|
]
|
|
121
198
|
}
|
|
122
199
|
```
|
|
123
200
|
|
|
124
201
|
## New Items With Dependencies
|
|
125
202
|
|
|
126
|
-
For a newly created source and dependent, use this operation order and expression placement.
|
|
127
|
-
This pattern is item-type independent: `displayConditions` is a sibling of `config` on the
|
|
128
|
-
raw item, never inside `config` or `translations`.
|
|
203
|
+
For a newly created source and dependent, use this operation order and expression placement. This pattern is element-type independent: `visibility` is on its owner, beside `config`, never inside translations. A flow item condition applies to its complete body; an element condition applies only to that element. Replace `<root-group-id>` with the inspected root group ID.
|
|
129
204
|
|
|
130
205
|
```json
|
|
131
206
|
[
|
|
132
207
|
{
|
|
133
208
|
"kind": "create-item",
|
|
134
|
-
"target": {
|
|
209
|
+
"target": {
|
|
210
|
+
"parentItemId": "<root-group-id>"
|
|
211
|
+
},
|
|
135
212
|
"item": {
|
|
136
|
-
"id": "
|
|
137
|
-
"
|
|
138
|
-
"itemType": "choiceItem",
|
|
213
|
+
"id": "routing",
|
|
214
|
+
"itemType": "content",
|
|
139
215
|
"config": {
|
|
140
|
-
"
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
216
|
+
"body": {
|
|
217
|
+
"blocks": [
|
|
218
|
+
{
|
|
219
|
+
"kind": "layout",
|
|
220
|
+
"id": "routing-layout",
|
|
221
|
+
"layout": {
|
|
222
|
+
"mode": "automatic",
|
|
223
|
+
"maxColumns": 1
|
|
224
|
+
},
|
|
225
|
+
"elements": [
|
|
226
|
+
{
|
|
227
|
+
"kind": "element",
|
|
228
|
+
"id": "routing-choice",
|
|
229
|
+
"elementType": "choice",
|
|
230
|
+
"config": {
|
|
231
|
+
"slotId": "routing-slot",
|
|
232
|
+
"variableName": "routing_choice",
|
|
233
|
+
"presentation": "radio",
|
|
234
|
+
"options": [
|
|
235
|
+
{
|
|
236
|
+
"id": "routing-a",
|
|
237
|
+
"code": "A"
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
"id": "routing-b",
|
|
241
|
+
"code": "B"
|
|
242
|
+
}
|
|
243
|
+
]
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
]
|
|
247
|
+
}
|
|
248
|
+
]
|
|
249
|
+
}
|
|
147
250
|
}
|
|
148
251
|
},
|
|
149
252
|
"translations": [
|
|
150
|
-
{
|
|
151
|
-
|
|
152
|
-
|
|
253
|
+
{
|
|
254
|
+
"ownerId": "routing",
|
|
255
|
+
"role": "title",
|
|
256
|
+
"locale": "en",
|
|
257
|
+
"plainText": "Choose an option"
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
"ownerId": "routing-a",
|
|
261
|
+
"role": "label",
|
|
262
|
+
"locale": "en",
|
|
263
|
+
"plainText": "Option A"
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
"ownerId": "routing-b",
|
|
267
|
+
"role": "label",
|
|
268
|
+
"locale": "en",
|
|
269
|
+
"plainText": "Option B"
|
|
270
|
+
}
|
|
153
271
|
]
|
|
154
272
|
},
|
|
155
273
|
{
|
|
156
274
|
"kind": "create-item",
|
|
157
|
-
"target": {
|
|
275
|
+
"target": {
|
|
276
|
+
"parentItemId": "<root-group-id>"
|
|
277
|
+
},
|
|
158
278
|
"item": {
|
|
159
|
-
"id": "
|
|
160
|
-
"
|
|
161
|
-
"itemType": "choiceItem",
|
|
279
|
+
"id": "routing-followup-a",
|
|
280
|
+
"itemType": "content",
|
|
162
281
|
"config": {
|
|
163
|
-
"
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
282
|
+
"body": {
|
|
283
|
+
"blocks": [
|
|
284
|
+
{
|
|
285
|
+
"kind": "layout",
|
|
286
|
+
"id": "routing-followup-a-layout",
|
|
287
|
+
"layout": {
|
|
288
|
+
"mode": "automatic",
|
|
289
|
+
"maxColumns": 1
|
|
290
|
+
},
|
|
291
|
+
"elements": [
|
|
292
|
+
{
|
|
293
|
+
"kind": "element",
|
|
294
|
+
"id": "routing-followup-a-text",
|
|
295
|
+
"elementType": "text",
|
|
296
|
+
"config": {
|
|
297
|
+
"slotId": "routing-followup-a-slot",
|
|
298
|
+
"variableName": "followup_a",
|
|
299
|
+
"presentation": "long"
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
]
|
|
303
|
+
}
|
|
178
304
|
]
|
|
179
305
|
}
|
|
306
|
+
},
|
|
307
|
+
"visibility": {
|
|
308
|
+
"type": "function",
|
|
309
|
+
"functionName": "eq",
|
|
310
|
+
"arguments": [
|
|
311
|
+
{
|
|
312
|
+
"type": "responseVariable",
|
|
313
|
+
"variableRef": {
|
|
314
|
+
"slotId": "routing-slot",
|
|
315
|
+
"method": "get"
|
|
316
|
+
}
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
"type": "const",
|
|
320
|
+
"value": {
|
|
321
|
+
"type": "reference",
|
|
322
|
+
"value": "routing-a"
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
]
|
|
180
326
|
}
|
|
181
327
|
},
|
|
182
328
|
"translations": [
|
|
183
|
-
{
|
|
184
|
-
|
|
185
|
-
|
|
329
|
+
{
|
|
330
|
+
"ownerId": "routing-followup-a",
|
|
331
|
+
"role": "title",
|
|
332
|
+
"locale": "en",
|
|
333
|
+
"plainText": "Details for option A"
|
|
334
|
+
}
|
|
186
335
|
]
|
|
187
336
|
},
|
|
188
337
|
{
|
|
189
338
|
"kind": "create-item",
|
|
190
|
-
"target": {
|
|
339
|
+
"target": {
|
|
340
|
+
"parentItemId": "<root-group-id>"
|
|
341
|
+
},
|
|
191
342
|
"item": {
|
|
192
|
-
"id": "
|
|
193
|
-
"
|
|
194
|
-
"itemType": "choiceItem",
|
|
343
|
+
"id": "routing-followup-b",
|
|
344
|
+
"itemType": "content",
|
|
195
345
|
"config": {
|
|
196
|
-
"
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
346
|
+
"body": {
|
|
347
|
+
"blocks": [
|
|
348
|
+
{
|
|
349
|
+
"kind": "layout",
|
|
350
|
+
"id": "routing-followup-b-layout",
|
|
351
|
+
"layout": {
|
|
352
|
+
"mode": "automatic",
|
|
353
|
+
"maxColumns": 1
|
|
354
|
+
},
|
|
355
|
+
"elements": [
|
|
356
|
+
{
|
|
357
|
+
"kind": "element",
|
|
358
|
+
"id": "routing-followup-b-text",
|
|
359
|
+
"elementType": "text",
|
|
360
|
+
"config": {
|
|
361
|
+
"slotId": "routing-followup-b-slot",
|
|
362
|
+
"variableName": "followup_b",
|
|
363
|
+
"presentation": "long"
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
]
|
|
367
|
+
}
|
|
211
368
|
]
|
|
212
369
|
}
|
|
370
|
+
},
|
|
371
|
+
"visibility": {
|
|
372
|
+
"type": "function",
|
|
373
|
+
"functionName": "eq",
|
|
374
|
+
"arguments": [
|
|
375
|
+
{
|
|
376
|
+
"type": "responseVariable",
|
|
377
|
+
"variableRef": {
|
|
378
|
+
"slotId": "routing-slot",
|
|
379
|
+
"method": "get"
|
|
380
|
+
}
|
|
381
|
+
},
|
|
382
|
+
{
|
|
383
|
+
"type": "const",
|
|
384
|
+
"value": {
|
|
385
|
+
"type": "reference",
|
|
386
|
+
"value": "routing-b"
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
]
|
|
213
390
|
}
|
|
214
391
|
},
|
|
215
392
|
"translations": [
|
|
216
|
-
{
|
|
217
|
-
|
|
218
|
-
|
|
393
|
+
{
|
|
394
|
+
"ownerId": "routing-followup-b",
|
|
395
|
+
"role": "title",
|
|
396
|
+
"locale": "en",
|
|
397
|
+
"plainText": "Details for option B"
|
|
398
|
+
}
|
|
219
399
|
]
|
|
220
400
|
}
|
|
221
401
|
]
|
|
222
402
|
```
|
|
223
403
|
|
|
224
|
-
|
|
225
|
-
translations change. The validator makes response slots from earlier creates available to later
|
|
226
|
-
creates in this one operations array.
|
|
227
|
-
|
|
228
|
-
## survey_expression Mutations
|
|
404
|
+
The validator makes response slots from earlier creates available to later creates in this one operations array. This example creates separate conditional questions. For content directly below a radio/checkbox option, use an option-inline follow-up instead; it already has an activation gate.
|
|
229
405
|
|
|
230
|
-
|
|
231
|
-
server validates every entry against these canonical shapes before changing the request-scoped
|
|
232
|
-
draft.
|
|
233
|
-
|
|
234
|
-
Item visibility:
|
|
235
|
-
|
|
236
|
-
```json
|
|
237
|
-
{
|
|
238
|
-
"kind": "set-item-display-condition",
|
|
239
|
-
"itemId": "followup_yes",
|
|
240
|
-
"expression": {
|
|
241
|
-
"type": "function",
|
|
242
|
-
"functionName": "eq",
|
|
243
|
-
"arguments": [
|
|
244
|
-
{ "type": "responseVariable", "variableRef": "q1...get...q1" },
|
|
245
|
-
{ "type": "const", "value": { "type": "reference", "value": "yes" } }
|
|
246
|
-
]
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
```
|
|
406
|
+
## Disabled state and applicability
|
|
250
407
|
|
|
251
|
-
|
|
252
|
-
`set-component-display-condition` with `itemId`, `componentId`, and a boolean `expression`.
|
|
253
|
-
For choice options the component id is usually the option id. For form fields or field groups, inspect item details/raw JSON first.
|
|
408
|
+
`ownerState` with `state: "disabled"` is the stored form of `isDisabled(owner)`. It reads the configured and inherited disabling of an inspected structural owner. It always returns boolean. It does not report visibility, answer presence or whether a choice cap permits a change. Answer and state dependencies share one cycle-checked graph. A parent reading its own child's disabled state is cyclic because the child inherits from that parent.
|
|
254
409
|
|
|
255
|
-
|
|
256
|
-
`set-component-disabled-condition` with `itemId`, `componentId`, and a boolean `expression`.
|
|
410
|
+
Active disabled answers are retained, readable in expressions and submitted. Participant writes are rejected while the whole input or an ancestor is disabled; initialization has its separate engine-owned path. Validation cannot block on targets the participant cannot change. Disabled checkbox options are locked; single-choice replacement can leave a disabled selection for an enabled destination. Hidden answers remain unavailable instead. Use visibility for “not applicable” and disabled for locking, not as a way of removing answers from data.
|
|
257
411
|
|
|
258
|
-
|
|
259
|
-
`set-validation-expression` with `itemId`, `validationKey`, and a boolean `expression`.
|
|
260
|
-
Add a matching validation message translation when respondents need visible feedback.
|
|
412
|
+
“Selected and enabled” is an authoring shortcut expanded into `and(selectionTest, not(ownerState(...)))`. Use eq for single choice, list_contains for checkbox selection. It is not a new persisted function. Disabled ancestors also make this false, including intentionally read-only prefilled items. Do not use the shortcut where the condition should still honor such answers. Mutual selected-and-enabled disabling may create a cycle; ordinary selection-based exclusivity avoids adding that state loop.
|
|
261
413
|
|
|
262
|
-
|
|
263
|
-
`set-prefill` with `itemId` and a full prefill object. `when`, when present, must return boolean.
|
|
264
|
-
Prefill source can be `static`, `expression`, `templateValue`, or `previousResponse`.
|
|
414
|
+
## Creation and expression mutations
|
|
265
415
|
|
|
266
|
-
|
|
267
|
-
`set-template-value` with `templateKey` and `templateValue`.
|
|
268
|
-
The template expression should match `returnType`; `date2string` also needs `dateFormat`.
|
|
416
|
+
Create source owners before their dependents. New owners may carry `visibility`, `disabled`, prefills and validation expressions in their creation payloads. For existing owners, use `survey_expression` with these mutations:
|
|
269
417
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
`
|
|
273
|
-
`clear-
|
|
418
|
+
- `set-condition`: `ownerId`, `property` (`visibility` or `disabled`), boolean `expression`.
|
|
419
|
+
- `clear-condition`: the same owner and property; absent visibility means visible and absent disabling means enabled, subject to ancestors.
|
|
420
|
+
- `set-config-expression`: `elementId`, inspected config-relative JSON Pointer `path`, and `expression`. The exact field must be declared as an expression by the installed element schema, including custom definitions.
|
|
421
|
+
- `clear-config-expression`: the same element and path; schema-required fields cannot be cleared.
|
|
422
|
+
- `set-validation-rule`: `itemId` and complete `rule` (`id`, nonempty `targets` slot IDs, boolean `expression`, optional boolean `when`, optional `phase: page|submit`). Its localized message belongs to that rule ID's `message` role.
|
|
423
|
+
- `remove-validation-rule`: `itemId`, `ruleId`.
|
|
424
|
+
- `set-prefill`: `elementId`, `slotId`, complete `prefill`.
|
|
425
|
+
- `clear-prefill`: `elementId`, `slotId`, `prefillId`.
|
|
426
|
+
- `set-template-value`: `templateKey`, complete `templateValue`.
|
|
427
|
+
- `clear-template-value`: `templateKey`.
|
|
274
428
|
|
|
275
|
-
|
|
429
|
+
Inspect exact owner IDs and expression locations. Conditions live directly on their annotated owner; prefills live at `element.prefills[slotId]`; validation rules live in `contentItem.config.validationRules`. Never reconstruct a location from a display label or old component path. Rules target slots owned by their item and cannot block when no target is active and changeable.
|
|
276
430
|
|
|
277
|
-
|
|
278
|
-
`/surveyItems/<itemIndex>/displayConditions/root`
|
|
279
|
-
|
|
280
|
-
Component display condition:
|
|
281
|
-
`/surveyItems/<itemIndex>/displayConditions/components/<componentId>`
|
|
282
|
-
|
|
283
|
-
Component disabled condition:
|
|
284
|
-
`/surveyItems/<itemIndex>/disabledConditions/components/<componentId>`
|
|
285
|
-
|
|
286
|
-
Item validation expression:
|
|
287
|
-
`/surveyItems/<itemIndex>/validations/<validationKey>`
|
|
288
|
-
|
|
289
|
-
Prefills:
|
|
290
|
-
`/surveyItems/<itemIndex>/prefills`
|
|
291
|
-
|
|
292
|
-
Template values:
|
|
293
|
-
`/templateValues/<templateValueKey>`
|
|
431
|
+
Page rules that read another item's answers can prevent reaching those answers on a later page. Inspect the shared `cross-item-page-rule` advisory; use submit-phase validation or an explicit unanswered-dependency condition when appropriate. Do not silently change an existing rule's phase.
|
|
294
432
|
|
|
295
433
|
## Prefills
|
|
296
434
|
|
|
297
|
-
|
|
435
|
+
Prefills initialize **once** when creating a new session, including hidden inputs, disabled inputs and closed follow-ups. They do not reapply while answering, after clearing a field, or on resume. A resumed session restores its versioned snapshot without new prefill lookups. Hiding/disabling retains initialized and participant-entered answers. Never use repeated prefilling to enforce exclusivity.
|
|
436
|
+
|
|
437
|
+
A prefill has `id`, optional boolean `when`, optional `apply: "ifEmpty"`, and `source`. Its target is the surrounding element's slot key, not an itemResponse/field/embeddedField descriptor.
|
|
298
438
|
|
|
299
439
|
```json
|
|
300
440
|
{
|
|
301
|
-
"
|
|
302
|
-
"
|
|
303
|
-
"
|
|
304
|
-
"
|
|
305
|
-
|
|
441
|
+
"kind": "set-prefill",
|
|
442
|
+
"elementId": "name-input",
|
|
443
|
+
"slotId": "name-slot",
|
|
444
|
+
"prefill": {
|
|
445
|
+
"id": "name-initial",
|
|
446
|
+
"source": { "type": "static", "value": { "type": "string", "value": "Example" } }
|
|
447
|
+
}
|
|
306
448
|
}
|
|
307
449
|
```
|
|
308
450
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
- `{ "type": "itemResponse" }`
|
|
312
|
-
- `{ "type": "field", "fieldId": "<fieldId>" }`
|
|
313
|
-
- `{ "type": "embeddedField", "optionId": "<optionId>", "fieldId": "<fieldId>" }`
|
|
314
|
-
|
|
315
|
-
Sources:
|
|
451
|
+
Sources are static typed value, expression, templateValue key, or previousResponse with `ref: {surveyKey?, slotId}` and optional `ifUnsupported: "skip"`. Inspect host capabilities before depending on previous responses. Values must match the target slot's domain; scores/computed slots are not prefill targets. Prefill dependencies also participate in cycle checking.
|
|
316
452
|
|
|
317
|
-
|
|
318
|
-
- `expression`: JsonExpression.
|
|
319
|
-
- `templateValue`: `{ "key": "<templateValueKey>" }`.
|
|
320
|
-
- `previousResponse`: `{ "ref": { "surveyKey"?, "itemId", "target"? }, "ifUnsupported": "skip" }`.
|
|
453
|
+
Do not assume a host provides previous responses. Survey Mode currently does not; it rejects publication with a required unsupported source. Use `ifUnsupported: "skip"` only when omitting that initial value is intentional. Invalid derived prefill values are skipped with a located diagnostic and later candidates may apply; do not treat that as permission to author incompatible values.
|
|
321
454
|
|
|
322
455
|
## Template Values
|
|
323
456
|
|
|
@@ -327,14 +460,20 @@ Template value shape:
|
|
|
327
460
|
{
|
|
328
461
|
"type": "default",
|
|
329
462
|
"returnType": "string",
|
|
330
|
-
"expression": {
|
|
463
|
+
"expression": {
|
|
464
|
+
"type": "const",
|
|
465
|
+
"value": {
|
|
466
|
+
"type": "string",
|
|
467
|
+
"value": "x"
|
|
468
|
+
}
|
|
469
|
+
}
|
|
331
470
|
}
|
|
332
471
|
```
|
|
333
472
|
|
|
334
|
-
Date formatting template values use `type: "
|
|
473
|
+
Date formatting template values use `type: "formatDate"`, `returnType: "string"`, and `dateFormat`.
|
|
335
474
|
|
|
336
475
|
## Bounded context and atomic batches
|
|
337
476
|
|
|
338
|
-
`survey_expression` action `context` filters response slots and expression locations with `itemIds` (
|
|
477
|
+
`survey_expression` action `context` filters response slots and expression locations with `itemIds` (exact persistent item IDs). Follow `nextCursor` with the same filter and `limit` until absent; restart after a draft mutation. The returned model text contains the actual page and distinguishes filtered totals from the survey-wide response-slot count. For a controlling question outside the filter, omit itemIds and cursor to retrieve the survey-wide catalog. Unresolved identifiers are reported explicitly, not as evidence that an item has no response.
|
|
339
478
|
|
|
340
479
|
A `prepare` mutation array is executed in order within one atomic delta. Later mutations see earlier additions, replacements, and removals, including new containers and shifted prefill indexes. If any mutation fails, none of the delta is staged. Retry all requested mutations from that rejected delta; `pendingRepairs` tracks every remaining operation and surface. For missing or invalid targets, use the indicated `retryOfChangeIds` with the complete corrected input. If an expression location is already empty, inspect and acknowledge that rejected request with a linked `survey_change` using `operations: []`; other identifiable pending edits remain required. Clearing a condition is not a repair of a requested condition change.
|