schemair 0.1.0 → 0.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.
Files changed (83) hide show
  1. package/README.md +542 -32
  2. package/dist/authoring/index.cjs +13 -350
  3. package/dist/authoring/index.d.cts +2 -90
  4. package/dist/authoring/index.d.ts +2 -90
  5. package/dist/authoring/index.js +2 -304
  6. package/dist/authoring-Dnrg5TXm.cjs +389 -0
  7. package/dist/authoring-DyJNYs5o.js +362 -0
  8. package/dist/chunk-bFfwrPtJ.js +18 -0
  9. package/dist/compatibility/index.cjs +43 -0
  10. package/dist/compatibility/index.d.cts +2 -0
  11. package/dist/compatibility/index.d.ts +2 -0
  12. package/dist/compatibility/index.js +42 -0
  13. package/dist/definition/index.cjs +10 -2
  14. package/dist/definition/index.d.cts +3 -21
  15. package/dist/definition/index.d.ts +3 -21
  16. package/dist/definition/index.js +2 -2
  17. package/dist/definition-BH_LzXGc.js +1115 -0
  18. package/dist/definition-BLLBpLKv.cjs +1187 -0
  19. package/dist/index-1sDuqu05.d.ts +373 -0
  20. package/dist/index-BFSMT_2d.d.ts +53 -0
  21. package/dist/index-BGz7L_Fq.d.cts +73 -0
  22. package/dist/index-BTtdqi_M.d.ts +418 -0
  23. package/dist/index-BfbClf6R.d.cts +20 -0
  24. package/dist/index-CK7Is7vy.d.cts +420 -0
  25. package/dist/index-CUGhtuwA.d.cts +53 -0
  26. package/dist/index-CV8EZQy8.d.ts +92 -0
  27. package/dist/index-CzNxjqVg.d.cts +47 -0
  28. package/dist/index-DGHMUZ5m.d.cts +92 -0
  29. package/dist/{index-eK2Xgrwp.d.ts → index-DxvfPmw6.d.ts} +13 -9
  30. package/dist/index-T6nFhvGQ.d.ts +20 -0
  31. package/dist/{index-TACcK_mi.d.cts → index-ba_52Yu9.d.cts} +13 -9
  32. package/dist/index-hrcM9oJe.d.ts +47 -0
  33. package/dist/index-i7Au-FQ6.d.cts +373 -0
  34. package/dist/index-mu1znI1F.d.ts +73 -0
  35. package/dist/index.cjs +70 -58
  36. package/dist/index.d.cts +9 -7
  37. package/dist/index.d.ts +9 -7
  38. package/dist/index.js +10 -6
  39. package/dist/integrations/json-schema/index.cjs +328 -0
  40. package/dist/integrations/json-schema/index.d.cts +53 -0
  41. package/dist/integrations/json-schema/index.d.ts +53 -0
  42. package/dist/integrations/json-schema/index.js +324 -0
  43. package/dist/integrations/standard-schema/index.cjs +32 -0
  44. package/dist/integrations/standard-schema/index.d.cts +22 -0
  45. package/dist/integrations/standard-schema/index.d.ts +22 -0
  46. package/dist/integrations/standard-schema/index.js +32 -0
  47. package/dist/payload/index.cjs +45 -82
  48. package/dist/payload/index.d.cts +2 -56
  49. package/dist/payload/index.d.ts +2 -56
  50. package/dist/payload/index.js +45 -81
  51. package/dist/relations/index.cjs +3 -4
  52. package/dist/relations/index.d.cts +2 -2
  53. package/dist/relations/index.d.ts +2 -2
  54. package/dist/relations/index.js +2 -2
  55. package/dist/{relations-CMz_0nf4.cjs → relations-NUVJkAG8.cjs} +143 -211
  56. package/dist/{relations-By0bK3UK.js → relations-QtnCRW9x.js} +140 -202
  57. package/dist/resolve/index.cjs +308 -0
  58. package/dist/resolve/index.d.cts +2 -0
  59. package/dist/resolve/index.d.ts +2 -0
  60. package/dist/resolve/index.js +305 -0
  61. package/dist/schema-ref-key-BHVFVfj1.cjs +26 -0
  62. package/dist/schema-ref-key-DR__tPr-.js +14 -0
  63. package/dist/spec/index.cjs +9 -0
  64. package/dist/spec/index.d.cts +2 -2
  65. package/dist/spec/index.d.ts +2 -2
  66. package/dist/spec/index.js +3 -1
  67. package/dist/standard/index.cjs +810 -0
  68. package/dist/standard/index.d.cts +3 -0
  69. package/dist/standard/index.d.ts +3 -0
  70. package/dist/standard/index.js +777 -0
  71. package/dist/standard-BGMgbbEQ.js +471 -0
  72. package/dist/standard-etPFZDJI.cjs +513 -0
  73. package/dist/type-interpreter/index.cjs +3 -7
  74. package/dist/type-interpreter/index.d.cts +2 -99
  75. package/dist/type-interpreter/index.d.ts +2 -99
  76. package/dist/type-interpreter/index.js +2 -2
  77. package/dist/{type-interpreter-Ck0VDhzn.js → type-interpreter-BA8gcLUU.js} +58 -560
  78. package/dist/{type-interpreter-DAIBUqKf.cjs → type-interpreter-D8xETgq1.cjs} +63 -589
  79. package/package.json +60 -5
  80. package/dist/definition-BXj3T94L.js +0 -467
  81. package/dist/definition-DSZZTz4e.cjs +0 -473
  82. package/dist/index-CEzHfht2.d.ts +0 -269
  83. package/dist/index-rzkFjgrt.d.cts +0 -269
