@petrarca/sonnet-core 0.5.0 → 0.7.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.
@@ -3,6 +3,7 @@ import {
3
3
  } from "../chunk-XAEHYRXC.js";
4
4
  import {
5
5
  FORM_ITEM_ID_FIELD,
6
+ generateId,
6
7
  isEqual
7
8
  } from "../chunk-XCCOEUVC.js";
8
9
 
@@ -57,7 +58,7 @@ function getFieldTitle(fieldName, property) {
57
58
  return property["x-ui-title"] || property.title || fieldName;
58
59
  }
59
60
  function getFieldHelpText(property) {
60
- return property["x-ui-description"] ?? property["x-ui-help"];
61
+ return property["x-ui-description"] ?? property.description;
61
62
  }
62
63
  function getFieldPlaceholder(property) {
63
64
  return property["x-ui-placeholder"];
@@ -96,6 +97,48 @@ function hasProperties(schema) {
96
97
  return Object.keys(schema.properties || {}).length > 0;
97
98
  }
98
99
 
100
+ // src/schema/variant.ts
101
+ function isOneOf(property) {
102
+ return Array.isArray(property.oneOf) && property.oneOf.length > 0;
103
+ }
104
+ function getDiscriminator(property) {
105
+ const explicit = property.discriminator?.propertyName;
106
+ if (typeof explicit === "string" && explicit) return explicit;
107
+ const branches = property.oneOf;
108
+ if (!Array.isArray(branches) || branches.length === 0) return void 0;
109
+ const first = branches[0].properties ?? {};
110
+ const candidates = Object.keys(first).filter(
111
+ (k) => first[k]?.const !== void 0
112
+ );
113
+ return candidates.find(
114
+ (key) => branches.every((b) => b.properties?.[key]?.const !== void 0)
115
+ );
116
+ }
117
+ function getBranchValue(branch, discriminator) {
118
+ const c = branch.properties?.[discriminator]?.const;
119
+ if (typeof c === "string" || typeof c === "number" || typeof c === "boolean") {
120
+ return c;
121
+ }
122
+ return void 0;
123
+ }
124
+ function getBranchLabel(branch, discriminator, index) {
125
+ if (branch.title) return branch.title;
126
+ if (discriminator) {
127
+ const v = getBranchValue(branch, discriminator);
128
+ if (v !== void 0) return String(v);
129
+ }
130
+ return `Option ${index + 1}`;
131
+ }
132
+ function findActiveBranch(property, data, discriminator) {
133
+ const branches = property.oneOf;
134
+ if (!Array.isArray(branches) || !discriminator || !data) return -1;
135
+ const current = data[discriminator];
136
+ if (current === void 0) return -1;
137
+ return branches.findIndex(
138
+ (b) => getBranchValue(b, discriminator) === current
139
+ );
140
+ }
141
+
99
142
  // src/schema/schemaResolver.ts
100
143
  import Ajv from "ajv";
101
144
  import addFormats from "ajv-formats";
@@ -112,9 +155,29 @@ function resolveSchema(schema, formData) {
112
155
  return schema;
113
156
  }
114
157
  let resolvedSchema = resolveAllOf(schema, formData);
158
+ resolvedSchema = resolveAnyOfConditionals(resolvedSchema, formData);
115
159
  resolvedSchema = resolveConditionals(resolvedSchema, formData);
116
160
  return resolvedSchema;
117
161
  }
