@jasonbelmonti/markdown-engine 3.0.0 → 3.1.1

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/CHANGELOG.md +28 -0
  2. package/README.md +77 -8
  3. package/dist/api/contracts.d.ts +2 -1
  4. package/dist/api/contracts.d.ts.map +1 -1
  5. package/dist/api/contracts.js +1 -0
  6. package/dist/api/contracts.js.map +1 -1
  7. package/dist/api/declarative-validation.d.ts +1 -1
  8. package/dist/api/declarative-validation.d.ts.map +1 -1
  9. package/dist/api/declarative-validation.js.map +1 -1
  10. package/dist/api/document-set-validation-types.d.ts +32 -0
  11. package/dist/api/document-set-validation-types.d.ts.map +1 -0
  12. package/dist/api/document-set-validation-types.js +2 -0
  13. package/dist/api/document-set-validation-types.js.map +1 -0
  14. package/dist/api/document-set-validation.d.ts +4 -0
  15. package/dist/api/document-set-validation.d.ts.map +1 -0
  16. package/dist/api/document-set-validation.js +60 -0
  17. package/dist/api/document-set-validation.js.map +1 -0
  18. package/dist/api/serialize.d.ts +2 -1
  19. package/dist/api/serialize.d.ts.map +1 -1
  20. package/dist/api/serialize.js.map +1 -1
  21. package/dist/declarative-validation/assertions/evaluator.d.ts.map +1 -1
  22. package/dist/declarative-validation/assertions/evaluator.js +6 -0
  23. package/dist/declarative-validation/assertions/evaluator.js.map +1 -1
  24. package/dist/declarative-validation/assertions/frontmatter-shape.d.ts +9 -0
  25. package/dist/declarative-validation/assertions/frontmatter-shape.d.ts.map +1 -0
  26. package/dist/declarative-validation/assertions/frontmatter-shape.js +105 -0
  27. package/dist/declarative-validation/assertions/frontmatter-shape.js.map +1 -0
  28. package/dist/declarative-validation/assertions/text-format.d.ts +9 -0
  29. package/dist/declarative-validation/assertions/text-format.d.ts.map +1 -0
  30. package/dist/declarative-validation/assertions/text-format.js +75 -0
  31. package/dist/declarative-validation/assertions/text-format.js.map +1 -0
  32. package/dist/declarative-validation/compiler/assertion-builders.d.ts.map +1 -1
  33. package/dist/declarative-validation/compiler/assertion-builders.js +38 -1
  34. package/dist/declarative-validation/compiler/assertion-builders.js.map +1 -1
  35. package/dist/declarative-validation/compiler/assertion-shapes.d.ts +3 -1
  36. package/dist/declarative-validation/compiler/assertion-shapes.d.ts.map +1 -1
  37. package/dist/declarative-validation/compiler/assertion-shapes.js +30 -0
  38. package/dist/declarative-validation/compiler/assertion-shapes.js.map +1 -1
  39. package/dist/declarative-validation/compiler/assertions.d.ts.map +1 -1
  40. package/dist/declarative-validation/compiler/assertions.js +2 -0
  41. package/dist/declarative-validation/compiler/assertions.js.map +1 -1
  42. package/dist/declarative-validation/compiler/compatibility.d.ts.map +1 -1
  43. package/dist/declarative-validation/compiler/compatibility.js +2 -0
  44. package/dist/declarative-validation/compiler/compatibility.js.map +1 -1
  45. package/dist/declarative-validation/compiler/plan.d.ts +8 -1
  46. package/dist/declarative-validation/compiler/plan.d.ts.map +1 -1
  47. package/dist/declarative-validation/evidence/index.d.ts.map +1 -1
  48. package/dist/declarative-validation/evidence/index.js +2 -1
  49. package/dist/declarative-validation/evidence/index.js.map +1 -1
  50. package/dist/declarative-validation/profile/assertion-schema.d.ts.map +1 -1
  51. package/dist/declarative-validation/profile/assertion-schema.js +48 -3
  52. package/dist/declarative-validation/profile/assertion-schema.js.map +1 -1
  53. package/dist/declarative-validation/profile/frontmatter-shape-schema.d.ts +8 -0
  54. package/dist/declarative-validation/profile/frontmatter-shape-schema.d.ts.map +1 -0
  55. package/dist/declarative-validation/profile/frontmatter-shape-schema.js +178 -0
  56. package/dist/declarative-validation/profile/frontmatter-shape-schema.js.map +1 -0
  57. package/dist/declarative-validation/profile/index.d.ts +21 -0
  58. package/dist/declarative-validation/profile/index.d.ts.map +1 -1
  59. package/dist/declarative-validation/profile/text-format-contract.d.ts +4 -0
  60. package/dist/declarative-validation/profile/text-format-contract.d.ts.map +1 -0
  61. package/dist/declarative-validation/profile/text-format-contract.js +6 -0
  62. package/dist/declarative-validation/profile/text-format-contract.js.map +1 -0
  63. package/dist/internal/package-version.d.ts +2 -0
  64. package/dist/internal/package-version.d.ts.map +1 -0
  65. package/dist/internal/package-version.js +2 -0
  66. package/dist/internal/package-version.js.map +1 -0
  67. package/dist-bundled/markdown-engine-cli.mjs +556 -6
  68. package/docs/contracts/api.md +60 -0
  69. package/docs/contracts/declarative-validation.md +161 -22
  70. package/fixtures/declarative-validation/examples/okf-v0.1/fail/invalid-log-date/log.md +5 -0
  71. package/fixtures/declarative-validation/examples/okf-v0.1/fail/missing-concept-type/concepts/customer-metric.md +8 -0
  72. package/fixtures/declarative-validation/examples/okf-v0.1/fail/non-root-index-frontmatter/datasets/index.md +7 -0
  73. package/fixtures/declarative-validation/examples/okf-v0.1/pass/datasets/index.md +3 -0
  74. package/fixtures/declarative-validation/examples/okf-v0.1/pass/datasets/sales.md +16 -0
  75. package/fixtures/declarative-validation/examples/okf-v0.1/pass/index.md +8 -0
  76. package/fixtures/declarative-validation/examples/okf-v0.1/pass/log.md +9 -0
  77. package/fixtures/declarative-validation/examples/okf-v0.1/pass/playbooks/incident-response.md +17 -0
  78. package/fixtures/declarative-validation/examples/okf-v0.1/profiles/concept.yaml +14 -0
  79. package/fixtures/declarative-validation/examples/okf-v0.1/profiles/log.yaml +10 -0
  80. package/fixtures/declarative-validation/examples/okf-v0.1/profiles/non-root-index.yaml +9 -0
  81. package/fixtures/declarative-validation/examples/okf-v0.1/profiles/root-index.yaml +12 -0
  82. package/package.json +3 -2
  83. package/scripts/install-markdown-engine-cli.sh +113 -0
