@surveystudio/node-registery 1.1.0 → 1.3.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 CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Typed survey question node registry with separate entrypoints for pure logic, runner UI, and builder UI.
4
4
 
5
- This package is currently in early migration. It exports the first migrated node, `plainText`, while the remaining SurveyChamp question nodes are being moved into the registry.
5
+ This package is currently in early migration. It exports migrated node logic for server runtimes and selected builder/runner UI adapters while the remaining SurveyChamp question nodes are being moved into the registry.
6
6
 
7
7
  ## Entrypoints
8
8
 
@@ -38,6 +38,13 @@ Migrated nodes:
38
38
  - `dateInput`
39
39
  - `multiInput`
40
40
  - `zipCodeInput`
41
+ - `singleChoice` logic
42
+ - `multipleChoice` logic
43
+ - `dropdown` logic
44
+ - `ranking` logic
45
+ - `rating` logic
46
+ - `slider` logic
47
+ - `consent` logic
41
48
  - `plainText`
42
49
 
43
50
  The registry contract test intentionally fails until all planned question nodes are migrated.
@@ -1,7 +1,7 @@
1
- export { B as BuilderRegistry, C as CompleteBuilderRegistry, N as NodeBuilder, a as NodeBuilderProps, b as NodeCanvasProps, Q as QuestionNodeDefinition, d as defineBuilderRegistry, c as defineQuestionNode } from './types-CgiAR_DF.mjs';
2
- import { J as JsonValue } from './coreTypes-YSpR0Oyh.mjs';
3
- export { N as NodeManifest, P as PropertyField, a as PropertyFieldType, S as SelectOption } from './coreTypes-YSpR0Oyh.mjs';
4
- import { P as PlainTextData, B as BaseTextData, M as MultiInputData, N as NumberInputData, T as TextInputData, Z as ZipCodeInputData } from './types-CjF8-OZi.mjs';
1
+ export { B as BuilderRegistry, C as CompleteBuilderRegistry, N as NodeBuilder, a as NodeBuilderProps, b as NodeCanvasProps, Q as QuestionNodeDefinition, d as defineBuilderRegistry, c as defineQuestionNode } from './types-4zXsOMLb.mjs';
2
+ import { J as JsonValue } from './coreTypes-CyFAym5A.mjs';
3
+ export { N as NodeManifest, P as PropertyField, a as PropertyFieldType, S as SelectOption } from './coreTypes-CyFAym5A.mjs';
4
+ import { P as PlainTextData, B as BaseTextData, M as MultiInputData, N as NumberInputData, T as TextInputData, Z as ZipCodeInputData } from './types-BMnck1ag.mjs';
5
5
  import 'react';
6
6
 
