@jasonbelmonti/markdown-engine 3.1.0 → 3.2.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 (81) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +29 -10
  3. package/SECURITY.md +4 -4
  4. package/dist/api/declarative-validation.d.ts.map +1 -1
  5. package/dist/api/declarative-validation.js +31 -11
  6. package/dist/api/declarative-validation.js.map +1 -1
  7. package/dist/api/document-set-validation.js +1 -0
  8. package/dist/api/document-set-validation.js.map +1 -1
  9. package/dist/cli/declarative-validation.d.ts.map +1 -1
  10. package/dist/cli/declarative-validation.js +4 -4
  11. package/dist/cli/declarative-validation.js.map +1 -1
  12. package/dist/declarative-validation/applicability/classifier.d.ts +2 -1
  13. package/dist/declarative-validation/applicability/classifier.d.ts.map +1 -1
  14. package/dist/declarative-validation/applicability/classifier.js +2 -2
  15. package/dist/declarative-validation/applicability/classifier.js.map +1 -1
  16. package/dist/declarative-validation/assertions/context.d.ts +4 -0
  17. package/dist/declarative-validation/assertions/context.d.ts.map +1 -1
  18. package/dist/declarative-validation/assertions/diagnostics.d.ts +3 -0
  19. package/dist/declarative-validation/assertions/diagnostics.d.ts.map +1 -1
  20. package/dist/declarative-validation/assertions/diagnostics.js +18 -0
  21. package/dist/declarative-validation/assertions/diagnostics.js.map +1 -1
  22. package/dist/declarative-validation/assertions/evaluator.d.ts +2 -1
  23. package/dist/declarative-validation/assertions/evaluator.d.ts.map +1 -1
  24. package/dist/declarative-validation/assertions/evaluator.js +7 -1
  25. package/dist/declarative-validation/assertions/evaluator.js.map +1 -1
  26. package/dist/declarative-validation/assertions/group-evaluator.d.ts +3 -2
  27. package/dist/declarative-validation/assertions/group-evaluator.d.ts.map +1 -1
  28. package/dist/declarative-validation/assertions/group-evaluator.js +32 -14
  29. package/dist/declarative-validation/assertions/group-evaluator.js.map +1 -1
  30. package/dist/declarative-validation/assertions/source-length.d.ts +9 -0
  31. package/dist/declarative-validation/assertions/source-length.d.ts.map +1 -0
  32. package/dist/declarative-validation/assertions/source-length.js +33 -0
  33. package/dist/declarative-validation/assertions/source-length.js.map +1 -0
  34. package/dist/declarative-validation/compiler/assertion-builders.d.ts.map +1 -1
  35. package/dist/declarative-validation/compiler/assertion-builders.js +4 -32
  36. package/dist/declarative-validation/compiler/assertion-builders.js.map +1 -1
  37. package/dist/declarative-validation/compiler/assertion-shapes.d.ts +1 -2
  38. package/dist/declarative-validation/compiler/assertion-shapes.d.ts.map +1 -1
  39. package/dist/declarative-validation/compiler/assertion-shapes.js +1 -25
  40. package/dist/declarative-validation/compiler/assertion-shapes.js.map +1 -1
  41. package/dist/declarative-validation/compiler/assertions.d.ts.map +1 -1
  42. package/dist/declarative-validation/compiler/assertions.js +1 -0
  43. package/dist/declarative-validation/compiler/assertions.js.map +1 -1
  44. package/dist/declarative-validation/compiler/compatibility.d.ts +2 -0
  45. package/dist/declarative-validation/compiler/compatibility.d.ts.map +1 -1
  46. package/dist/declarative-validation/compiler/compatibility.js +10 -0
  47. package/dist/declarative-validation/compiler/compatibility.js.map +1 -1
  48. package/dist/declarative-validation/compiler/index.d.ts.map +1 -1
  49. package/dist/declarative-validation/compiler/index.js +10 -3
  50. package/dist/declarative-validation/compiler/index.js.map +1 -1
  51. package/dist/declarative-validation/compiler/length-assertion-builders.d.ts +7 -0
  52. package/dist/declarative-validation/compiler/length-assertion-builders.d.ts.map +1 -0
  53. package/dist/declarative-validation/compiler/length-assertion-builders.js +58 -0
  54. package/dist/declarative-validation/compiler/length-assertion-builders.js.map +1 -0
  55. package/dist/declarative-validation/compiler/plan.d.ts +4 -0
  56. package/dist/declarative-validation/compiler/plan.d.ts.map +1 -1
  57. package/dist/declarative-validation/evidence/index.d.ts +2 -1
  58. package/dist/declarative-validation/evidence/index.d.ts.map +1 -1
  59. package/dist/declarative-validation/evidence/index.js +27 -3
  60. package/dist/declarative-validation/evidence/index.js.map +1 -1
  61. package/dist/declarative-validation/profile/assertion-schema.d.ts.map +1 -1
  62. package/dist/declarative-validation/profile/assertion-schema.js +11 -50
  63. package/dist/declarative-validation/profile/assertion-schema.js.map +1 -1
  64. package/dist/declarative-validation/profile/index.d.ts +4 -0
  65. package/dist/declarative-validation/profile/index.d.ts.map +1 -1
  66. package/dist/declarative-validation/profile/length-bound-schema.d.ts +7 -0
  67. package/dist/declarative-validation/profile/length-bound-schema.d.ts.map +1 -0
  68. package/dist/declarative-validation/profile/length-bound-schema.js +55 -0
  69. package/dist/declarative-validation/profile/length-bound-schema.js.map +1 -0
  70. package/dist/declarative-validation/results/create-result.js +1 -1
  71. package/dist/declarative-validation/results/create-result.js.map +1 -1
  72. package/dist/declarative-validation/results/types.d.ts +1 -0
  73. package/dist/declarative-validation/results/types.d.ts.map +1 -1
  74. package/dist/internal/package-version.d.ts +2 -0
  75. package/dist/internal/package-version.d.ts.map +1 -0
  76. package/dist/internal/package-version.js +2 -0
  77. package/dist/internal/package-version.js.map +1 -0
  78. package/dist-bundled/markdown-engine-cli.mjs +702 -525
  79. package/docs/contracts/declarative-validation.md +66 -10
  80. package/package.json +9 -5
  81. package/scripts/install-markdown-engine-cli.sh +2 -2