@@ -7715,6 +7715,134 @@ function hasOwnProperty(object, property) {
7715
7715
  return Object.prototype.hasOwnProperty.call(object, property);
7716
7716
  }
7717
7717
 
7718
+ // src/declarative-validation/assertions/frontmatter-shape.ts
7719
+ function evaluateFrontmatterShape(assertion, context) {
7720
+ const frontmatter = context.selection.document.frontmatter;
7721
+ const diagnostics = [];
7722
+ if (assertion.presence === "required" && frontmatter === void 0) {
7723
+ diagnostics.push(requiredFrontmatterDiagnostic(context));
7724
+ }
7725
+ if (assertion.presence === "forbidden" && frontmatter !== void 0) {
7726
+ diagnostics.push(forbiddenFrontmatterDiagnostic(context));
7727
+ }
7728
+ if (assertion.fields === void 0) {
7729
+ return diagnostics;
7730
+ }
7731
+ if (!isPlainRecord(frontmatter)) {
7732
+ for (const [fieldOrder, field] of assertion.fields.entries()) {
7733
+ if (field.required === true) {
7734
+ diagnostics.push(missingFieldDiagnostic(field, context, fieldOrder));
7735
+ }
7736
+ }
7737
+ return diagnostics;
7738
+ }
7739
+ for (const [fieldOrder, field] of assertion.fields.entries()) {
7740
+ diagnostics.push(
7741
+ ...evaluateFrontmatterField(frontmatter, field, context, fieldOrder)
7742
+ );
7743
+ }
7744
+ return diagnostics;
7745
+ }
7746
+ function evaluateFrontmatterField(frontmatter, field, context, fieldOrder) {
7747
+ if (!hasOwnProperty(frontmatter, field.field)) {
7748
+ return field.required === true ? [missingFieldDiagnostic(field, context, fieldOrder)] : [];
7749
+ }
7750
+ const value = frontmatter[field.field];
7751
+ const diagnostics = [];
7752
+ if (field.valueType !== void 0 && !matchesFrontmatterValueType(value, field.valueType)) {
7753
+ diagnostics.push(typeMismatchDiagnostic(field, context, fieldOrder));
7754
+ }
7755
+ if (field.nonEmpty === true && failsNonEmptyStringPredicate(value, field)) {
7756
+ diagnostics.push(emptyFieldDiagnostic(field, context, fieldOrder));
7757
+ }
7758
+ return diagnostics;
7759
+ }
7760
+ function requiredFrontmatterDiagnostic(context) {
7761
+ return validationDiagnostic(
7762
+ "profile.validation.frontmatterMissing",
7763
+ "Frontmatter is required.",
7764
+ context.rule,
7765
+ {
7766
+ assertionIndex: context.assertionIndex,
7767
+ targetKey: "frontmatter:presence:required",
7768
+ diagnosticOrder: 0
7769
+ }
7770
+ );
7771
+ }
7772
+ function forbiddenFrontmatterDiagnostic(context) {
7773
+ return validationDiagnostic(
7774
+ "profile.validation.frontmatterForbidden",
7775
+ "Frontmatter is forbidden.",
7776
+ context.rule,
7777
+ {
7778
+ assertionIndex: context.assertionIndex,
7779
+ targetKey: "frontmatter:presence:forbidden",
7780
+ diagnosticOrder: 0
7781
+ }
7782
+ );
7783
+ }
7784
+ function missingFieldDiagnostic(field, context, fieldOrder) {
7785
+ return validationDiagnostic(
7786
+ "profile.validation.frontmatterFieldMissing",
7787
+ `Required frontmatter field "${field.field}" is missing.`,
7788
+ context.rule,
7789
+ {
7790
+ assertionIndex: context.assertionIndex,
7791
+ targetKey: frontmatterFieldTargetKey(field, "missing"),
7792
+ diagnosticOrder: fieldOrder
7793
+ }
7794
+ );
7795
+ }
7796
+ function typeMismatchDiagnostic(field, context, fieldOrder) {
7797
+ return validationDiagnostic(
7798
+ "profile.validation.frontmatterFieldTypeMismatch",
7799
+ `Frontmatter field "${field.field}" must be ${field.valueType}.`,
7800
+ context.rule,
7801
+ {
7802
+ assertionIndex: context.assertionIndex,
7803
+ targetKey: frontmatterFieldTargetKey(field, "type"),
7804
+ diagnosticOrder: fieldOrder
7805
+ }
7806
+ );
7807
+ }
7808
+ function emptyFieldDiagnostic(field, context, fieldOrder) {
7809
+ return validationDiagnostic(
7810
+ "profile.validation.frontmatterFieldEmpty",
7811
+ `Frontmatter field "${field.field}" must be a non-empty string.`,
7812
+ context.rule,
7813
+ {
7814
+ assertionIndex: context.assertionIndex,
7815
+ targetKey: frontmatterFieldTargetKey(field, "nonEmpty"),
7816
+ diagnosticOrder: fieldOrder
7817
+ }
7818
+ );
7819
+ }
7820
+ function frontmatterFieldTargetKey(field, predicate) {
7821
+ return `frontmatter:${field.field}:${predicate}`;
7822
+ }
7823
+ function matchesFrontmatterValueType(value, valueType) {
7824
+ switch (valueType) {
7825
+ case "string":
7826
+ return typeof value === "string";
7827
+ case "number":
7828
+ return typeof value === "number" && Number.isFinite(value);
7829
+ case "boolean":
7830
+ return typeof value === "boolean";
7831
+ case "array":
7832
+ return Array.isArray(value);
7833
+ case "object":
7834
+ return isPlainRecord(value);
7835
+ case "null":
7836
+ return value === null;
7837
+ }
7838
+ }
7839
+ function failsNonEmptyStringPredicate(value, field) {
7840
+ if (field.valueType === "string" && typeof value !== "string") {
7841
+ return false;
7842
+ }
7843
+ return typeof value !== "string" || value.length === 0;
7844
+ }
7845
+
7718
7846
  // src/declarative-validation/assertions/frontmatter-required.ts