7
7
  declare const plainTextBuilder: {
@@ -82,4 +82,4 @@ type RegistryKeySet = Readonly<Partial<Record<SurveyNodeType, unknown>>>;
82
82
  declare function defineLogicRegistry<const TRegistry extends RegistryKeySet>(registry: TRegistry): TRegistry;
83
83
  declare function createInitialData<TData extends NodeData>(manifest: NodeManifest<SurveyNodeType, TData>): TData;
84
84
 
85
- export { type CompleteLogicRegistry as C, type DataType as D, type ExtractedValue as E, type JsonValue as J, type LogicRegistry as L, type NodeManifest as N, type PropertyField as P, type QuestionNodeType as Q, type SelectOption as S, type ValidationContext as V, type PropertyFieldType as a, type NodeLogic as b, type ExtractionContext as c, type NodeLogicContext as d, type ValidationResult as e, createInitialData as f, defineLogicRegistry as g, type NodeData as h, type SurveyNodeType as i, type NodeValue as j };
85
+ export { type CompleteLogicRegistry as C, type DataType as D, type ExtractedValue as E, type JsonValue as J, type LogicRegistry as L, type NodeManifest as N, type PropertyField as P, type QuestionNodeType as Q, type SelectOption as S, type ValidationContext as V, type PropertyFieldType as a, type NodeData as b, type NodeLogic as c, type ExtractionContext as d, type NodeLogicContext as e, type ValidationResult as f, createInitialData as g, defineLogicRegistry as h, type SurveyNodeType as i, type NodeValue as j };
package/dist/logic.d.mts CHANGED
@@ -1,9 +1,104 @@
1
- import { P as PlainTextData, a as PlainTextValue, D as DateInputData, b as TextValue, E as EmailInputData, M as MultiInputData, c as MultiInputValue, N as NumberInputData, T as TextInputData, Z as ZipCodeInputData, B as BaseTextData } from './types-CjF8-OZi.mjs';
2
- import { b as NodeLogic } from './coreTypes-YSpR0Oyh.mjs';
3
- export { C as CompleteLogicRegistry, D as DataType, E as ExtractedValue, c as ExtractionContext, L as LogicRegistry, d as NodeLogicContext, V as ValidationContext, e as ValidationResult, f as createInitialData, g as defineLogicRegistry } from './coreTypes-YSpR0Oyh.mjs';
1
+ import { P as PlainTextData, a as PlainTextValue, D as DateInputData, b as TextValue, E as EmailInputData, M as MultiInputData, c as MultiInputValue, N as NumberInputData, T as TextInputData, Z as ZipCodeInputData, B as BaseTextData } from './types-BMnck1ag.mjs';
2
+ import { b as NodeData, J as JsonValue, c as NodeLogic } from './coreTypes-CyFAym5A.mjs';
3
+ export { C as CompleteLogicRegistry, D as DataType, E as ExtractedValue, d as ExtractionContext, L as LogicRegistry, e as NodeLogicContext, V as ValidationContext, f as ValidationResult, g as createInitialData, h as defineLogicRegistry } from './coreTypes-CyFAym5A.mjs';
4
+
5
+ type ConditionData$2 = {
6
+ id: string;
7
+ type: "group";
8
+ logicType: "AND" | "OR";
9
+ children: JsonValue[];
10
+ };
11
+ type ConsentData = NodeData & {
12
+ label: string;
13
+ description: string;
14
+ condition: ConditionData$2;
15
+ checkboxLabel: string;
16
+ disagreeLabel?: string;
17
+ };
18
+ type ConsentValue = boolean;
19
+
20
+ type ConditionData$1 = {
21
+ id: string;
22
+ type: "group";
23
+ logicType: "AND" | "OR";
24
+ children: JsonValue[];
25
+ };
26
+ type ScaleItem = NodeData & {
27
+ id?: string;
28
+ exportId?: string;
29
+ technicalId?: string;
30
+ label?: string;
31
+ value?: string;
32
+ };
33
+ type ScaleResponseMode = "single" | "multi";
34
+ type BaseScaleData = NodeData & {
35
+ label: string;
36
+ description: string;
37
+ condition: ConditionData$1;
38
+ responseMode?: ScaleResponseMode;
39
+ items: ScaleItem[];
40
+ };
41
+ type RatingData = BaseScaleData & {
42
+ maxRating: number;
43
+ };
44
+ type SliderData = BaseScaleData & {
45
+ min: number;
46
+ max: number;
47
+ step: number;
48
+ startValue: number;
49
+ };
50
+ type ScaleValue = number | Record<string, number>;
51
+
52
+ type ConditionData = {
53
+ id: string;
54
+ type: "group";
55
+ logicType: "AND" | "OR";
56
+ children: JsonValue[];
57
+ };
58
+ type ChoiceOption = NodeData & {
59
+ id?: string;
60
+ label: string;
61
+ value: string;
62
+ imageUrl?: string;
63
+ };
64
+ type BaseChoiceData = NodeData & {
65
+ label: string;
66
+ description: string;
67
+ condition: ConditionData;
68
+ options: ChoiceOption[];
69
+ randomizeOptions?: boolean;
70
+ };
71
+ type SingleChoiceData = BaseChoiceData & {
72
+ allowOther?: boolean;
73
+ otherLabel?: string;
74
+ };
75
+ type DropdownData = BaseChoiceData & {
76
+ searchable?: boolean;
77
+ };
78
+ type MultipleChoiceData = BaseChoiceData & {
79
+ minChoices?: number;
80
+ maxChoices?: number;
81
+ };
82
+ type RankingData = BaseChoiceData & {
83
+ maxRank?: number;
84
+ displayMode?: "drag" | "select";
85
+ };
86
+ type SingleChoiceValue = string;
87
+ type MultipleChoiceValue = string[];
88
+ type RankingValue = string[];
4
89
 
5
90
  declare const plainTextLogic: NodeLogic<"plainText", PlainTextData, PlainTextValue>;
6
91
 
92
+ declare const consentLogic: NodeLogic<"consent", ConsentData, ConsentValue>;
93
+
94
+ declare const ratingLogic: NodeLogic<"rating", RatingData, ScaleValue>;
95
+ declare const sliderLogic: NodeLogic<"slider", SliderData, ScaleValue>;
96
+
97
+ declare const singleChoiceLogic: NodeLogic<"singleChoice", SingleChoiceData, SingleChoiceValue>;
98
+ declare const dropdownLogic: NodeLogic<"dropdown", DropdownData, SingleChoiceValue>;
99
+ declare const multipleChoiceLogic: NodeLogic<"multipleChoice", MultipleChoiceData, MultipleChoiceValue>;
100
+ declare const rankingLogic: NodeLogic<"ranking", RankingData, RankingValue>;
101
+
7
102
  declare const textInputLogic: NodeLogic<"textInput", TextInputData, TextValue>;
8
103
  declare const emailInputLogic: NodeLogic<"emailInput", EmailInputData, TextValue>;
9
104
  declare const dateInputLogic: NodeLogic<"dateInput", DateInputData, TextValue>;
@@ -18,7 +113,14 @@ declare const logicRegistry: {
18
113
  readonly dateInput: NodeLogic<"dateInput", BaseTextData, string>;
19
114
  readonly multiInput: NodeLogic<"multiInput", MultiInputData, MultiInputValue>;
20
115
  readonly zipCodeInput: NodeLogic<"zipCodeInput", ZipCodeInputData, string>;
116
+ readonly singleChoice: NodeLogic<"singleChoice", SingleChoiceData, string>;
117
+ readonly multipleChoice: NodeLogic<"multipleChoice", MultipleChoiceData, MultipleChoiceValue>;
118
+ readonly dropdown: NodeLogic<"dropdown", DropdownData, string>;
119
+ readonly ranking: NodeLogic<"ranking", RankingData, RankingValue>;
120
+ readonly rating: NodeLogic<"rating", RatingData, ScaleValue>;
121
+ readonly slider: NodeLogic<"slider", SliderData, ScaleValue>;
122
+ readonly consent: NodeLogic<"consent", ConsentData, boolean>;
21
123
  readonly plainText: NodeLogic<"plainText", PlainTextData, PlainTextValue>;
22
124
  };
23
125
 
24
- export { NodeLogic, dateInputLogic, emailInputLogic, logicRegistry, multiInputLogic, numberInputLogic, plainTextLogic, textInputLogic, zipCodeInputLogic };
126
+ export { NodeLogic, consentLogic, dateInputLogic, dropdownLogic, emailInputLogic, logicRegistry, multiInputLogic, multipleChoiceLogic, numberInputLogic, plainTextLogic, rankingLogic, ratingLogic, singleChoiceLogic, sliderLogic, textInputLogic, zipCodeInputLogic };
package/dist/logic.mjs CHANGED
@@ -67,30 +67,449 @@ var plainTextLogic = {
67
67
  ]
68
68
  };