@@ -2,7 +2,8 @@
2
2
 
3
3
  Status: package 3.0.0, v1 profile syntax with v2 Conditional V2, document contract 1.0.0
4
4
  Last updated: 2026-06-15
5
- Current v2 surface: flat-rule result/evidence shell, ID count-bound schema and
5
+ Current v2 surface: flat-rule result/evidence shell, document `sourceLength`
6
+ schema and runtime measurement, ID count-bound schema and
6
7
  runtime evaluator contract, plus `tableColumnCoverage` schema, compiled-plan,
7
8
  and runtime evaluator contract, `frontmatterShape` schema, compiled-plan, and
8
9
  runtime evaluator contract, `textFormat` schema, compiled-plan, and runtime
@@ -63,8 +64,19 @@ validateDocumentSet(
63
64
  entries: readonly ValidateDocumentSetEntry[],
64
65
  options?: ValidateDocumentSetOptions,
65
66
  ): ValidateDocumentSetResult
67
+
68
+ interface DeclarativeValidationOptions {
69
+ path?: string;
70
+ includeEvidence?: boolean;
71
+ sourceText?: string;
72
+ }
66
73
  ```
67
74
 
75
+ `sourceText` carries the complete original Markdown string to validation. It is
76
+ required only when an evaluated v2 `sourceLength` assertion needs to measure
77
+ the pre-normalization input; it does not become an enumerable
78
+ `EngineDocument` field.
79
+
68
80
  Compiled declarative validation plans are internal. They are not exported from
69
81
  the package root, are not serialized in API or CLI results, and carry no semver
70
82
  stability guarantee.
@@ -334,6 +346,10 @@ interface DeclarativeAssertion {
334
346
  min?: number;
335
347
  max?: number;
336
348
  };
349
+ sourceLength?: {
350
+ min?: number;
351
+ max?: number;
352
+ };
337
353
  textFormat?: {
338
354
  format: "isoDate";
339
355
  };
@@ -364,6 +380,7 @@ Selector/assertion compatibility is part of the public contract:
364
380
  | `text` | all supported selector targets |
365
381
  | `textOccurrenceCount` | all supported selector targets |
366
382
  | `textLength` | all supported selector targets |
383
+ | `sourceLength` | `document` |
367
384
  | `textFormat` | all supported selector targets |
368
385
  | `frontmatterRequired` | `document` |
369
386
 
@@ -442,6 +459,17 @@ integers, `min` must be less than or equal to `max` when both are present, and
442
459
  evaluation uses JavaScript string `.length` for each selected target's
443
460
  normalized text.
444
461
 
462
+ For v2 profiles, `sourceLength` must include `min`, `max`, or both with the same
463
+ non-negative integer and ordered-range rules. It is compatible only with a
464
+ `document` selector and evaluates the complete `sourceText` supplied to
465
+ `validateWithProfile`, including frontmatter, Markdown syntax, whitespace, line
466
+ endings, and surrogate pairs, using JavaScript string `.length` (UTF-16 code
467
+ units). The CLI and `validateDocumentSet` supply the exact Markdown input they
468
+ read. When an evaluated `sourceLength` assertion has no complete source
469
+ context, validation fails closed with
470
+ `profile.validation.sourceUnavailable`; normalized document text and source
471
+ ranges are never used as fallbacks.
472
+
445
473
  For v2 profiles, `textFormat` is admitted as a flat-rule schema and internal
446
474
  compiled-plan assertion. `textFormat.format` must be exactly `"isoDate"`;
447
475
  custom formats, locale options, regex-like formats, profile-supplied patterns,
@@ -470,9 +498,12 @@ rather than fabricated when unavailable.
470
498
 
471
499
  Rule-level `when` uses the existing validation diagnostic codes. When
472
500
  applicability does not match, those diagnostics are cloned into
473
- `ruleResults[].when.diagnostics`; they are not promoted into top-level
501
+ `ruleResults[].when.diagnostics`; they are not normally promoted into top-level
474
502
  `diagnostics`, so a skipped rule with a nested error-severity applicability
475
- diagnostic can still leave the aggregate `valid` value `true`.
503
+ diagnostic can still leave the aggregate `valid` value `true`. The
504
+ `profile.validation.sourceUnavailable` safety diagnostic is the exception: it
505
+ is promoted with error severity so unavailable raw-source context cannot yield
506
+ an unproven pass.
476
507
 
477
508
  | Code | Severity source | Emitted when |
478
509
  | --- | --- | --- |
@@ -498,6 +529,7 @@ diagnostic can still leave the aggregate `valid` value `true`.
498
529
  | `profile.validation.referenceMissing` | Rule severity | A source ID is absent from a required target section. |
499
530
  | `profile.validation.sectionMissing` | Rule severity | A required section heading is absent. |
500
531
  | `profile.validation.sectionOrder` | Rule severity | A strict required-section order cannot be satisfied. |
532
+ | `profile.validation.sourceUnavailable` | `error` | An evaluated `sourceLength` assertion cannot access the complete original Markdown source. |
501
533
  | `profile.validation.tableColumnCoverageIdMissing` | Rule severity | A source ID is absent from the configured target table column. |
502
534
  | `profile.validation.tableColumnCoverageTargetColumnMissing` | Rule severity | The configured target table column cannot be resolved. |
503
535
  | `profile.validation.tableColumnCoverageTargetSectionMissing` | Rule severity | The configured target section cannot be resolved. |
@@ -599,6 +631,7 @@ interface DeclarativeValidationEvidence<
599
631
  profileHash: string;
600
632
  engineVersion: string;
601
633
  runtimeVersion: string;
634
+ sourceLength?: number;
602
635
  ruleResults: readonly RuleResult[];
603
636
  diagnostics: readonly MarkdownDiagnostic[];
604
637
  }
@@ -613,10 +646,18 @@ diagnostics array. Evidence does not serialize compiled rule plans, selector
613
646
  target records, assertion-specific ID count evidence, or assertion-specific
614
647
  table-column coverage evidence.
615
648
 
649
+ When a v2 profile uses `sourceLength` and complete source context is available,
650
+ evidence exposes the measured UTF-16 code-unit count as `sourceLength`.
651
+ Profiles that do not use the assertion omit this field and preserve their
652
+ existing evidence serialization.
653
+
616
654
  `inputHash` is a lowercase hexadecimal SHA-256 digest of the stable JSON
617
655
  serialization of the supplied normalized `EngineDocument` after omitting only
618
656
  the top-level `document.path` field. Structural target paths remain part of the
619
- canonical input.
657
+ canonical input. For a profile using `sourceLength`, the canonical input is an
658
+ object containing that normalized document plus the exposed `sourceLength`
659
+ measurement, so otherwise equivalent normalized documents with different raw
660
+ source lengths produce different input hashes.
620
661
 
621
662
  `profileHash` is a lowercase hexadecimal SHA-256 digest of the stable JSON
622
663
  serialization of the resolved `ValidationProfile` after applying the resolved
@@ -624,12 +665,14 @@ serialization of the resolved `ValidationProfile` after applying the resolved
624
665
  `documentVersion` and an explicit matching `documentVersion` therefore produce
625
666
  the same profile hash for the same document version and rules.
626
667
 
627
- `engineVersion` records the package version that produced the evidence. In the
628
- 3.0 release line this is `"3.0.0"` even though `documentVersion` remains
668
+ `engineVersion` records the package version that produced the evidence. This
669
+ matches the package metadata version even though `documentVersion` remains
629
670
  `"1.0.0"`.
630
671
 
631
- Raw Markdown bytes, raw YAML bytes, YAML comments, caller file paths, and
632
- `includeEvidence` itself are not part of either evidence hash.
672
+ Raw Markdown content, raw YAML bytes, YAML comments, caller file paths, and
673
+ `includeEvidence` itself are not part of either evidence hash. Only the numeric
674
+ raw-source length measurement is added to `inputHash`, and only for profiles
675
+ that use `sourceLength`.
633
676
 
634
677
  ## CLI Behavior
635
678
 
@@ -750,6 +793,18 @@ rules:
750
793
  format: isoDate
751
794
  ```
