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