69
69
 
70
- // src/nodes/textLike/manifest.ts
70
+ // src/nodes/consent/manifest.ts
71
71
  var defaultCondition = {
72
72
  id: "root",
73
73
  type: "group",
74
74
  logicType: "AND",
75
75
  children: []
76
76
  };
77
+ var consentDefaultData = {
78
+ label: "Consent",
79
+ description: "I agree to the terms and conditions.",
80
+ condition: defaultCondition,
81
+ checkboxLabel: "I agree",
82
+ disagreeLabel: "I do not agree to the terms"
83
+ };
84
+
85
+ // src/nodes/consent/logic.ts
86
+ var normalizeConsentValue = (value) => {
87
+ if (typeof value === "boolean") return value;
88
+ if (typeof value === "string") {
89
+ const normalized = value.trim().toLowerCase();
90
+ if (["true", "yes", "agree", "agreed", "1"].includes(normalized)) return true;
91
+ if (["false", "no", "disagree", "disagreed", "0"].includes(normalized)) return false;
92
+ }
93
+ if (typeof value === "number") return value !== 0;
94
+ return false;
95
+ };
96
+ var consentLogic = {
97
+ type: "consent",
98
+ dataType: "boolean",
99
+ defaultData: consentDefaultData,
100
+ defaultValue: false,
101
+ normalizeValue: normalizeConsentValue,
102
+ validate: () => ({ valid: true }),
103
+ extractValue: (value) => [
104
+ {
105
+ booleanValue: value,
106
+ textValue: value ? "Yes" : "No"
107
+ }
108
+ ]
109
+ };
110
+
111
+ // src/nodes/scale/manifest.ts
112
+ var defaultCondition2 = {
113
+ id: "root",
114
+ type: "group",
115
+ logicType: "AND",
116
+ children: []
117
+ };
118
+ var baseScaleData = {
119
+ label: "",
120
+ description: "",
121
+ condition: defaultCondition2,
122
+ responseMode: "single",
123
+ items: []
124
+ };
125
+ var responseModeProperty = {
126
+ name: "responseMode",
127
+ label: "Response Mode",
128
+ type: "select",
129
+ defaultValue: "single",
130
+ options: [
131
+ { label: "Single Question", value: "single" },
132
+ { label: "Multiple Items", value: "multi" }
133
+ ]
134
+ };
135
+ var commonProperties = [
136
+ responseModeProperty,
137
+ { name: "label", label: "Question Label", type: "text" },
138
+ { name: "description", label: "Description", type: "textarea" },
139
+ {
140
+ name: "condition",
141
+ label: "Logic Rule",
142
+ type: "condition",
143
+ defaultValue: defaultCondition2
144
+ },
145
+ { name: "items", label: "Items", type: "options", defaultValue: [] }
146
+ ];
147
+ var ratingDefaultData = {
148
+ ...baseScaleData,
149
+ label: "Rating",
150
+ maxRating: 5
151
+ };
152
+ var sliderDefaultData = {
153
+ ...baseScaleData,
154
+ label: "Slider / Scale",
155
+ min: 0,
156
+ max: 10,
157
+ step: 1,
158
+ startValue: 5
159
+ };
160
+ var ratingManifest = {
161
+ type: "rating",
162
+ label: "Rating",
163
+ description: "Star rating scale",
164
+ category: "choice",
165
+ dataType: "number",
166
+ defaultData: ratingDefaultData,
167
+ properties: [
168
+ ...commonProperties,
169
+ { name: "maxRating", label: "Max Stars", type: "number", defaultValue: 5 }
170
+ ]
171
+ };
172
+ var sliderManifest = {
173
+ type: "slider",
174
+ label: "Slider / Scale",
175
+ description: "Single or multi-item scale",
176
+ category: "choice",
177
+ dataType: "number",
178
+ defaultData: sliderDefaultData,
179
+ properties: [
180
+ ...commonProperties,
181
+ { name: "min", label: "Minimum", type: "number", defaultValue: 0 },
182
+ { name: "max", label: "Maximum", type: "number", defaultValue: 10 },
183
+ { name: "step", label: "Step", type: "number", defaultValue: 1, min: 0 },
184
+ { name: "startValue", label: "Start Value", type: "number", defaultValue: 5 }
185
+ ]
186
+ };
187
+
188
+ // src/nodes/scale/logic.ts
189
+ var isRecord = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
190
+ var numberValue = (value) => {
191
+ const parsed = Number(value);
192
+ return Number.isFinite(parsed) ? parsed : 0;
193
+ };
194
+ var positiveNumber = (value) => {
195
+ const parsed = Number(value);
196
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : 0;
197
+ };
198
+ var itemsFor = (nodeData) => Array.isArray(nodeData.items) ? nodeData.items : [];
199
+ var inferMode = (nodeData) => {
200
+ if (nodeData.responseMode === "single" || nodeData.responseMode === "multi") return nodeData.responseMode;
201
+ return itemsFor(nodeData).length > 0 ? "multi" : "single";
202
+ };
203
+ var itemKey = (item) => item.exportId || item.technicalId || item.value || item.id;
204
+ var itemLabel = (item, fallback) => item.label || fallback;
205
+ var firstItemKey = (nodeData) => {
206
+ const firstItem = itemsFor(nodeData)[0];
207
+ return firstItem ? itemKey(firstItem) : void 0;
208
+ };
209
+ var normalizeScaleValue = (rawValue, nodeData) => {
210
+ const mode = inferMode(nodeData);
211
+ if (mode === "multi") {
212
+ if (isRecord(rawValue)) {
213
+ return Object.fromEntries(Object.entries(rawValue).map(([key2, value]) => [key2, numberValue(value)]));
214
+ }
215
+ const key = firstItemKey(nodeData);
216
+ if (key) return { [key]: numberValue(rawValue) };
217
+ }
218
+ return numberValue(rawValue);
219
+ };
220
+ var validateFiniteScale = (value) => {
221
+ if (typeof value === "number") return Number.isFinite(value) ? { valid: true } : { valid: false, error: "Please enter a valid number" };
222
+ const invalidKey = Object.entries(value).find(([, itemValue]) => !Number.isFinite(itemValue))?.[0];
223
+ return invalidKey ? { valid: false, error: `${invalidKey}: Please enter a valid number` } : { valid: true };
224
+ };
225
+ var validateRatingValue = (value, nodeData) => {
226
+ const finiteResult = validateFiniteScale(value);
227
+ if (!finiteResult.valid) return finiteResult;
228
+ const maxRating = positiveNumber(nodeData.maxRating) || 5;
229
+ const values = typeof value === "number" ? [value] : Object.values(value);
230
+ const outOfRange = values.find((itemValue) => itemValue < 1 || itemValue > maxRating);
231
+ return typeof outOfRange === "number" ? { valid: false, error: `Rating must be between 1 and ${maxRating}` } : { valid: true };
232
+ };
233
+ var validateSliderValue = (value, nodeData) => {
234
+ const finiteResult = validateFiniteScale(value);
235
+ if (!finiteResult.valid) return finiteResult;
236
+ const min = numberValue(nodeData.min);
237
+ const max = Number.isFinite(Number(nodeData.max)) ? Number(nodeData.max) : 10;
238
+ const values = typeof value === "number" ? [value] : Object.values(value);
239
+ const outOfRange = values.find((itemValue) => itemValue < min || itemValue > max);
240
+ return typeof outOfRange === "number" ? { valid: false, error: `Value must be between ${min} and ${max}` } : { valid: true };
241
+ };
242
+ var extractScaleValue = (value, nodeData) => {
243
+ const mode = inferMode(nodeData);
244
+ if (mode === "multi" && isRecord(value)) {
245
+ const items = itemsFor(nodeData);
246
+ return Object.entries(value).map(([key, itemValue]) => {
247
+ const item = items.find((candidate) => itemKey(candidate) === key);
248
+ return {
249
+ columnKey: key,
250
+ columnLabel: item ? itemLabel(item, key) : key,
251
+ numberValue: itemValue,
252
+ textValue: String(itemValue)
253
+ };
254
+ });
255
+ }
256
+ const scalarValue = typeof value === "number" ? value : 0;
257
+ return [{ numberValue: scalarValue, textValue: String(scalarValue) }];
258
+ };
259
+ var ratingLogic = {
260
+ type: "rating",
261
+ dataType: "number",
262
+ defaultData: ratingDefaultData,
263
+ defaultValue: 0,
264
+ normalizeValue: normalizeScaleValue,
265
+ validate: validateRatingValue,
266
+ extractValue: extractScaleValue
267
+ };
268
+ var sliderLogic = {
269
+ type: "slider",
270
+ dataType: "number",
271
+ defaultData: sliderDefaultData,
272
+ defaultValue: 0,
273
+ normalizeValue: normalizeScaleValue,
274
+ validate: validateSliderValue,
275
+ extractValue: extractScaleValue
276
+ };
277
+
278
+ // src/nodes/choice/manifest.ts
279
+ var defaultCondition3 = {
280
+ id: "root",
281
+ type: "group",
282
+ logicType: "AND",
283
+ children: []
284
+ };
285
+ var baseChoiceData = {
286
+ label: "",
287
+ description: "",
288
+ condition: defaultCondition3,
289
+ options: [],
290
+ randomizeOptions: false
291
+ };
292
+ var commonProperties2 = [
293
+ { name: "label", label: "Question Label", type: "text" },
294
+ { name: "description", label: "Description", type: "textarea" },
295
+ {
296
+ name: "condition",
297
+ label: "Logic Rule",
298
+ type: "condition",
299
+ defaultValue: defaultCondition3
300
+ },
301
+ { name: "options", label: "Options", type: "options", defaultValue: [] },
302
+ { name: "bulkOptions", label: "Bulk Add (one per line)", type: "textarea", placeholder: "Option A\nOption B\nOption C..." }
303
+ ];
304
+ var singleChoiceDefaultData = {
305
+ ...baseChoiceData,
306
+ label: "Single Choice",
307
+ allowOther: false,
308
+ otherLabel: "Other"
309
+ };
310
+ var dropdownDefaultData = {
311
+ ...baseChoiceData,
312
+ label: "Dropdown",
313
+ searchable: true
314
+ };
315
+ var multipleChoiceDefaultData = {
316
+ ...baseChoiceData,
317
+ label: "Multiple Choice",
318
+ minChoices: 0,
319
+ maxChoices: 0
320
+ };
321
+ var rankingDefaultData = {
322
+ ...baseChoiceData,
323
+ label: "Ranking",
324
+ maxRank: 0,
325
+ displayMode: "drag"
326
+ };
327
+ var singleChoiceManifest = {
328
+ type: "singleChoice",
329
+ label: "Single Choice",
330
+ description: "Select one option",
331
+ category: "choice",
332
+ dataType: "option",
333
+ defaultData: singleChoiceDefaultData,
334
+ properties: [
335
+ ...commonProperties2,
336
+ { name: "allowOther", label: "Allow Other", type: "switch", defaultValue: false },
337
+ { name: "otherLabel", label: "Other Label", type: "text", defaultValue: "Other" },
338
+ { name: "randomizeOptions", label: "Randomize Options", type: "switch", defaultValue: false }
339
+ ]
340
+ };
341
+ var dropdownManifest = {
342
+ type: "dropdown",
343
+ label: "Dropdown",
344
+ description: "Select from a dropdown menu",
345
+ category: "choice",
346
+ dataType: "option",
347
+ defaultData: dropdownDefaultData,
348
+ properties: [
349
+ ...commonProperties2,
350
+ { name: "searchable", label: "Searchable", type: "switch", defaultValue: true }
351
+ ]
352
+ };
353
+ var multipleChoiceManifest = {
354
+ type: "multipleChoice",
355
+ label: "Multiple Choice",
356
+ description: "Select multiple options",
357
+ category: "choice",
358
+ dataType: "array",
359
+ defaultData: multipleChoiceDefaultData,
360
+ properties: [
361
+ ...commonProperties2,
362
+ { name: "minChoices", label: "Minimum Choices", type: "number", defaultValue: 0 },
363
+ { name: "maxChoices", label: "Maximum Choices", type: "number", defaultValue: 0 },
364
+ { name: "randomizeOptions", label: "Randomize Options", type: "switch", defaultValue: false }
365
+ ]
366
+ };
367
+ var rankingManifest = {
368
+ type: "ranking",
369
+ label: "Ranking",
370
+ description: "Rank options in order",
371
+ category: "choice",
372
+ dataType: "array",
373
+ defaultData: rankingDefaultData,
374
+ properties: [
375
+ ...commonProperties2,
376
+ { name: "maxRank", label: "Maximum Rank", type: "number", defaultValue: 0 },
377
+ {
378
+ name: "displayMode",
379
+ label: "Display Mode",
380
+ type: "select",
381
+ defaultValue: "drag",
382
+ options: [
383
+ { label: "Drag and Drop", value: "drag" },
384
+ { label: "Dropdown Selection", value: "select" }
385
+ ]
386
+ }
387
+ ]
388
+ };
389
+
390
+ // src/nodes/choice/logic.ts
391
+ var normalizeChoiceValue = (value) => {
392
+ if (typeof value === "string") return value;
393
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
394
+ return "";
395
+ };
396
+ var normalizeChoiceArray = (value) => {
397
+ if (!Array.isArray(value)) return [];
398
+ return value.map(normalizeChoiceValue).filter(Boolean);
399
+ };
400
+ var positiveNumber2 = (value) => {
401
+ const parsed = Number(value);
402
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : 0;
403
+ };
404
+ var optionsFor = (nodeData) => Array.isArray(nodeData.options) ? nodeData.options : [];
405
+ var optionForValue = (nodeData, value) => optionsFor(nodeData).find((option) => option.value === value || option.id === value);
406
+ var optionLabel = (nodeData, value) => optionForValue(nodeData, value)?.label || value;
407
+ var optionUuid = (nodeData, value) => optionForValue(nodeData, value)?.value || value;
408
+ var validateOptionExists = (value, nodeData) => {
409
+ if (!value) return { valid: true };
410
+ if (optionsFor(nodeData).length === 0) return { valid: true };
411
+ return optionForValue(nodeData, value) ? { valid: true } : { valid: false, error: "Selected option is not available" };
412
+ };
413
+ var validateOptionArray = (value, nodeData) => {
414
+ if (optionsFor(nodeData).length === 0) return { valid: true };
415
+ const invalid = value.find((item) => !optionForValue(nodeData, item));
416
+ return invalid ? { valid: false, error: "Selected option is not available" } : { valid: true };
417
+ };
418
+ var extractSingleChoiceValue = (value, nodeData) => [
419
+ {
420
+ optionUuid: optionUuid(nodeData, value),
421
+ optionText: optionLabel(nodeData, value),
422
+ textValue: optionLabel(nodeData, value)
423
+ }
424
+ ];
425
+ var singleChoiceLogic = {
426
+ type: "singleChoice",
427
+ dataType: "option",
428
+ defaultData: singleChoiceDefaultData,
429
+ defaultValue: "",
430
+ normalizeValue: normalizeChoiceValue,
431
+ validate: validateOptionExists,
432
+ extractValue: extractSingleChoiceValue
433
+ };
434
+ var dropdownLogic = {
435
+ type: "dropdown",
436
+ dataType: "option",
437
+ defaultData: dropdownDefaultData,
438
+ defaultValue: "",
439
+ normalizeValue: normalizeChoiceValue,
440
+ validate: validateOptionExists,
441
+ extractValue: extractSingleChoiceValue
442
+ };
443
+ var multipleChoiceLogic = {
444
+ type: "multipleChoice",
445
+ dataType: "array",
446
+ defaultData: multipleChoiceDefaultData,
447
+ defaultValue: [],
448
+ normalizeValue: normalizeChoiceArray,
449
+ validate: (value, nodeData) => {
450
+ const optionResult = validateOptionArray(value, nodeData);
451
+ if (!optionResult.valid) return optionResult;
452
+ const minChoices = positiveNumber2(nodeData.minChoices);
453
+ const maxChoices = positiveNumber2(nodeData.maxChoices);
454
+ if (minChoices && value.length < minChoices) return { valid: false, error: `Select at least ${minChoices} option(s)` };
455
+ if (maxChoices && value.length > maxChoices) return { valid: false, error: `Select at most ${maxChoices} option(s)` };
456
+ return { valid: true };
457
+ },
458
+ extractValue: (value, nodeData) => value.map((item) => ({
459
+ optionUuid: optionUuid(nodeData, item),
460
+ optionText: optionLabel(nodeData, item),
461
+ textValue: optionLabel(nodeData, item)
462
+ }))
463
+ };
464
+ var rankingLogic = {
465
+ type: "ranking",
466
+ dataType: "array",
467
+ defaultData: rankingDefaultData,
468
+ defaultValue: [],
469
+ normalizeValue: normalizeChoiceArray,
470
+ validate: (value, nodeData) => {
471
+ const optionResult = validateOptionArray(value, nodeData);
472
+ if (!optionResult.valid) return optionResult;
473
+ const maxRank = positiveNumber2(nodeData.maxRank);
474
+ if (maxRank && value.length > maxRank) return { valid: false, error: `Rank at most ${maxRank} option(s)` };
475
+ return { valid: true };
476
+ },
477
+ extractValue: (value, nodeData) => value.map((item, index) => {
478
+ const rank = index + 1;
479
+ const label = optionLabel(nodeData, item);
480
+ return {
481
+ optionUuid: optionUuid(nodeData, item),
482
+ optionText: label,
483
+ numberValue: rank,
484
+ textValue: `${rank}. ${label}`
485
+ };
486
+ })
487
+ };
488
+
489
+ // src/nodes/textLike/manifest.ts
490
+ var defaultCondition4 = {
491
+ id: "root",
492
+ type: "group",
493
+ logicType: "AND",
494
+ children: []
495
+ };
77
496
  var baseTextData = {
78
497
  label: "",
79
498
  description: "",
80
- condition: defaultCondition,
499
+ condition: defaultCondition4,
81
500
  minChars: 0,
82
501
  maxChars: 0,
83
502
  minWords: 0,
84
503
  maxWords: 0
85
504
  };