752
795
 
796
+ ```yaml
797
+ # v2 source budget: bounds the complete original Markdown input.
798
+ syntaxVersion: markdown-engine.validation@v2
799
+ rules:
800
+ - id: document.source-budget
801
+ select:
802
+ target: document
803
+ assert:
804
+ sourceLength:
805
+ max: 12000
806
+ ```
807
+
753
808
  The v1 profile above continues to emit the v1 validation-result shape. V2
754
809
  profiles emit syntax-versioned v2 result metadata with
755
810
  `evaluatedRuleCount` and `skippedRuleCount`; v2 rule results include `status`
@@ -773,8 +828,9 @@ Migration notes:
773
828
  skipped-rule `when` diagnostics. Aggregate validity is still determined from
774
829
  top-level diagnostics.
775
830
  - Consumers that compare evidence hashes must normalize expectations around
776
- resolved `documentVersion`, default rule severity, stable key order, and
777
- exclusion of only top-level `document.path` from `inputHash`.
831
+ resolved `documentVersion`, default rule severity, stable key order,
832
+ exclusion of only top-level `document.path`, and conditional inclusion of the
833
+ numeric `sourceLength` measurement in `inputHash`.
778
834
  - Regex-like matching, JavaScript predicates, plugins, semantic scoring, and