@@ -1,4 +1,5 @@
1
- import { t as validateSchemaNodeDefinition } from "./definition-BXj3T94L.js";
1
+ import { f as validateSchemaNodeDefinition, l as validateOperationDefinition } from "./definition-BH_LzXGc.js";
2
+ import { n as schemaRefKey, t as indexSchemaRefs } from "./schema-ref-key-DR__tPr-.js";
2
3
 
3
4
  //#region src/relations/validator.ts
4
5
  function equalLiteral(left, right) {
@@ -17,11 +18,13 @@ var RelationChecker = class {
17
18
  activePairs = /* @__PURE__ */ new Set();
18
19
  activeRefs = /* @__PURE__ */ new Set();
19
20
  ids = /* @__PURE__ */ new WeakMap();
21
+ refIndex;
20
22
  nextId = 1;
21
23
  constructor(context, rootSourceSchema, rootTargetSchema) {
22
24
  this.context = context;
23
25
  this.rootSourceSchema = rootSourceSchema;
24
26
  this.rootTargetSchema = rootTargetSchema;
27
+ this.refIndex = indexSchemaRefs(context.refs);
25
28
  }
26
29
  run() {
27
30
  const result = this.compare(this.rootSourceSchema, this.rootTargetSchema, [], this.rootSourceSchema, this.rootTargetSchema);
@@ -251,8 +254,14 @@ var RelationChecker = class {
251
254
  }
252
255
  }
253
256
  compareRef(ref, other, path, rootSource, rootTarget, side) {
254
- if (!this.context.resolveRef) return this.unknown(path, "ref.resolver", "refPath relation requires an explicit resolver");
255
- const key = `${side}:${ref.refPath.join("\0")}`;
257
+ const materialized = this.refIndex.get(schemaRefKey(ref.refPath));
258
+ if (materialized !== void 0) {
259
+ const definition$1 = validateSchemaNodeDefinition(materialized);
260
+ if (!definition$1.ok) return this.rejected(path, "ref.schema", "resolved ref environment contains an invalid schema");
261
+ return side === "source" ? this.compare(definition$1.node, other, path, rootSource, rootTarget) : this.compare(other, definition$1.node, path, rootSource, rootTarget);
262
+ }
263
+ if (!this.context.resolveRef) return this.context.refs === void 0 ? this.unknown(path, "ref.resolver", "refPath relation requires an explicit resolver") : this.unknown(path, "ref.unresolved", "refPath is not present in the resolved environment");
264
+ const key = `${side}:${schemaRefKey(ref.refPath)}`;
256
265
  if (this.activeRefs.has(key)) return this.unknown(path, "ref.cycle", "refPath relation cycle could not be determined");
257
266
  this.activeRefs.add(key);
258
267
  let resolved;
@@ -349,10 +358,10 @@ function validateSchema(schema, side) {
349
358
  if (definition.ok) return definition.node;
350
359
  return {
351
360
  status: "rejected",
352
- issues: definition.issues.map((issue) => ({
353
- code: `relation.rejected.schema.${side}.${issue.code}`,
354
- path: issue.path,
355
- message: issue.message
361
+ issues: definition.issues.map((issue$1) => ({
362
+ code: `relation.rejected.schema.${side}.${issue$1.code}`,
363
+ path: issue$1.path,
364
+ message: issue$1.message
356
365
  }))
357
366
  };
358
367
  }
@@ -364,210 +373,139 @@ function checkSchemaRelation(source, target, context = {}) {
364
373
  return new RelationChecker(context, validSource, validTarget).run();
365
374
  }
366
375
  const checkSchemaAssignability = checkSchemaRelation;
367
- async function resolveAsyncSchema(schema, context, active, path, side, rootSourceSchema, rootTargetSchema) {
368
- if (schema.kind === "ref" && context.resolveRef) {
369
- const key = schema.refPath.join("\0");
370
- if (active.has(key)) return schema;
371
- active.add(key);
372
- let resolved;
373
- try {
374
- resolved = await context.resolveRef(schema.refPath, {
375
- path,
376
- side,
377
- sourceSchema: side === "source" ? schema : rootSourceSchema,
378
- targetSchema: side === "source" ? rootTargetSchema : schema,
379
- rootSourceSchema,
380
- rootTargetSchema,
381
- refPath: schema.refPath
382
- });
383
- } catch {
384
- return schema;
385
- } finally {
386
- active.delete(key);
387
- }
388
- if (resolved && !(typeof resolved === "object" && "status" in resolved && resolved.status !== "resolved")) {
389
- const definition = validateSchemaNodeDefinition(typeof resolved === "object" && "status" in resolved ? resolved.schema : resolved);
390
- if (definition.ok) return resolveAsyncSchema(definition.node, context, new Set(active), path, side, rootSourceSchema, rootTargetSchema);
391
- }
392
- return schema;
393
- }
394
- switch (schema.kind) {
395
- case "record": return {
396
- ...schema,
397
- fields: await Promise.all(schema.fields.map(async (field) => ({
398
- ...field,
399
- schema: await resolveAsyncSchema(field.schema, context, new Set(active), [
400
- ...path,
401
- "fields",
402
- field.key
403
- ], side, rootSourceSchema, rootTargetSchema)
404
- }))),
405
- ...schema.additionalFields && schema.additionalFields !== true ? { additionalFields: await resolveAsyncSchema(schema.additionalFields, context, new Set(active), [...path, "additionalFields"], side, rootSourceSchema, rootTargetSchema) } : {}
406
- };
407
- case "array": return {
408
- ...schema,
409
- elementSchema: await resolveAsyncSchema(schema.elementSchema, context, new Set(active), [...path, "elementSchema"], side, rootSourceSchema, rootTargetSchema)
410
- };
411
- case "tuple": return {
412
- ...schema,
413
- elementSchemas: await Promise.all(schema.elementSchemas.map((item, index) => resolveAsyncSchema(item, context, new Set(active), [
414
- ...path,
415
- "elementSchemas",
416
- index
417
- ], side, rootSourceSchema, rootTargetSchema)))
418
- };
419
- case "nullable": return {
420
- ...schema,
421
- value: await resolveAsyncSchema(schema.value, context, new Set(active), path, side, rootSourceSchema, rootTargetSchema)
422
- };
423
- case "taggedUnion": return {
424
- ...schema,
425
- variants: await Promise.all(schema.variants.map(async (variant) => ({
426
- ...variant,
427
- schema: await resolveAsyncSchema(variant.schema, context, new Set(active), [
428
- ...path,
429
- "variants",
430
- variant.key
431
- ], side, rootSourceSchema, rootTargetSchema)
432
- })))
433
- };
434
- default: return schema;
376
+
377
+ //#endregion
378
+ //#region src/relations/operation.ts
379
+ function issue(status, path, code, message, component, direction) {
380
+ return { result: {
381
+ status,
382
+ issues: [{
383
+ code: `operation.relation.${code}`,
384
+ path,
385
+ message,
386
+ ...component === void 0 ? {} : { details: {
387
+ component,
388
+ ...direction === void 0 ? {} : { direction }
389
+ } }
390
+ }]
391
+ } };
392
+ }
393
+ function payload(source, target, path) {
394
+ const component = path[0] === "input" ? "input" : path[0] === "output" ? "output" : "emission";
395
+ const direction = component === "input" ? "target-to-source" : "source-to-target";
396
+ if (source === void 0 && target === void 0) return [];
397
+ if (source === void 0 || target === void 0) return [issue("incompatible", path, "presence", "A value and absence are distinct contracts.", component, direction)];
398
+ if (source.kind !== target.kind) return [issue("incompatible", path, "payload-kind", "Structured and binary payloads are distinct.", component, direction)];
399
+ if (source.kind === "structured" && target.kind === "structured") return [{
400
+ source: source.schema,
401
+ target: target.schema,
402
+ path: [...path, "schema"]
403
+ }];
404
+ if (source.kind === "binary" && target.kind === "binary") {
405
+ if (target.meta === void 0 || source.meta?.value === target.meta.value) return [];
406
+ return [issue("unknown", [...path, "meta"], "binary-metadata", "The declared metadata does not prove the target MIME requirement.", component, direction)];
435
407
  }
408
+ return [];
436
409
  }
437
- function collectRelationPairs(source, target, path = [], result = [], seen = /* @__PURE__ */ new Set()) {
438
- const key = `${JSON.stringify(path)}:${JSON.stringify(source)}:${JSON.stringify(target)}`;
439
- if (seen.has(key)) return result;
440
- seen.add(key);
441
- result.push({
442
- source,
443
- target,
444
- path
445
- });
446
- if (source.kind === "record" && target.kind === "record") {
447
- const targetFields = new Map(target.fields.map((field) => [field.key, field]));
448
- for (const field of source.fields) {
449
- const targetField = targetFields.get(field.key);
450
- if (targetField) collectRelationPairs(field.schema, targetField.schema, [
451
- ...path,
452
- "fields",
453
- field.key
454
- ], result, seen);
410
+ function errors(source, target, path) {
411
+ const comparisons$1 = [];
412
+ for (const [code, error] of Object.entries(source ?? {})) {
413
+ const at = [...path, code];
414
+ if (!target || !Object.hasOwn(target, code)) {
415
+ comparisons$1.push(issue("incompatible", at, "error-code", "The source may produce an error not declared by the target.", "error", "source-to-target"));
416
+ continue;
455
417
  }
456
- } else if (source.kind === "array" && target.kind === "array") collectRelationPairs(source.elementSchema, target.elementSchema, [...path, "elementSchema"], result, seen);
457
- else if (source.kind === "tuple" && target.kind === "tuple") for (let index = 0; index < Math.min(source.elementSchemas.length, target.elementSchemas.length); index += 1) collectRelationPairs(source.elementSchemas[index], target.elementSchemas[index], [
458
- ...path,
459
- "elementSchemas",
460
- index
461
- ], result, seen);
462
- else if (source.kind === "nullable" && target.kind === "nullable") collectRelationPairs(source.value, target.value, path, result, seen);
463
- else if (source.kind === "taggedUnion" && target.kind === "taggedUnion" && source.discriminator === target.discriminator) {
464
- const targetVariants = new Map(target.variants.map((variant) => [variant.key, variant]));
465
- for (const variant of source.variants) {
466
- const targetVariant = targetVariants.get(variant.key);
467
- if (targetVariant) collectRelationPairs(variant.schema, targetVariant.schema, [
468
- ...path,
469
- "variants",
470
- variant.key
471
- ], result, seen);
418
+ const expected = target[code];
419
+ if (error.data !== void 0 && expected.data !== void 0) comparisons$1.push({
420
+ source: error.data,
421
+ target: expected.data,
422
+ path: [...at, "data"]
423
+ });
424
+ else if (error.data !== expected.data) comparisons$1.push(issue("incompatible", [...at, "data"], "presence", "Error details and absence are distinct contracts.", "error", "source-to-target"));
425
+ }
426
+ return comparisons$1;
427
+ }
428
+ function comparisons(source, target) {
429
+ const result = [
430
+ ...payload(target.input, source.input, ["input"]),
431
+ ...payload(source.output, target.output, ["output"]),
432
+ ...errors(source.errors, target.errors, ["errors"])
433
+ ];
434
+ for (const [name, channel] of Object.entries(source.emits ?? {})) {
435
+ const path = ["emits", name];
436
+ if (!target.emits || !Object.hasOwn(target.emits, name)) {
437
+ result.push(issue("incompatible", path, "channel", "The source may emit on a channel not declared by the target.", "emission", "source-to-target"));
438
+ continue;
472
439
  }
440
+ const expected = target.emits[name];
441
+ result.push(...payload(channel.data, expected.data, [...path, "data"]));
442
+ result.push(...errors(channel.errors, expected.errors, [...path, "errors"]));
473
443
  }
474
444
  return result;
475
445
  }
476
- async function checkSchemaRelationAsync(source, target, context = {}) {
477
- try {
478
- const validSource = validateSchema(source, "source");
479
- if ("status" in validSource) return validSource;
480
- const validTarget = validateSchema(target, "target");
481
- if ("status" in validTarget) return validTarget;
482
- const resolvedSource = await resolveAsyncSchema(validSource, context, /* @__PURE__ */ new Set(), [], "source", validSource, validTarget);
483
- const resolvedTarget = await resolveAsyncSchema(validTarget, context, /* @__PURE__ */ new Set(), [], "target", validSource, validTarget);
484
- const pairs = collectRelationPairs(resolvedSource, resolvedTarget);
485
- const semanticCache = /* @__PURE__ */ new Map();
486
- const constraintCache = /* @__PURE__ */ new Map();
487
- if (context.semanticProvider) for (const pair of pairs) {
488
- const { source: left, target: right } = pair;
489
- const sourceSemantic = left.kind === "primitive" && (left.name === "string" || left.name === "number") ? left.semanticPath : void 0;
490
- const targetSemantic = right.kind === "primitive" && (right.name === "string" || right.name === "number") ? right.semanticPath : void 0;
491
- if (right.kind !== "primitive" || right.name !== "string" && right.name !== "number" || sourceSemantic === void 0 && targetSemantic === void 0) continue;
492
- const providerContext = {
493
- path: pair.path,
494
- side: "source",
495
- sourceSchema: left,
496
- targetSchema: right,
497
- rootSourceSchema: resolvedSource,
498
- rootTargetSchema: resolvedTarget
499
- };
500
- let decision;
501
- try {
502
- decision = await context.semanticProvider(sourceSemantic, targetSemantic, providerContext);
503
- } catch (error) {
504
- decision = {
505
- status: "unknown",
506
- message: `semantic provider failed: ${error instanceof Error ? error.message : String(error)}`
507
- };
446
+ function prepare(source, target) {
447
+ const left = validateOperationDefinition(source);
448
+ const right = validateOperationDefinition(target);
449
+ if (!left.ok || !right.ok) return {
450
+ status: "rejected",
451
+ issues: [...!left.ok ? left.issues.map((i) => ({
452
+ ...i,
453
+ path: ["source", ...i.path],
454
+ code: `operation.relation.rejected.${i.code}`,
455
+ details: {
456
+ component: "definition",
457
+ side: "source"
508
458
  }
509
- semanticCache.set(JSON.stringify([
510
- pair.path,
511
- sourceSemantic,
512
- targetSemantic
513
- ]), decision);
514
- }
515
- if (context.constraintProvider) for (const pair of pairs) {
516
- const { source: left, target: right } = pair;
517
- if (left.kind === "primitive" || left.kind === "array" || left.kind === "record" || left.kind === "literal" || left.kind === "literalUnion") {
518
- const literalToPrimitive = (left.kind === "literal" || left.kind === "literalUnion") && right.kind === "primitive";
519
- if (right.kind !== left.kind && !literalToPrimitive) continue;
520
- const sourceConstraints = "constraints" in left ? left.constraints : void 0;
521
- const targetConstraints = "constraints" in right ? right.constraints : void 0;
522
- if (sourceConstraints === void 0 && targetConstraints === void 0) continue;
523
- const providerContext = {
524
- path: pair.path,
525
- side: "source",
526
- sourceSchema: left,
527
- targetSchema: right,
528
- rootSourceSchema: resolvedSource,
529
- rootTargetSchema: resolvedTarget
530
- };
531
- let decision;
532
- try {
533
- decision = await context.constraintProvider(sourceConstraints, targetConstraints, providerContext);
534
- } catch (error) {
535
- decision = {
536
- status: "unknown",
537
- message: `constraint provider failed: ${error instanceof Error ? error.message : String(error)}`
538
- };
539
- }
540
- constraintCache.set(JSON.stringify([
541
- pair.path,
542
- sourceConstraints,
543
- targetConstraints
544
- ]), decision);
459
+ })) : [], ...!right.ok ? right.issues.map((i) => ({
460
+ ...i,
461
+ path: ["target", ...i.path],
462
+ code: `operation.relation.rejected.${i.code}`,
463
+ details: {
464
+ component: "definition",
465
+ side: "target"
545
466
  }
546
- }
547
- return checkSchemaRelation(resolvedSource, resolvedTarget, {
548
- semanticProvider: (sourcePath, targetPath, providerContext) => semanticCache.get(JSON.stringify([
549
- providerContext.path,
550
- sourcePath,
551
- targetPath
552
- ])) ?? { status: "unknown" },
553
- constraintProvider: (sourceConstraints, targetConstraints, providerContext) => constraintCache.get(JSON.stringify([
554
- providerContext.path,
555
- sourceConstraints,
556
- targetConstraints
557
- ])) ?? { status: "unknown" }
558
- });
559
- } catch (error) {
560
- return {
561
- status: "unknown",
562
- issues: [{
563
- code: "relation.unknown.async",
564
- path: [],
565
- message: `async relation provider failed: ${error instanceof Error ? error.message : String(error)}`
566
- }]
567
- };
568
- }
467
+ })) : []]
468
+ };
469
+ return comparisons(left.operation, right.operation);
470
+ }
471
+ const priority = {
472
+ assignable: 0,
473
+ unknown: 1,
474
+ incompatible: 2,
475
+ rejected: 3
476
+ };
477
+ function combine(results) {
478
+ let status = "assignable";
479
+ for (const result of results) if (priority[result.status] > priority[status]) status = result.status;
480
+ return {
481
+ status,
482
+ issues: results.flatMap((result) => result.issues)
483
+ };
484
+ }
485
+ function prefix(result, path) {
486
+ const component = path[0] === "input" ? "input" : path[0] === "output" ? "output" : path[0] === "errors" ? "error" : path[0] === "emits" ? "emission" : "definition";
487
+ const direction = component === "input" ? "target-to-source" : "source-to-target";
488
+ return {
489
+ status: result.status,
490
+ issues: result.issues.map((issue$1) => ({
491
+ ...issue$1,
492
+ path: [...path, ...issue$1.path],
493
+ details: {
494
+ ...issue$1.details ?? {},
495
+ component,
496
+ direction
497
+ }
498
+ }))
499
+ };
500
+ }
501
+ /** Can source replace target for callers using the target contract?
502
+ * Provider contexts describe each directed schema comparison; input reverses sides.
503
+ */
504
+ function checkOperationRelation(source, target, context = {}) {
505
+ const work = prepare(source, target);
506
+ if (!Array.isArray(work)) return work;
507
+ return combine(work.map((item) => "result" in item ? item.result : prefix(checkSchemaRelation(item.source, item.target, context), item.path)));
569
508
  }
570
- const checkSchemaAssignabilityAsync = checkSchemaRelationAsync;
571
509
 
572
510
  //#endregion
573
- export { checkSchemaRelationAsync as i, checkSchemaAssignabilityAsync as n, checkSchemaRelation as r, checkSchemaAssignability as t };
511
+ export { checkSchemaAssignability as n, checkSchemaRelation as r, checkOperationRelation as t };