162
+ function resolveAnyOfConditionals(schema, formData) {
163
+ if (!schema.anyOf || !Array.isArray(schema.anyOf)) return schema;
164
+ const conditionalMembers = schema.anyOf.filter(
165
+ (m) => typeof m === "object" && m !== null && "if" in m
166
+ );
167
+ if (conditionalMembers.length === 0) return schema;
168
+ const nonConditionalMembers = schema.anyOf.filter(
169
+ (m) => !(typeof m === "object" && m !== null && "if" in m)
170
+ );
171
+ const { anyOf: _anyOf, ...baseSchema } = schema;
172
+ let result = nonConditionalMembers.length > 0 ? { ...baseSchema, anyOf: nonConditionalMembers } : baseSchema;
173
+ for (const member of conditionalMembers) {
174
+ const resolved = resolveConditionals(member, formData);
175
+ if (resolved !== member && typeof resolved === "object") {
176
+ result = mergeSchemas(result, resolved);
177
+ }
178
+ }
179
+ return result;
180
+ }
118
181
  function resolveConditionals(schema, formData) {
119
182
  if (!schema.if) {
120
183
  return schema;
@@ -212,6 +275,65 @@ function hasConditionals(schema) {
212
275
  return !!(schema.if || schema.allOf || schema.anyOf || schema.oneOf);
213
276
  }
214
277
 
278
+ // src/schema/uiRule.ts
279
+ var validatorCache = /* @__PURE__ */ new Map();
280
+ function getValidator(schema) {
281
+ const key = JSON.stringify(schema);
282
+ let validator = validatorCache.get(key);
283
+ if (!validator) {
284
+ validator = ajv.compile(schema);
285
+ validatorCache.set(key, validator);
286
+ }
287
+ return validator;
288
+ }
289
+ var VISIBLE_ENABLED = { visible: true, enabled: true };
290
+ function unescapeToken(token) {
291
+ return token.replace(/~1/g, "/").replace(/~0/g, "~");
292
+ }
293
+ function getAtPointer(data, pointer) {
294
+ if (pointer === "") return data;
295
+ if (!pointer.startsWith("/")) return void 0;
296
+ const tokens = pointer.slice(1).split("/").map(unescapeToken);
297
+ let current = data;
298
+ for (const token of tokens) {
299
+ if (current == null || typeof current !== "object") return void 0;
300
+ if (Array.isArray(current)) {
301
+ const idx = Number(token);
302
+ if (!Number.isInteger(idx) || idx < 0 || idx >= current.length)
303
+ return void 0;
304
+ current = current[idx];
305
+ } else {
306
+ current = current[token];
307
+ }
308
+ }
309
+ return current;
310
+ }
311
+ function conditionHolds(rule, formData) {
312
+ const { scope, schema } = rule.condition;
313
+ const value = getAtPointer(formData, scope);
314
+ try {
315
+ const validator = getValidator(schema);
316
+ return !!validator(value);
317
+ } catch (error) {
318
+ warnLog("Error evaluating x-ui-rule condition:", error);
319
+ return false;
320
+ }
321
+ }
322
+ function evaluateUiRule(rule, formData) {
323
+ if (!rule) return VISIBLE_ENABLED;
324
+ const holds = conditionHolds(rule, formData);
325
+ switch (rule.effect) {
326
+ case "show":
327
+ return { visible: holds, enabled: true };
328
+ case "hide":
329
+ return { visible: !holds, enabled: true };
330
+ case "enable":
331
+ return { visible: true, enabled: holds };
332
+ case "disable":
333
+ return { visible: true, enabled: !holds };
334
+ }
335
+ }
336
+
215
337
  // src/schema/schemaUtils.ts
216
338
  function applySchemaDefaults(data, schema) {
217
339
  const result = { ...data };
@@ -269,51 +391,56 @@ function extractDefName(ref) {
269
391
  const match = ref.match(/^#\/(\$defs|definitions)\/(.+)$/);
270
392
  return match ? match[2] : null;
271
393
  }
272
- function resolveRef(node, defs, seen) {
394
+ function inDocumentResolver(defs) {
395
+ return (ref) => {
396
+ const name = extractDefName(ref);
397
+ return name ? defs[name] : void 0;
398
+ };
399
+ }
400
+ function resolveRef(node, resolve, seen) {
273
401
  const ref = node.$ref;
274
- const name = extractDefName(ref);
275
- if (!name || !defs[name]) return node;
402
+ const resolved = resolve(ref);
403
+ if (!resolved) return node;
276
404
  if (seen.has(ref)) return node;
277
405
  seen.add(ref);
278
- const resolved = defs[name];
279
406
  const overrides = Object.fromEntries(
280
407
  Object.entries(node).filter(([k]) => k !== "$ref")
281
408
  );
282
409
  const merged = Object.keys(overrides).length > 0 ? { ...resolved, ...overrides } : resolved;
283
- const result = resolveNode(merged, defs, seen);
410
+ const result = resolveNode(merged, resolve, seen);
284
411
  seen.delete(ref);
285
412
  return result;
286
413
  }
287
- function resolveList(list, defs, seen) {
288
- const resolved = list.map((s) => resolveNode(s, defs, seen));
414
+ function resolveList(list, resolve, seen) {
415
+ const resolved = list.map((s) => resolveNode(s, resolve, seen));
289
416
  return resolved.some((s, i) => s !== list[i]) ? resolved : null;
290
417
  }
291
- function resolveProperties(props, defs, seen) {
418
+ function resolveProperties(props, resolve, seen) {
292
419
  const entries = Object.entries(props);
293
420
  const resolved = entries.map(
294
- ([k, v]) => [k, resolveNode(v, defs, seen)]
421
+ ([k, v]) => [k, resolveNode(v, resolve, seen)]
295
422
  );
296
423
  return resolved.some(([, v], i) => v !== entries[i][1]) ? Object.fromEntries(resolved) : null;
297
424
  }
298
- function resolveItems(items, defs, seen) {
299
- const r = resolveNode(items, defs, seen);
425
+ function resolveItems(items, resolve, seen) {
426
+ const r = resolveNode(items, resolve, seen);
300
427
  return r !== items ? r : null;
301
428
  }
302
- function resolveChildren(node, defs, seen) {
429
+ function resolveChildren(node, resolve, seen) {
303
430
  const overrides = {};
304
431
  let changed = false;
305
- const props = node.properties ? resolveProperties(node.properties, defs, seen) : null;
432
+ const props = node.properties ? resolveProperties(node.properties, resolve, seen) : null;
306
433
  if (props) {
307
434
  overrides.properties = props;
308
435
  changed = true;
309
436
  }
310
- const items = node.items ? resolveItems(node.items, defs, seen) : null;
437
+ const items = node.items ? resolveItems(node.items, resolve, seen) : null;
311
438
  if (items) {
312
439
  overrides.items = items;
313
440
  changed = true;
314
441
  }
315
442
  for (const key of ["allOf", "anyOf", "oneOf"]) {
316
- const list = node[key] ? resolveList(node[key], defs, seen) : null;
443
+ const list = node[key] ? resolveList(node[key], resolve, seen) : null;
317
444
  if (list) {
318
445
  overrides[key] = list;
319
446
  changed = true;
@@ -321,39 +448,60 @@ function resolveChildren(node, defs, seen) {
321
448
  }
322
449
  return changed ? overrides : null;
323
450
  }
324
- function resolveNode(node, defs, seen) {
325
- if (node.$ref) return resolveRef(node, defs, seen);
326
- const overrides = resolveChildren(node, defs, seen);
451
+ function resolveNode(node, resolve, seen) {
452
+ if (node.$ref) return resolveRef(node, resolve, seen);
453
+ const overrides = resolveChildren(node, resolve, seen);
327
454
  return overrides ? { ...node, ...overrides } : node;
328
455
  }
329
- function resolveRefs(schema) {
456
+ function resolveRefs(schema, resolve) {
330
457
  const defs = schema.$defs ?? schema.definitions ?? {};
331
- if (Object.keys(defs).length === 0) return schema;
458
+ if (!resolve && Object.keys(defs).length === 0) return schema;
459
+ const fallback = inDocumentResolver(defs);
460
+ const resolver = resolve ? (ref) => resolve(ref) ?? fallback(ref) : fallback;
332
461
  const seen = /* @__PURE__ */ new Set();
333
462
  const resolved = resolveNode(
334
463
  schema,
335
- defs,
464
+ resolver,
336
465
  seen
337
466
  );
338
467
  return resolved;
339
468
  }
340
469
 
341
470
  // src/schema/validateFormData.ts
471
+ function escapePointerToken(token) {
472
+ return token.replace(/~/g, "~0").replace(/\//g, "~1");
473
+ }
342
474
  function isEmpty(value) {
343
475
  return value === void 0 || value === null || value === "";
344
476
  }
345
- function fieldLabel(path, fieldName, property) {
346
- return path ? `${path} > ${getFieldTitle(fieldName, property)}` : getFieldTitle(fieldName, property);
347
- }
348
477
  function isPlainObject(value) {
349
478
  return value !== null && typeof value === "object" && !Array.isArray(value);
350
479
  }
351
- function validateArray(property, value, label, errors) {
480
+ function childContext(parent, fieldName, property) {
481
+ const title = getFieldTitle(fieldName, property);
482
+ return {
483
+ label: parent.label ? `${parent.label} > ${title}` : title,
484
+ pointer: `${parent.pointer}/${escapePointerToken(fieldName)}`
485
+ };
486
+ }
487
+ function itemContext(parent, index) {
488
+ return {
489
+ label: `${parent.label}[${index + 1}]`,
490
+ pointer: `${parent.pointer}/${index}`
491
+ };
492
+ }
493
+ function validateArray(property, value, ctx, out) {
352
494
  if (property.minItems !== void 0 && value.length < property.minItems) {
353
- errors.push(`${label} requires at least ${property.minItems} item(s)`);
495
+ out.push({
496
+ pointer: ctx.pointer,
497
+ message: `${ctx.label} requires at least ${property.minItems} item(s)`
498
+ });
354
499
  }
355
500
  if (property.maxItems !== void 0 && value.length > property.maxItems) {
356
- errors.push(`${label} allows at most ${property.maxItems} item(s)`);
501
+ out.push({
502
+ pointer: ctx.pointer,
503
+ message: `${ctx.label} allows at most ${property.maxItems} item(s)`
504
+ });
357
505
  }
358
506
  if (!property.items?.properties) return;
359
507
  const itemProperties = property.items.properties;
@@ -364,8 +512,8 @@ function validateArray(property, value, label, errors) {
364
512
  itemProperties,
365
513
  itemSchema,
366
514
  value[i],
367
- `${label}[${i + 1}]`,
368
- errors
515
+ itemContext(ctx, i),
516
+ out
369
517
  );
370
518
  }
371
519
  }
@@ -373,32 +521,47 @@ function validateArray(property, value, label, errors) {
373
521
  function isNestedObject(property, value) {
374
522
  return property.type === "object" && !!property.properties && isPlainObject(value);
375
523
  }
376
- function validateField(property, value, required, label, errors) {
524
+ function validateField(property, value, required, ctx, out) {
377
525
  if (required && isEmpty(value)) {
378
- errors.push(`${label} is required`);
526
+ out.push({ pointer: ctx.pointer, message: `${ctx.label} is required` });
379
527
  return;
380
528
  }
381
529
  if (isNestedObject(property, value)) {
382
- validateObject(property.properties, property, value, label, errors);
530
+ validateObject(property.properties, property, value, ctx, out);
383
531
  return;
384
532
  }
385
533
  if (property.type === "array" && Array.isArray(value)) {
386
- validateArray(property, value, label, errors);
534
+ validateArray(property, value, ctx, out);
387
535
  }
388
536
  }
389
- function validateObject(properties, schema, data, path, errors) {
537
+ function validateObject(properties, schema, data, ctx, out) {
390
538
  for (const fieldName of Object.keys(properties)) {
391
539
  const property = properties[fieldName];
392
540
  const required = schema.required?.includes(fieldName) ?? false;
393
- const label = fieldLabel(path, fieldName, property);
394
- validateField(property, data[fieldName], required, label, errors);
541
+ validateField(
542
+ property,
543
+ data[fieldName],
544
+ required,
545
+ childContext(ctx, fieldName, property),
546
+ out
547
+ );
395
548
  }
396
549
  }
397
550
  function validateFormData(schema, data) {
398
- const errors = [];
551
+ const fieldErrors = [];
399
552
  const properties = getSchemaProperties(schema);
400
- validateObject(properties, schema, data, "", errors);
401
- return { errors, isValid: errors.length === 0 };
553
+ validateObject(
554
+ properties,
555
+ schema,
556
+ data,
557
+ { label: "", pointer: "" },
558
+ fieldErrors
559
+ );
560
+ return {
561
+ errors: fieldErrors.map((e) => e.message),
562
+ fieldErrors,
563
+ isValid: fieldErrors.length === 0
564
+ };
402
565
  }
403
566
 
404
567
  // src/schema/diffFormData.ts
@@ -543,13 +706,59 @@ function diffFormData(original, updated) {
543
706
  }
544
707
  return diff;
545
708
  }
709
+
710
+ // src/schema/ensureFormItemIds.ts
711
+ function isPlainObject2(v) {
712
+ return typeof v === "object" && v !== null && !Array.isArray(v);
713
+ }
714
+ function isObjectArray2(property) {
715
+ return property.type === "array" && !!property.items?.properties;
716
+ }
717
+ function stampItem(item, itemSchema) {
718
+ if (!isPlainObject2(item)) return item;
719
+ const withChildren = stampObject(item, itemSchema.properties ?? {});
720
+ return FORM_ITEM_ID_FIELD in withChildren ? withChildren : { ...withChildren, [FORM_ITEM_ID_FIELD]: generateId() };
721
+ }
722
+ function stampValue(value, property) {
723
+ if (isObjectArray2(property) && Array.isArray(value)) {
724
+ return value.map((item) => stampItem(item, property.items));
725
+ }
726
+ if (property.type === "object" && property.properties && isPlainObject2(value)) {
727
+ return stampObject(value, property.properties);
728
+ }
729
+ return value;
730
+ }
731
+ function stampObject(data, properties) {
732
+ let changed = false;
733
+ const out = { ...data };
734
+ for (const [name, property] of Object.entries(properties)) {
735
+ if (!(name in data)) continue;
736
+ const next = stampValue(data[name], property);
737
+ if (next !== data[name]) {
738
+ out[name] = next;
739
+ changed = true;
740
+ }
741
+ }
742
+ return changed ? out : data;
743
+ }
744
+ function ensureFormItemIds(data, schema) {
745
+ return stampObject(data, getSchemaProperties(schema));
746
+ }
546
747
  export {
547
748
  WIDTH_TOKEN_REMS,
749
+ ajv,
548
750
  applySchemaDefaults,
549
751
  diffFormData,
752
+ ensureFormItemIds,
753
+ evaluateUiRule,
550
754
  extractEnumValues,
551
755
  extractSchemaDefaults,
756
+ findActiveBranch,
552
757
  generateJsonSchemaFromVariables,
758
+ getAtPointer,
759
+ getBranchLabel,
760
+ getBranchValue,
761
+ getDiscriminator,
553
762
  getFieldConstraints,
554
763
  getFieldDefault,
555
764
  getFieldHelpText,
@@ -565,6 +774,7 @@ export {
565
774
  isFieldOptional,
566
775
  isFieldRequired,
567
776
  isNullableBoolean,
777
+ isOneOf,
568
778
  matchesConst,
569
779
  resolveRefs,
570
780
  resolveSchema,