779
835
  profile-specific rules belong outside this package unless a future contract
780
836
  explicitly expands the engine boundary.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jasonbelmonti/markdown-engine",
3
- "version": "3.1.0",
3
+ "version": "3.2.0",
4
4
  "description": "Deterministic Markdown parsing and validation engine package.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -49,8 +49,9 @@
49
49
  "build:cli-bundle": "node scripts/build-cli-bundle.mjs",
50
50
  "prepack": "npm run release:verify",
51
51
  "prepublishOnly": "npm run release:verify",
52
- "release:verify": "npm run typecheck && npm test && node scripts/check-boundaries.mjs && npm run docs:rich-ir-contract && npm run docs:declarative-validation-contract && npm run audit:declarative-validation-boundary && npm run build && npm run build:cli:bundled && node scripts/prove-repeatability.mjs --runs 10 && npm run release:check-clean",
53
- "release:check-clean": "git diff --check HEAD -- && git diff --exit-code HEAD --",
52
+ "release:verify": "npm run typecheck && npm test && node scripts/check-boundaries.mjs && npm run docs:rich-ir-contract && npm run docs:declarative-validation-contract && npm run audit:declarative-validation-boundary && npm run build && npm run build:cli:bundled && npm run release:verify:installer-pin && node scripts/prove-repeatability.mjs --runs 10 && npm run release:check-clean",
53
+ "release:verify:installer-pin": "node scripts/check-installer-pin.mjs",
54
+ "release:check-clean": "node scripts/check-release-clean.mjs",
54
55
  "snapshots:update:parser": "npm run build && vitest run tests/parser-fixtures.test.ts -u \"--exclude=.worktrees/**\"",
55
56
  "snapshots:update:rules": "npm run build && vitest run tests/rules.test.ts -u \"--exclude=.worktrees/**\"",
56
57
  "snapshots:update:rich-ir": "npm run build && vitest run tests/rich-ir-targets.test.ts tests/rich-ir-queries.test.ts tests/rich-ir-link-references.test.ts -u \"--exclude=.worktrees/**\"",