86
- var commonProperties = [
505
+ var commonProperties3 = [
87
506
  { name: "label", label: "Field Label", type: "text" },
88
507
  { name: "description", label: "Description", type: "textarea" },
89
508
  {
90
509
  name: "condition",
91
510
  label: "Logic Rule",
92
511
  type: "condition",
93
- defaultValue: defaultCondition
512
+ defaultValue: defaultCondition4
94
513
  }
95
514
  ];
96
515
  var textLimitProperties = [
@@ -129,7 +548,7 @@ var textInputManifest = {
129
548
  dataType: "text",
130
549
  defaultData: textInputDefaultData,
131
550
  properties: [
132
- ...commonProperties,
551
+ ...commonProperties3,
133
552
  { name: "placeholder", label: "Placeholder", type: "text", placeholder: "e.g., Type here...", defaultValue: "" },
134
553
  { name: "longAnswer", label: "Long Answer (Multi-line)", type: "switch", defaultValue: false },
135
554
  ...textLimitProperties
@@ -143,7 +562,7 @@ var numberInputManifest = {
143
562
  dataType: "number",
144
563
  defaultData: numberInputDefaultData,
145
564
  properties: [
146
- ...commonProperties,
565
+ ...commonProperties3,
147
566
  { name: "min", label: "Minimum Value", type: "number" },
148
567
  { name: "max", label: "Maximum Value", type: "number" }
149
568
  ]
@@ -156,7 +575,7 @@ var zipCodeInputManifest = {
156
575
  dataType: "text",
157
576
  defaultData: zipCodeInputDefaultData,
158
577
  properties: [
159
- ...commonProperties,
578
+ ...commonProperties3,
160
579
  { name: "allowedZips", label: "Allowed Zip Codes", type: "fileTextarea", placeholder: "10001, 10002, 90210... (Leave empty to allow all)", helperText: "Validation: Only users entering these zip codes can proceed. Others will be blocked." }
161
580
  ]
162
581
  };
@@ -168,7 +587,7 @@ var multiInputManifest = {
168
587
  dataType: "object",
169
588
  defaultData: multiInputDefaultData,
170
589
  properties: [
171
- ...commonProperties,
590
+ ...commonProperties3,
172
591
  { name: "fields", label: "Input Fields", type: "options", defaultValue: [], helperText: "Value column represents input type (text, number, email)" },
173
592
  ...textLimitProperties
174
593
  ]
@@ -261,12 +680,12 @@ var numberInputLogic = {
261
680
  const limitResult = validateTextLimits(value, nodeData);
262
681
  if (!limitResult.valid) return limitResult;
263
682
  if (!value.trim()) return { valid: true };
264
- const numberValue = Number(value);
265
- if (Number.isNaN(numberValue)) return { valid: false, error: "Please enter a valid number" };
266
- if (typeof nodeData.min === "number" && numberValue < nodeData.min) {
683
+ const numberValue2 = Number(value);
684
+ if (Number.isNaN(numberValue2)) return { valid: false, error: "Please enter a valid number" };
685
+ if (typeof nodeData.min === "number" && numberValue2 < nodeData.min) {
267
686
  return { valid: false, error: `Minimum value is ${nodeData.min}` };
268
687
  }
269
- if (typeof nodeData.max === "number" && numberValue > nodeData.max) {
688
+ if (typeof nodeData.max === "number" && numberValue2 > nodeData.max) {
270
689
  return { valid: false, error: `Maximum value is ${nodeData.max}` };
271
690
  }
272
691
  return { valid: true };
@@ -337,17 +756,31 @@ var logicRegistry = defineLogicRegistry({
337
756
  dateInput: dateInputLogic,
338
757
  multiInput: multiInputLogic,
339
758
  zipCodeInput: zipCodeInputLogic,
759
+ singleChoice: singleChoiceLogic,
760
+ multipleChoice: multipleChoiceLogic,
761
+ dropdown: dropdownLogic,
762
+ ranking: rankingLogic,
763
+ rating: ratingLogic,
764
+ slider: sliderLogic,
765
+ consent: consentLogic,
340
766
  plainText: plainTextLogic
341
767
  });
342
768
  export {
769
+ consentLogic,
343
770
  createInitialData,
344
771
  dateInputLogic,
345
772
  defineLogicRegistry,
773
+ dropdownLogic,
346
774
  emailInputLogic,
347
775
  logicRegistry,
348
776
  multiInputLogic,
777
+ multipleChoiceLogic,
349
778
  numberInputLogic,
350
779
  plainTextLogic,
780
+ rankingLogic,
781
+ ratingLogic,
782
+ singleChoiceLogic,
783
+ sliderLogic,
351
784
  textInputLogic,
352
785
  zipCodeInputLogic
353
786
  };
package/dist/runner.d.mts CHANGED
@@ -1,6 +1,6 @@
1
- export { e as CompleteRunnerRegistry, f as NodeRunner, g as NodeRunnerProps, Q as QuestionNodeDefinition, R as RunnerRegistry, c as defineQuestionNode, h as defineRunnerRegistry } from './types-CgiAR_DF.mjs';
1
+ export { e as CompleteRunnerRegistry, f as NodeRunner, g as NodeRunnerProps, Q as QuestionNodeDefinition, R as RunnerRegistry, c as defineQuestionNode, h as defineRunnerRegistry } from './types-4zXsOMLb.mjs';
2
2
  import 'react';
3
- import './coreTypes-YSpR0Oyh.mjs';
3
+ import './coreTypes-CyFAym5A.mjs';
4
4
 
5
5
  declare const plainTextRunner: {
6
6
  type: "plainText";
@@ -1,5 +1,5 @@
1
1
  import { ReactNode, ComponentType } from 'react';
2
- import { i as SurveyNodeType, h as NodeData, Q as QuestionNodeType, j as NodeValue, N as NodeManifest, b as NodeLogic } from './coreTypes-YSpR0Oyh.mjs';
2
+ import { i as SurveyNodeType, b as NodeData, Q as QuestionNodeType, j as NodeValue, N as NodeManifest, c as NodeLogic } from './coreTypes-CyFAym5A.mjs';
3
3
 
4
4
  interface NodeRunnerProps<TData extends NodeData = NodeData, TValue extends NodeValue = NodeValue> {
5
5
  readonly data: Readonly<TData>;
@@ -1,4 +1,4 @@
1
- import { h as NodeData, J as JsonValue } from './coreTypes-YSpR0Oyh.mjs';
1
+ import { b as NodeData, J as JsonValue } from './coreTypes-CyFAym5A.mjs';
2
2
 
3
3
  type PlainTextData = NodeData & {
4
4
  label: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@surveystudio/node-registery",
3
- "version": "1.1.0",
3
+ "version": "1.3.0",
4
4
  "description": "Typed survey question node registry with split builder, runner, and logic entrypoints.",
5
5
  "main": "./dist/logic.mjs",
6
6
  "types": "./dist/logic.d.mts",