@jasonbelmonti/markdown-engine 3.3.0 → 3.5.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 (35) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +11 -10
  3. package/dist/declarative-validation/assertions/evaluator.d.ts.map +1 -1
  4. package/dist/declarative-validation/assertions/evaluator.js +3 -0
  5. package/dist/declarative-validation/assertions/evaluator.js.map +1 -1
  6. package/dist/declarative-validation/assertions/frontmatter-shape.js +45 -5
  7. package/dist/declarative-validation/assertions/frontmatter-shape.js.map +1 -1
  8. package/dist/declarative-validation/assertions/table-columns-exact.d.ts +9 -0
  9. package/dist/declarative-validation/assertions/table-columns-exact.d.ts.map +1 -0
  10. package/dist/declarative-validation/assertions/table-columns-exact.js +44 -0
  11. package/dist/declarative-validation/assertions/table-columns-exact.js.map +1 -0
  12. package/dist/declarative-validation/compiler/assertion-builders.d.ts.map +1 -1
  13. package/dist/declarative-validation/compiler/assertion-builders.js +18 -0
  14. package/dist/declarative-validation/compiler/assertion-builders.js.map +1 -1
  15. package/dist/declarative-validation/compiler/assertions.d.ts.map +1 -1
  16. package/dist/declarative-validation/compiler/assertions.js +1 -0
  17. package/dist/declarative-validation/compiler/assertions.js.map +1 -1
  18. package/dist/declarative-validation/compiler/compatibility.d.ts.map +1 -1
  19. package/dist/declarative-validation/compiler/compatibility.js +1 -0
  20. package/dist/declarative-validation/compiler/compatibility.js.map +1 -1
  21. package/dist/declarative-validation/compiler/plan.d.ts +3 -0
  22. package/dist/declarative-validation/compiler/plan.d.ts.map +1 -1
  23. package/dist/declarative-validation/profile/assertion-schema.d.ts.map +1 -1
  24. package/dist/declarative-validation/profile/assertion-schema.js +20 -0
  25. package/dist/declarative-validation/profile/assertion-schema.js.map +1 -1
  26. package/dist/declarative-validation/profile/frontmatter-shape-schema.js +52 -7
  27. package/dist/declarative-validation/profile/frontmatter-shape-schema.js.map +1 -1
  28. package/dist/declarative-validation/profile/index.d.ts +17 -0
  29. package/dist/declarative-validation/profile/index.d.ts.map +1 -1
  30. package/dist/internal/package-version.d.ts +1 -1
  31. package/dist/internal/package-version.js +1 -1
  32. package/dist-bundled/markdown-engine-cli.mjs +501 -287
  33. package/docs/contracts/declarative-validation.md +71 -11
  34. package/package.json +2 -2
  35. package/scripts/install-markdown-engine-cli.sh +2 -2
@@ -1,10 +1,10 @@
1
1
  # Declarative Validation Contract
2
2
 
3
3
  Status: package 3.0.0, v1 profile syntax with v2 Conditional V2, document contract 1.0.0
4
- Last updated: 2026-08-12
4
+ Last updated: 2026-08-15
5
5
  Current v2 surface: flat-rule result/evidence shell, generic selector
6
6
  `selectionCount` bounds, document `sourceLength` schema and runtime
7
- measurement, ID count-bound schema and
7
+ measurement, exact normalized table-header `tableColumnsExact` assertions, ID count-bound schema and
8
8
  runtime evaluator contract, plus `tableColumnCoverage` schema, compiled-plan,
9
9
  and runtime evaluator contract, `frontmatterShape` schema, compiled-plan, and
10
10
  runtime evaluator contract, `textFormat` schema, compiled-plan, and runtime
@@ -106,7 +106,8 @@ runtime evaluator layers, admits `tableColumnCoverage` at the schema, internal
106
106
  compiled-plan, and
107
107
  runtime evaluator layers, admits `frontmatterShape` at the schema, internal
108
108
  compiled-plan, and runtime evaluator layers, admits `textFormat` at the schema,
109
- internal compiled-plan, and runtime evaluator layers, admits non-recursive
109
+ internal compiled-plan, and runtime evaluator layers, admits `tableColumnsExact`
110
+ at the schema, internal compiled-plan, and runtime evaluator layers, admits non-recursive
110
111
  grouped rules at the schema, compiled-plan, and runtime evaluator layers, and