@@ -70,7 +71,7 @@
70
71
  "test:validation:profile": "npm run build && vitest run tests/declarative-validation-profile.test.ts \"--exclude=.worktrees/**\"",
71
72
  "test:validation:compiler": "npm run build && vitest run tests/declarative-validation-compiler.test.ts \"--exclude=.worktrees/**\"",
72
73
  "test:validation:selectors": "npm run build && vitest run tests/declarative-validation-selectors.test.ts \"--exclude=.worktrees/**\"",
73
- "test:validation:assertions": "npm run build && vitest run tests/declarative-validation-assertions.test.ts tests/declarative-validation-frontmatter-shape-assertions.test.ts tests/declarative-validation-table-column-coverage-fixtures.test.ts tests/declarative-validation-grouped-rules-fixtures.test.ts tests/declarative-validation-when-skipped-rules-fixtures.test.ts \"--exclude=.worktrees/**\"",
74
+ "test:validation:assertions": "npm run build && vitest run tests/declarative-validation-assertions.test.ts tests/declarative-validation-frontmatter-shape-assertions.test.ts tests/declarative-validation-table-column-coverage-fixtures.test.ts tests/declarative-validation-source-assertion-coverage.test.ts tests/declarative-validation-source-length.test.ts tests/declarative-validation-normalized-source-ranges.test.ts tests/declarative-validation-grouped-rules-fixtures.test.ts tests/declarative-validation-when-skipped-rules-fixtures.test.ts \"--exclude=.worktrees/**\"",
74
75
  "test:validation:diagnostics": "npm run build && vitest run tests/declarative-validation-diagnostics.test.ts \"--exclude=.worktrees/**\"",
75
76
  "test:validation:cli": "npm run build && vitest run tests/declarative-validation-cli.test.ts \"--exclude=.worktrees/**\"",
76
77
  "test:validation:examples": "npm run build && vitest run tests/declarative-validation-examples.test.ts \"--exclude=.worktrees/**\"",
@@ -80,6 +81,8 @@
80
81
  "docs:declarative-validation-contract": "node scripts/check-declarative-validation-contract-docs.mjs",
81
82
  "typecheck": "tsc -p tsconfig.json --noEmit",
82
83
  "test": "npm run build && vitest run \"--exclude=.worktrees/**\"",
84
+ "test:coverage": "npm run build && npm run build:cli:bundled && vitest run --coverage \"--exclude=.worktrees/**\"",
85
+ "test:coverage:ci": "npm run test:coverage",
83
86
  "test:snapshots": "npm run build && vitest run tests/parser-fixtures.test.ts tests/rules.test.ts tests/rich-ir-targets.test.ts tests/rich-ir-queries.test.ts tests/rich-ir-link-references.test.ts tests/serialization-repeatability.test.ts \"--exclude=.worktrees/**\""
84
87
  },
85
88
  "engines": {
@@ -87,10 +90,11 @@
87
90
  },
88
91
  "devDependencies": {
89
92
  "@types/node": "^22.19.17",
93
+ "@vitest/coverage-v8": "^3.2.7",
90
94
  "cmark-gfm": "^0.9.0",
91
95
  "esbuild": "^0.27.7",
92
96
  "typescript": "^5.8.3",
93
- "vitest": "^3.1.3"
97
+ "vitest": "^3.2.7"
94
98
  },
95
99
  "dependencies": {
96
100
  "remark-gfm": "^4.0.1",
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env sh
2
2
  set -eu
3
3
 
4
- VERSION="3.0.0"
4
+ VERSION="3.2.0"
5
5
  PACKAGE="@jasonbelmonti/markdown-engine"
6
- EXPECTED_SHA256="f61ab59e28eb92cccf4fd8073c090102e67a9397a7f0d208661ded14b879053d"
6
+ EXPECTED_SHA256="5033d08160fcd3f44b11498dc29d01db73d7aff2a55eb3257fd9a1c14e29c3f6"
7
7
  DEFAULT_DATA_HOME="${XDG_DATA_HOME:-$HOME/.local/share}"
8
8
  MARKDOWN_ENGINE_HOME="${MARKDOWN_ENGINE_HOME:-$DEFAULT_DATA_HOME/markdown-engine}"
9
9
  MARKDOWN_ENGINE_BIN_DIR="${MARKDOWN_ENGINE_BIN_DIR:-$HOME/.local/bin}"