7719
7847
  function evaluateFrontmatterRequired(assertion, context) {
7720
7848
  return assertion.fields.filter(
@@ -9572,6 +9700,86 @@ function excludedTextDiagnostics(assertion, context, target, targetOrder) {
9572
9700
  );
9573
9701
  }
9574
9702
 
9703
+ // src/declarative-validation/assertions/text-format.ts
9704
+ function evaluateTextFormat(assertion, context) {
9705
+ if (context.selection.targets.length === 0) {
9706
+ return [emptySelectionDiagnostic(context.rule, context.assertionIndex)];
9707
+ }
9708
+ switch (assertion.format) {
9709
+ case "isoDate":
9710
+ return context.selection.targets.flatMap(
9711
+ (target, targetOrder) => evaluateIsoDateTarget(context, target, targetOrder)
9712
+ );
9713
+ }
9714
+ }
9715
+ function evaluateIsoDateTarget(context, target, targetOrder) {
9716
+ if (isIsoDateText(target.text)) {
9717
+ return [];
9718
+ }
9719
+ return [
9720
+ validationDiagnostic(
9721
+ "profile.validation.assertionFailed",
9722
+ `Selected ${target.kind} text must be an exact ISO date in YYYY-MM-DD form.`,
9723
+ context.rule,
9724
+ {
9725
+ assertionIndex: context.assertionIndex,
9726
+ target,
9727
+ targetOrder
9728
+ }
9729
+ )
9730
+ ];
9731
+ }
9732
+ function isIsoDateText(text5) {
9733
+ if (text5.length !== 10 || text5[4] !== "-" || text5[7] !== "-") {
9734
+ return false;
9735
+ }
9736
+ const year = decimalValue(text5, 0, 4);
9737
+ const month = decimalValue(text5, 5, 7);
9738
+ const day = decimalValue(text5, 8, 10);
9739
+ if (year === void 0 || month === void 0 || day === void 0) {
9740
+ return false;
9741
+ }
9742
+ const maximumDay = daysInMonth(year, month);
9743
+ return maximumDay !== void 0 && day >= 1 && day <= maximumDay;
9744
+ }
9745
+ function decimalValue(text5, start, end) {
9746
+ const zeroCode = 48;
9747
+ const nineCode = 57;
9748
+ let value = 0;
9749
+ for (let index2 = start; index2 < end; index2 += 1) {
9750
+ const charCode = text5.charCodeAt(index2);
9751
+ if (charCode < zeroCode || charCode > nineCode) {
9752
+ return void 0;
9753
+ }
9754
+ value = value * 10 + (charCode - zeroCode);
9755
+ }
9756
+ return value;
9757
+ }
9758
+ function daysInMonth(year, month) {
9759
+ switch (month) {
9760
+ case 1:
9761
+ case 3:
9762
+ case 5:
9763
+ case 7:
9764
+ case 8:
9765
+ case 10:
9766
+ case 12:
9767
+ return 31;
9768
+ case 4:
9769
+ case 6:
9770
+ case 9:
9771
+ case 11:
9772
+ return 30;
9773
+ case 2:
9774
+ return isLeapYear(year) ? 29 : 28;
9775
+ default:
9776
+ return void 0;
9777
+ }
9778
+ }
9779
+ function isLeapYear(year) {
9780
+ return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
9781
+ }
9782
+
9575
9783
  // src/declarative-validation/assertions/text-length.ts
9576
9784
  function evaluateTextLength(assertion, context) {
9577
9785
  if (context.selection.targets.length === 0) {
@@ -9677,6 +9885,8 @@ function evaluateAssertion(assertion, context) {
9677
9885
  return evaluateTextOccurrenceCount(assertion, context);
9678
9886
  case "textLength":
9679
9887
  return evaluateTextLength(assertion, context);
9888
+ case "textFormat":
9889
+ return evaluateTextFormat(assertion, context);
9680
9890
  case "frontmatterRequired":
9681
9891
  return evaluateFrontmatterRequired(assertion, context);
9682
9892
  case "tableColumnsRequired":
@@ -9687,6 +9897,8 @@ function evaluateAssertion(assertion, context) {
9687
9897
  return evaluateReferences(assertion, context);
9688
9898
  case "tableColumnCoverage":
9689
9899
  return evaluateTableColumnCoverage(assertion, context);
9900
+ case "frontmatterShape":
9901
+ return evaluateFrontmatterShape(assertion, context);
9690
9902
  }
9691
9903
  }
9692
9904
 
@@ -10383,6 +10595,220 @@ function requiredSelectorString(value, field, diagnostics) {
10383
10595
  );
10384
10596
  }
10385
10597
 
10598
+ // src/declarative-validation/profile/text-format-contract.ts
10599
+ var TEXT_FORMAT_ASSERTION_KEYS = ["format"];
10600
+ var TEXT_FORMAT_ISO_DATE = "isoDate";
10601
+ function isTextFormatAssertionFormat(value) {
10602
+ return value === TEXT_FORMAT_ISO_DATE;
10603
+ }
10604
+
10605
+ // src/declarative-validation/profile/frontmatter-shape-schema.ts
10606
+ var FRONTMATTER_PRESENCE_VALUES = /* @__PURE__ */ new Set([
10607
+ "required",
10608
+ "forbidden"
10609
+ ]);
10610
+ var FRONTMATTER_VALUE_TYPES = /* @__PURE__ */ new Set([
10611
+ "string",
10612
+ "number",
10613
+ "boolean",
10614
+ "array",
10615
+ "object",
10616
+ "null"
10617
+ ]);
10618
+ function frontmatterShapeFromValue(value) {
10619
+ const invalidShapes = [];
10620
+ const unsupportedKeys2 = [];
10621
+ const shape = frontmatterShape(value, invalidShapes, unsupportedKeys2);
10622
+ return {
10623
+ ...shape !== void 0 ? { shape } : {},
10624
+ invalidShapes,
10625
+ unsupportedKeys: unsupportedKeys2
10626
+ };
10627
+ }
10628
+ function frontmatterShape(value, invalidShapes, unsupportedKeys2) {
10629
+ if (!isPlainRecord(value)) {
10630
+ invalidShapes.push("frontmatterShape must be an object.");
10631
+ return void 0;
10632
+ }
10633
+ collectUnsupportedKeys(value, ["presence", "fields"], unsupportedKeys2);
10634
+ let valid = true;
10635
+ const presence = frontmatterPresence(value.presence, invalidShapes);
10636
+ if (value.presence !== void 0 && presence === void 0) {
10637
+ valid = false;
10638
+ }
10639
+ const fields = frontmatterFields(value.fields, invalidShapes, unsupportedKeys2);
10640
+ if (value.fields !== void 0 && fields === void 0) {
10641
+ valid = false;
10642
+ }
10643
+ if (value.presence === void 0 && value.fields === void 0) {
10644
+ invalidShapes.push("frontmatterShape must include presence or fields.");
10645
+ valid = false;
10646
+ }
10647
+ if (presence === "forbidden" && fields !== void 0) {
10648
+ invalidShapes.push(
10649
+ "frontmatterShape.fields cannot be provided when presence is forbidden."
10650
+ );
10651
+ valid = false;
10652
+ }
10653
+ return valid ? {
10654
+ ...presence !== void 0 ? { presence } : {},
10655
+ ...fields !== void 0 ? { fields } : {}
10656
+ } : void 0;
10657
+ }
10658
+ function frontmatterPresence(value, invalidShapes) {
10659
+ if (value === void 0) {
10660
+ return void 0;
10661
+ }
10662
+ if (typeof value === "string" && FRONTMATTER_PRESENCE_VALUES.has(value)) {
10663
+ return value;
10664
+ }
10665
+ invalidShapes.push(
10666
+ 'frontmatterShape.presence must be "required" or "forbidden" when provided.'
10667
+ );
10668
+ return void 0;
10669
+ }
10670
+ function frontmatterFields(value, invalidShapes, unsupportedKeys2) {
10671
+ if (value === void 0) {
10672
+ return void 0;
10673
+ }
10674
+ if (!Array.isArray(value) || value.length === 0) {
10675
+ invalidShapes.push(
10676
+ "frontmatterShape.fields must be a non-empty array when provided."
10677
+ );
10678
+ return void 0;
10679
+ }
10680
+ const fields = [];
10681
+ const seenFields = /* @__PURE__ */ new Set();
10682
+ let valid = true;
10683
+ for (let index2 = 0; index2 < value.length; index2 += 1) {
10684
+ const fieldShape = frontmatterField(
10685
+ value[index2],
10686
+ index2,
10687
+ invalidShapes,
10688
+ unsupportedKeys2
10689
+ );
10690
+ if (fieldShape === void 0) {
10691
+ valid = false;
10692
+ continue;
10693
+ }
10694
+ if (seenFields.has(fieldShape.field)) {
10695
+ invalidShapes.push(
10696
+ `frontmatterShape.fields[${index2}].field duplicates field "${fieldShape.field}".`
10697
+ );
10698
+ valid = false;
10699
+ continue;
10700
+ }
10701
+ seenFields.add(fieldShape.field);
10702
+ fields.push(fieldShape);
10703
+ }
10704
+ return valid ? fields : void 0;
10705
+ }
10706
+ function frontmatterField(value, index2, invalidShapes, unsupportedKeys2) {
10707
+ const fieldName = `frontmatterShape.fields[${index2}]`;
10708
+ if (!isPlainRecord(value)) {
10709
+ invalidShapes.push(`${fieldName} must be an object.`);
10710
+ return void 0;
10711
+ }
10712
+ collectUnsupportedKeys(
10713
+ value,
10714
+ ["field", "required", "valueType", "nonEmpty"],
10715
+ unsupportedKeys2
10716
+ );
10717
+ let valid = true;
10718
+ const field = nonEmptyString2(value.field);
10719
+ if (field === void 0) {
10720
+ invalidShapes.push(`${fieldName}.field must be a non-empty string.`);
10721
+ valid = false;
10722
+ }
10723
+ const required = trueConstraint(
10724
+ value.required,
10725
+ `${fieldName}.required`,
10726
+ invalidShapes
10727
+ );
10728
+ if (value.required !== void 0 && required === void 0) {
10729
+ valid = false;
10730
+ }
10731
+ const valueType = frontmatterValueType(
10732
+ value.valueType,
10733
+ `${fieldName}.valueType`,
10734
+ invalidShapes
10735
+ );
10736
+ if (value.valueType !== void 0 && valueType === void 0) {
10737
+ valid = false;
10738
+ }
10739
+ const nonEmpty = trueConstraint(
10740
+ value.nonEmpty,
10741
+ `${fieldName}.nonEmpty`,
10742
+ invalidShapes
10743
+ );
10744
+ if (value.nonEmpty !== void 0 && nonEmpty === void 0) {
10745
+ valid = false;
10746
+ }
10747
+ if (nonEmpty === true && valueType !== void 0 && valueType !== "string") {
10748
+ invalidShapes.push(
10749
+ `${fieldName}.nonEmpty can be combined only with valueType "string".`
10750
+ );
10751
+ valid = false;
10752
+ }
10753
+ const hasPredicateInput = value.required !== void 0 || value.valueType !== void 0 || value.nonEmpty !== void 0;
10754
+ if (!hasPredicateInput) {
10755
+ invalidShapes.push(
10756
+ `${fieldName} must include required, valueType, or nonEmpty.`
10757
+ );
10758
+ valid = false;
10759
+ }
10760
+ if (!valid || field === void 0) {
10761
+ return void 0;
10762
+ }
10763
+ const baseField = {
10764
+ field,
10765
+ ...required === true ? { required: true } : {}
10766
+ };
10767
+ if (nonEmpty === true) {
10768
+ return {
10769
+ ...baseField,
10770
+ ...valueType === "string" ? { valueType } : {},
10771
+ nonEmpty: true
10772
+ };
10773
+ }
10774
+ return {
10775
+ ...baseField,
10776
+ ...valueType !== void 0 ? { valueType } : {}
10777
+ };
10778
+ }
10779
+ function trueConstraint(value, fieldName, invalidShapes) {
10780
+ if (value === void 0) {
10781
+ return void 0;
10782
+ }
10783
+ if (value === true) {
10784
+ return true;
10785
+ }
10786
+ invalidShapes.push(`${fieldName} must be true when provided.`);
10787
+ return void 0;
10788
+ }
10789
+ function frontmatterValueType(value, fieldName, invalidShapes) {
10790
+ if (value === void 0) {
10791
+ return void 0;
10792
+ }
10793
+ if (typeof value === "string" && FRONTMATTER_VALUE_TYPES.has(value)) {
10794
+ return value;
10795
+ }
10796
+ invalidShapes.push(
10797
+ `${fieldName} must be "string", "number", "boolean", "array", "object", or "null" when provided.`
10798
+ );
10799
+ return void 0;
10800
+ }
10801
+ function nonEmptyString2(value) {
10802
+ return typeof value === "string" && value.length > 0 ? value : void 0;
10803
+ }
10804
+ function collectUnsupportedKeys(record, allowedKeys, unsupportedKeys2) {
10805
+ for (const key of Object.keys(record)) {
10806
+ if (!allowedKeys.includes(key)) {
10807
+ unsupportedKeys2.push(key);
10808
+ }
10809
+ }
10810
+ }
10811
+
10386
10812
  // src/declarative-validation/compiler/diagnostics.ts
10387
10813
  function compileDiagnostic(code3, message, ruleId) {
10388
10814
  return {
@@ -10630,6 +11056,46 @@ function closedTableColumnCoverageTarget(value, ruleId, diagnostics) {
10630
11056
  column: target.column
10631
11057
  } : void 0;
10632
11058
  }
11059
+ function closedFrontmatterShape(value, ruleId, diagnostics) {
11060
+ const result = frontmatterShapeFromValue(value);
11061
+ for (const key of result.unsupportedKeys) {
11062
+ diagnostics.push(unsupportedProfileKey(key));
11063
+ }
11064
+ for (const message of result.invalidShapes) {
11065
+ diagnostics.push(
11066
+ compileDiagnostic(
11067
+ "profile.config.invalidShape",
11068
+ message,
11069
+ ruleId
11070
+ )
11071
+ );
11072
+ }
11073
+ return result.shape;
11074
+ }
11075
+ function closedTextFormat(value, ruleId, diagnostics) {
11076
+ const diagnosticCountBefore = diagnostics.length;
11077
+ if (!pushObjectDiagnostic("textFormat", value, ruleId, diagnostics)) {
11078
+ return void 0;
11079
+ }
11080
+ const record = value;
11081
+ const hasSupportedKeys = pushUnsupportedKeyDiagnostics(
11082
+ record,
11083
+ TEXT_FORMAT_ASSERTION_KEYS,
11084
+ diagnostics
11085
+ );
11086
+ const format = record.format;
11087
+ const hasSupportedFormat = isTextFormatAssertionFormat(format);
11088
+ if (!hasSupportedFormat) {
11089
+ diagnostics.push(
11090
+ compileDiagnostic(
11091
+ "profile.config.invalidShape",
11092
+ 'textFormat.format must be "isoDate".',
11093
+ ruleId
11094
+ )
11095
+ );
11096
+ }
11097
+ return hasSupportedKeys && diagnostics.length === diagnosticCountBefore && hasSupportedFormat ? { format } : void 0;
11098
+ }
10633
11099
  function pushOptionalNonEmptyStringDiagnostic(fieldName, value, ruleId, diagnostics) {
10634
11100
  return value === void 0 ? true : pushNonEmptyStringDiagnostic(fieldName, value, ruleId, diagnostics);
10635
11101
  }
@@ -10763,6 +11229,7 @@ function selectorAssertionCompatibilityError(assertionName, selector) {
10763
11229
  case "sectionsRequired":
10764
11230
  case "references":
10765
11231
  case "tableColumnCoverage":
11232
+ case "frontmatterShape":
10766
11233
  return selector.target === "document" ? void 0 : selectorCompatibilityMessage(assertionName, "document");
10767
11234
  case "tableColumnsRequired":
10768
11235
  return selector.target === "table" ? void 0 : selectorCompatibilityMessage(assertionName, "table");
@@ -10770,6 +11237,7 @@ function selectorAssertionCompatibilityError(assertionName, selector) {
10770
11237
  case "text":
10771
11238
  case "textOccurrenceCount":
10772
11239
  case "textLength":
11240
+ case "textFormat":
10773
11241
  return void 0;
10774
11242
  case "frontmatterRequired":
10775
11243
  return selector.target === "document" ? void 0 : selectorCompatibilityMessage(assertionName, "document");
@@ -10787,9 +11255,11 @@ var ASSERTION_BUILDERS = [
10787
11255
  buildIdsAssertion,
10788
11256
  buildReferencesAssertion,
10789
11257
  buildTableColumnCoverageAssertion,
11258
+ buildFrontmatterShapeAssertion,
10790
11259
  buildTextAssertion,
10791
11260
  buildTextOccurrenceCountAssertion,
10792
11261
  buildTextLengthAssertion,
11262
+ buildTextFormatAssertion,
10793
11263
  buildFrontmatterRequiredAssertion
10794
11264
  ];
10795
11265
  function buildExistsAssertion(assertion, selector, ruleId, _syntaxVersion, diagnostics) {
@@ -11018,6 +11488,29 @@ function buildTableColumnCoverageAssertion(assertion, selector, ruleId, syntaxVe
11018
11488
  }
11019
11489
  return void 0;
11020
11490
  }
11491
+ function buildFrontmatterShapeAssertion(assertion, selector, ruleId, syntaxVersion, diagnostics) {
11492
+ if (assertion.frontmatterShape === void 0 || syntaxVersion !== PROFILE_SYNTAX_VERSION_V2) {
11493
+ return void 0;
11494
+ }
11495
+ const frontmatterShape2 = closedFrontmatterShape(
11496
+ assertion.frontmatterShape,
11497
+ ruleId,
11498
+ diagnostics
11499
+ );
11500
+ if (frontmatterShape2 !== void 0 && pushCompatibilityDiagnostic(
11501
+ "frontmatterShape",
11502
+ selector,
11503
+ ruleId,
11504
+ diagnostics
11505
+ )) {
11506
+ return {
11507
+ kind: "frontmatterShape",
11508
+ ...frontmatterShape2.presence !== void 0 ? { presence: frontmatterShape2.presence } : {},
11509
+ ...frontmatterShape2.fields !== void 0 ? { fields: frontmatterShape2.fields } : {}
11510
+ };
11511
+ }
11512
+ return void 0;
11513
+ }
11021
11514
  function buildTextAssertion(assertion, selector, ruleId, _syntaxVersion, diagnostics) {
11022
11515
  if (assertion.text === void 0) {
11023
11516
  return void 0;
@@ -11110,6 +11603,19 @@ function buildTextLengthAssertion(assertion, selector, ruleId, _syntaxVersion, d
11110
11603
  }
11111
11604
  return void 0;
11112
11605
  }
11606
+ function buildTextFormatAssertion(assertion, selector, ruleId, syntaxVersion, diagnostics) {
11607
+ if (assertion.textFormat === void 0 || syntaxVersion !== PROFILE_SYNTAX_VERSION_V2) {
11608
+ return void 0;
11609
+ }
11610
+ const textFormat = closedTextFormat(assertion.textFormat, ruleId, diagnostics);
11611
+ if (textFormat !== void 0 && pushCompatibilityDiagnostic("textFormat", selector, ruleId, diagnostics)) {
11612
+ return {
11613
+ kind: "textFormat",
11614
+ format: textFormat.format
11615
+ };
11616
+ }
11617
+ return void 0;
11618
+ }
11113
11619
  function buildFrontmatterRequiredAssertion(assertion, selector, ruleId, _syntaxVersion, diagnostics) {
11114
11620
  if (assertion.frontmatterRequired === void 0) {
11115
11621
  return void 0;
@@ -11173,7 +11679,9 @@ var ASSERTION_KEYS_V1 = [
11173
11679
  ];
11174
11680
  var ASSERTION_KEYS_V2 = [
11175
11681
  ...ASSERTION_KEYS_V1,
11176
- "tableColumnCoverage"
11682
+ "tableColumnCoverage",
11683
+ "frontmatterShape",
11684
+ "textFormat"
11177
11685
  ];
11178
11686
  function compiledAssertionsFromValue(assertion, selector, ruleId, syntaxVersion, diagnostics) {
11179
11687
  const compiled = [];
@@ -11869,7 +12377,9 @@ var SUPPORTED_ASSERTION_KEYS_V1 = [
11869
12377
  ];
11870
12378
  var SUPPORTED_ASSERTION_KEYS_V2 = [
11871
12379
  ...SUPPORTED_ASSERTION_KEYS_V1,
11872
- "tableColumnCoverage"
12380
+ "tableColumnCoverage",
12381
+ "frontmatterShape",
12382
+ "textFormat"
11873
12383
  ];
11874
12384
  function assertionFromValue(value, syntaxVersion, diagnostics) {
11875
12385
  if (!isPlainRecord(value)) {
@@ -11891,7 +12401,9 @@ function assertionFromValue(value, syntaxVersion, diagnostics) {
11891
12401
  ...textOccurrenceCountFromValue(value.textOccurrenceCount, diagnostics),
11892
12402
  ...textLengthFromValue(value.textLength, diagnostics),
11893
12403
  ...frontmatterRequiredFromValue(value.frontmatterRequired, diagnostics),
11894
- ...supportsTableColumnCoverage(syntaxVersion) ? tableColumnCoverageFromValue(value.tableColumnCoverage, diagnostics) : {}
12404
+ ...supportsV2AssertionSurface(syntaxVersion) ? tableColumnCoverageFromValue(value.tableColumnCoverage, diagnostics) : {},
12405
+ ...supportsV2AssertionSurface(syntaxVersion) ? frontmatterShapeFromValue2(value.frontmatterShape, diagnostics) : {},
12406
+ ...supportsV2AssertionSurface(syntaxVersion) ? textFormatFromValue(value.textFormat, diagnostics) : {}
11895
12407
  };
11896
12408
  if (Object.keys(assertion).length === 0) {
11897
12409
  if (hasUnsupportedVocabulary) {
@@ -11926,9 +12438,9 @@ function unsupportedAssertionKeys(value, syntaxVersion, diagnostics) {
11926
12438
  return hasUnsupportedVocabulary;
11927
12439
  }
11928
12440
  function assertionKeysForSyntaxVersion2(syntaxVersion) {
11929
- return supportsTableColumnCoverage(syntaxVersion) ? SUPPORTED_ASSERTION_KEYS_V2 : SUPPORTED_ASSERTION_KEYS_V1;
12441
+ return supportsV2AssertionSurface(syntaxVersion) ? SUPPORTED_ASSERTION_KEYS_V2 : SUPPORTED_ASSERTION_KEYS_V1;
11930
12442
  }
11931
- function supportsTableColumnCoverage(syntaxVersion) {
12443
+ function supportsV2AssertionSurface(syntaxVersion) {
11932
12444
  return syntaxVersion === PROFILE_SYNTAX_VERSION_V2;
11933
12445
  }
11934
12446
  function existsFromValue(value, diagnostics) {
@@ -12169,6 +12681,19 @@ function optionalTableColumnCoverageTargetStringArray(record, key, diagnostics)
12169
12681
  (field) => `tableColumnCoverage.target.${field} must be an array of non-empty strings when provided.`
12170
12682
  );
12171
12683
  }
12684
+ function frontmatterShapeFromValue2(value, diagnostics) {
12685
+ if (value === void 0) {
12686
+ return {};
12687
+ }
12688
+ const result = frontmatterShapeFromValue(value);
12689
+ for (const key of result.unsupportedKeys) {
12690
+ diagnostics.push(unsupportedProfileKey(key));
12691
+ }
12692
+ for (const message of result.invalidShapes) {
12693
+ diagnostics.push(invalidShape(message));
12694
+ }
12695
+ return result.shape === void 0 ? {} : { frontmatterShape: result.shape };
12696
+ }
12172
12697
  function textAssertionFromValue(value, diagnostics) {
12173
12698
  if (value === void 0) {
12174
12699
  return {};
@@ -12247,6 +12772,28 @@ function textLengthFromValue(value, diagnostics) {
12247
12772
  }
12248
12773
  return { textLength };
12249
12774
  }
12775
+ function textFormatFromValue(value, diagnostics) {
12776
+ if (value === void 0) {
12777
+ return {};
12778
+ }
12779
+ if (!isPlainRecord(value)) {
12780
+ diagnostics.push(invalidShape("textFormat must be an object."));
12781
+ return {};
12782
+ }
12783
+ const diagnosticCountBeforeShape = diagnostics.length;
12784
+ unsupportedKeys(value, TEXT_FORMAT_ASSERTION_KEYS, diagnostics);
12785
+ const format = value.format;
12786
+ const hasSupportedFormat = isTextFormatAssertionFormat(format);
12787
+ if (!hasSupportedFormat) {
12788
+ diagnostics.push(
12789
+ invalidShape('textFormat.format must be "isoDate".')
12790
+ );
12791
+ }
12792
+ if (diagnostics.length > diagnosticCountBeforeShape || !hasSupportedFormat) {
12793
+ return {};
12794
+ }
12795
+ return { textFormat: { format } };
12796
+ }
12250
12797
  function frontmatterRequiredFromValue(value, diagnostics) {
12251
12798
  if (value === void 0) {
12252
12799
  return {};
@@ -12871,6 +13418,9 @@ function cloneSourceRange2(sourceRange) {
12871
13418
  // src/declarative-validation/evidence/index.ts
12872
13419
  import { createHash } from "node:crypto";
12873
13420
 
13421
+ // src/internal/package-version.ts
13422
+ var MARKDOWN_ENGINE_PACKAGE_VERSION = "3.1.1";
13423
+
12874
13424
  // src/internal/stable-json.ts
12875
13425
  function normalizeStableJsonValue(value) {
12876
13426
  if (Array.isArray(value)) {
@@ -13009,7 +13559,7 @@ function createDeclarativeValidationEvidence(document3, profile, ruleResults, di
13009
13559
  return {
13010
13560
  inputHash: sha256(stringifyStableJson(documentWithoutPath(document3))),
13011
13561
  profileHash: sha256(stringifyStableJson(resolvedProfile(profile, document3))),
13012
- engineVersion: "3.0.0",
13562
+ engineVersion: MARKDOWN_ENGINE_PACKAGE_VERSION,
13013
13563
  runtimeVersion: process.version,
13014
13564
  ruleResults: cloneRuleResults(ruleResults),
13015
13565
  diagnostics: cloneDiagnostics(diagnostics)