111
112
  admits optional rule-level `when` at the schema, internal compiled-plan, and
112
113
  matcher layers.
@@ -236,7 +237,7 @@ emit `profile.config.invalidShape`.
236
237
  Rule-level `when` is allowed only on v2 rules. Branch-level `when` remains
237
238
  unsupported. V1 profiles preserve the original flat rule authoring contract;
238
239
  grouped `anyOf` / `allOf`, ID count bounds, `tableColumnCoverage`,
239
- `frontmatterShape`, `textFormat`, and rule-level `when` are v2 additions.
240
+ `tableColumnsExact`, `frontmatterShape`, `textFormat`, and rule-level `when` are v2 additions.
240
241
 
241
242
  Profile values must be JSON-safe data properties after YAML materialization.
242
243
  Functions, accessors, proxies, cyclic structures, sparse arrays, `undefined`
@@ -307,6 +308,9 @@ interface DeclarativeAssertion {
307
308
  tableColumnsRequired?: {
308
309
  columns: readonly string[];
309
310
  };
311
+ tableColumnsExact?: {
312
+ columns: readonly string[];
313
+ };
310
314
  ids?: {
311
315
  prefix?: string;
312
316
  unique?: boolean;
@@ -339,6 +343,9 @@ interface DeclarativeAssertion {
339
343
  required?: true;
340
344
  valueType?: "string" | "number" | "boolean" | "array" | "object" | "null";
341
345
  nonEmpty?: true;
346
+ equals?: string;
347
+ nonBlank?: true;
348
+ forbidden?: true;
342
349
  }[];
343
350
  };
344
351
  text?: {
@@ -381,6 +388,7 @@ Selector/assertion compatibility is part of the public contract:
381
388
  | `selectionCount` | all supported selector targets |
382
389
  | `sectionsRequired` | `document` |
383
390
  | `tableColumnsRequired` | `table` |
391
+ | `tableColumnsExact` | `table` |
384
392
  | `ids` | all supported selector targets |
385
393
  | `references` | `document` |
386
394
  | `tableColumnCoverage` | `document` |
@@ -409,6 +417,19 @@ result with `profile.validation.emptySelection`. Maximum-bound failures use the
409
417
  first excess selected target as source evidence when available. Minimum-bound
410
418
  failures at zero omit source location rather than fabricating one.
411
419
 
420
+ For v2 profiles, `tableColumnsExact` requires a non-empty `columns` array of
421
+ non-empty strings and is compatible only with a `table` selector. It compares
422
+ the complete normalized header sequence from the selected `EngineTable` exactly:
423
+ count, order, text, and duplicate occurrences must all match. Additional
424
+ columns before, between, or after configured columns, and missing, reordered,
425
+ renamed, or duplicated columns fail with
426
+ `profile.validation.assertionFailed`. The deterministic diagnostic includes the
427
+ expected and actual header sequences. When an actual header first mismatches or
428
+ is excess, its header-cell source range is attached when available; otherwise
429
+ the normal selected-table source evidence is retained. This assertion does not
430
+ change the ordered-subsequence behavior of `tableColumnsRequired` or table
431
+ selector `header` / `tableHeader` matching.
432
+
412
433
  `sectionsRequired.order` defaults to `none`. `strict` checks that configured
413
434
  headings appear as an ordered subsequence in the normalized section tree
414
435
  flattened in source order.
@@ -446,12 +467,17 @@ selector because frontmatter is document metadata. `presence` is optional and
446
467
  must be exactly `"required"` or `"forbidden"` when provided. `fields` is an
447
468
  optional non-empty array of field constraints. Each field constraint has a
448
469
  required non-empty `field` name and must include at least one effective
449
- constraint: `required: true`, `valueType`, or `nonEmpty: true`. Field names
450
- within one `frontmatterShape.fields` array must be unique. `valueType` must be
470
+ constraint: `required: true`, `valueType`, `nonEmpty: true`, `equals`,
471
+ `nonBlank: true`, or `forbidden: true`. Field names within one
472
+ `frontmatterShape.fields` array must be unique. `valueType` must be
451
473
  one of `"string"`, `"number"`, `"boolean"`, `"array"`, `"object"`, or `"null"`.
452
- `nonEmpty: true` is a string predicate; when it is combined with `valueType`,
453
- `valueType` must be `"string"`. `presence: "forbidden"` cannot be combined with
454
- `fields`.
474
+ `nonEmpty: true`, `equals`, and `nonBlank: true` are string predicates; when
475
+ any is combined with `valueType`, `valueType` must be `"string"`. `equals`
476
+ accepts any string, including the empty string, and compares it exactly.
477
+ `nonBlank: true` requires a string whose JavaScript `trim()` result is non-empty.
478
+ `forbidden: true` requires the named field to be absent and cannot be combined
479
+ with `required`, `valueType`, `nonEmpty`, `equals`, or `nonBlank`.
480
+ `presence: "forbidden"` cannot be combined with `fields`.
455
481
 
456
482
  Runtime evaluation treats frontmatter as present when
457
483
  `document.frontmatter !== undefined`. `presence: "required"` fails absent
@@ -468,6 +494,16 @@ requires a present field value to be a non-empty string and emits
468
494
  `profile.validation.frontmatterFieldEmpty` when that predicate fails. When
469
495
  `valueType: "string"` and `nonEmpty: true` are combined, a non-string value
470
496
  emits the type-mismatch diagnostic without a duplicate empty-string diagnostic.
497
+ `equals` compares present strings exactly without coercion and emits
498
+ `profile.validation.frontmatterFieldValueMismatch` on mismatch. `nonBlank: true`
499
+ uses JavaScript `trim()` without coercion and emits
500
+ `profile.validation.frontmatterFieldBlank` when its field is not a non-blank
501
+ string. `forbidden: true` emits `profile.validation.frontmatterFieldForbidden`
502
+ when the named field is present, regardless of value. When `valueType: "string"`
503
+ is combined with `equals` or `nonBlank`, a non-string value emits only
504
+ `profile.validation.frontmatterFieldTypeMismatch` rather than an additional
505
+ string-predicate diagnostic. Diagnostic messages do not include frontmatter
506
+ values.
471
507
 
472
508
  `text` must include `contains` or a non-empty `excludes` array.
473
509
  `textOccurrenceCount.count` is a finite number and counts non-overlapping
@@ -501,7 +537,8 @@ profile-supplied regular expressions, call `Date.parse`, perform locale
501
537
  parsing, or implement date ordering.
502
538
 
503
539
  Empty selector results produce `profile.validation.emptySelection` for exists,
504
- table, ID, reference, text, occurrence, text-length, and text-format assertions.
540
+ table, ID, reference, text, occurrence, text-length, text-format, and
541
+ tableColumnsExact assertions.
505
542
  `selectionCount` instead evaluates the empty selection as zero. Document-scoped
506
543
  required-section, required-frontmatter, and frontmatter-shape assertions
507
544
  evaluate against the document.
@@ -536,12 +573,15 @@ an unproven pass.
536
573
  | `profile.compile.unsupportedAssertion` | `error` | Parsed YAML or JSON-safe `assert` input contains an unsupported first-level assertion member that does not have unsupported-key precedence. |
537
574
  | `profile.compile.incompatibleSelectorAssertion` | `error` | A supported selector target is paired with an incompatible supported assertion. |
538
575
  | `profile.validation.emptySelection` | Rule severity | A rule cannot evaluate because its selector matches no applicable target. |
539
- | `profile.validation.assertionFailed` | Rule severity | A supported assertion evaluates and fails without a more specific diagnostic code, including selector-count bound failures, missing table columns, exact occurrence-count mismatches, text-length bound failures, and text-format `isoDate` failures. |
576
+ | `profile.validation.assertionFailed` | Rule severity | A supported assertion evaluates and fails without a more specific diagnostic code, including selector-count bound failures, missing table columns, exact table-header mismatches, exact occurrence-count mismatches, text-length bound failures, and text-format `isoDate` failures. |
540
577
  | `profile.validation.duplicateId` | Rule severity | An `ids.unique` assertion finds repeated IDs. |
541
578
  | `profile.validation.frontmatterForbidden` | Rule severity | A `frontmatterShape` assertion with `presence: "forbidden"` finds present frontmatter. |
579
+ | `profile.validation.frontmatterFieldBlank` | Rule severity | A frontmatter field configured with `nonBlank: true` is not a non-blank string. |
542
580
  | `profile.validation.frontmatterFieldEmpty` | Rule severity | A frontmatter field configured with `nonEmpty: true` is not a non-empty string. |
581
+ | `profile.validation.frontmatterFieldForbidden` | Rule severity | A frontmatter field configured with `forbidden: true` is present. |
543
582
  | `profile.validation.frontmatterFieldMissing` | Rule severity | A required frontmatter field is absent. |
544
583
  | `profile.validation.frontmatterFieldTypeMismatch` | Rule severity | A frontmatter field value does not match the configured `frontmatterShape.fields[].valueType` without coercion. |
584
+ | `profile.validation.frontmatterFieldValueMismatch` | Rule severity | A frontmatter field configured with `equals` does not exactly match the configured string without coercion. |
545
585
  | `profile.validation.frontmatterMissing` | Rule severity | A `frontmatterShape` assertion with `presence: "required"` finds absent frontmatter. |
546
586
  | `profile.validation.idCountTooHigh` | Rule severity | Unique ID count after filtering is higher than `ids.maxCount`. |
547
587
  | `profile.validation.idCountTooLow` | Rule severity | Unique ID count after filtering is lower than `ids.minCount`. |
@@ -956,6 +996,26 @@ rules:
956
996
  max: 1
957
997
  ```
958
998
 
999
+ V2 exact table-column profile:
1000
+
1001
+ ```yaml
1002
+ syntaxVersion: markdown-engine.validation@v2
1003
+ rules:
1004
+ - id: task-control.columns
1005
+ select:
1006
+ target: table
1007
+ assert:
1008
+ tableColumnsExact:
1009
+ columns:
1010
+ - Contract state
1011
+ - Execution route
1012
+ - State rationale
1013
+ ```
1014
+
1015
+ The assertion compares the complete normalized header sequence. It is stricter
1016
+ than `tableColumnsRequired`, which continues to accept ordered-subsequence
1017
+ matches with unrelated columns before, between, or after required names.
1018
+
959
1019
  ### OKF v0.1 Hard-Validation Profile Composition
960
1020
 
961
1021
  `markdown-engine` does not ship a built-in OKF validator. Consumers that want to
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jasonbelmonti/markdown-engine",
3
- "version": "3.3.0",
3
+ "version": "3.5.0",
4
4
  "description": "Deterministic Markdown parsing and validation engine package.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -71,7 +71,7 @@
71
71
  "test:validation:profile": "npm run build && vitest run tests/declarative-validation-profile.test.ts \"--exclude=.worktrees/**\"",
72
72
  "test:validation:compiler": "npm run build && vitest run tests/declarative-validation-compiler.test.ts \"--exclude=.worktrees/**\"",
73
73
  "test:validation:selectors": "npm run build && vitest run tests/declarative-validation-selectors.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-selection-count.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
+ "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-table-columns-exact.test.ts tests/declarative-validation-source-assertion-coverage.test.ts tests/declarative-validation-source-length.test.ts tests/declarative-validation-selection-count.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/**\"",
75
75
  "test:validation:diagnostics": "npm run build && vitest run tests/declarative-validation-diagnostics.test.ts \"--exclude=.worktrees/**\"",
76
76
  "test:validation:cli": "npm run build && vitest run tests/declarative-validation-cli.test.ts \"--exclude=.worktrees/**\"",
77
77
  "test:validation:examples": "npm run build && vitest run tests/declarative-validation-examples.test.ts \"--exclude=.worktrees/**\"",
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env sh
2
2
  set -eu
3
3
 
4
- VERSION="3.3.0"
4
+ VERSION="3.5.0"
5
5
  PACKAGE="@jasonbelmonti/markdown-engine"
6
- EXPECTED_SHA256="367455824fde63074afe896510fd2133690bb3c06222257e579e575d59105768"
6
+ EXPECTED_SHA256="4a4e9ae7d9ff4797c96a7483555bcba7cbec5852d330c6c50dc0ef8fb2e5d933"
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}"