@jasonbelmonti/markdown-engine 3.4.0 → 3.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +10 -10
  3. package/dist/declarative-validation/assertions/frontmatter-shape.js +45 -5
  4. package/dist/declarative-validation/assertions/frontmatter-shape.js.map +1 -1
  5. package/dist/declarative-validation/assertions/id-targets.d.ts +2 -0
  6. package/dist/declarative-validation/assertions/id-targets.d.ts.map +1 -1
  7. package/dist/declarative-validation/assertions/id-targets.js.map +1 -1
  8. package/dist/declarative-validation/assertions/table-column-coverage.d.ts.map +1 -1
  9. package/dist/declarative-validation/assertions/table-column-coverage.js +8 -10
  10. package/dist/declarative-validation/assertions/table-column-coverage.js.map +1 -1
  11. package/dist/declarative-validation/assertions/text.d.ts.map +1 -1
  12. package/dist/declarative-validation/assertions/text.js +5 -0
  13. package/dist/declarative-validation/assertions/text.js.map +1 -1
  14. package/dist/declarative-validation/assertions/visible-text.d.ts +5 -0
  15. package/dist/declarative-validation/assertions/visible-text.d.ts.map +1 -0
  16. package/dist/declarative-validation/assertions/visible-text.js +42 -0
  17. package/dist/declarative-validation/assertions/visible-text.js.map +1 -0
  18. package/dist/declarative-validation/compiler/assertion-builders.js +11 -2
  19. package/dist/declarative-validation/compiler/assertion-builders.js.map +1 -1
  20. package/dist/declarative-validation/compiler/assertion-shapes.d.ts.map +1 -1
  21. package/dist/declarative-validation/compiler/assertion-shapes.js +20 -4
  22. package/dist/declarative-validation/compiler/assertion-shapes.js.map +1 -1
  23. package/dist/declarative-validation/compiler/plan.d.ts +4 -1
  24. package/dist/declarative-validation/compiler/plan.d.ts.map +1 -1
  25. package/dist/declarative-validation/profile/assertion-schema.d.ts.map +1 -1
  26. package/dist/declarative-validation/profile/assertion-schema.js +21 -6
  27. package/dist/declarative-validation/profile/assertion-schema.js.map +1 -1
  28. package/dist/declarative-validation/profile/cell-predicate-schema.d.ts +4 -0
  29. package/dist/declarative-validation/profile/cell-predicate-schema.d.ts.map +1 -0
  30. package/dist/declarative-validation/profile/cell-predicate-schema.js +29 -0
  31. package/dist/declarative-validation/profile/cell-predicate-schema.js.map +1 -0
  32. package/dist/declarative-validation/profile/frontmatter-shape-schema.js +52 -7
  33. package/dist/declarative-validation/profile/frontmatter-shape-schema.js.map +1 -1
  34. package/dist/declarative-validation/profile/index.d.ts +17 -0
  35. package/dist/declarative-validation/profile/index.d.ts.map +1 -1
  36. package/dist/declarative-validation/profile/selector-schema.d.ts.map +1 -1
  37. package/dist/declarative-validation/profile/selector-schema.js +1 -23
  38. package/dist/declarative-validation/profile/selector-schema.js.map +1 -1
  39. package/dist/declarative-validation/selectors/table-targets.d.ts +2 -1
  40. package/dist/declarative-validation/selectors/table-targets.d.ts.map +1 -1
  41. package/dist/declarative-validation/selectors/table-targets.js +13 -5
  42. package/dist/declarative-validation/selectors/table-targets.js.map +1 -1
  43. package/dist/internal/package-version.d.ts +1 -1
  44. package/dist/internal/package-version.js +1 -1
  45. package/dist-bundled/markdown-engine-cli.mjs +571 -351
  46. package/docs/contracts/declarative-validation.md +59 -7
  47. package/package.json +2 -2
  48. package/scripts/install-markdown-engine-cli.sh +2 -2
@@ -323,7 +323,9 @@ interface DeclarativeAssertion {
323
323
  mustAppearIn: readonly string[];
324
324
  };
325
325
  tableColumnCoverage?: {
326
+ allowEmptySource?: boolean;
326
327
  source: {
328
+ rowWhere?: { column: string; equals?: string; includes?: string };
327
329
  section: string;
328
330
  column: string;
329
331
  prefix?: string;
@@ -343,9 +345,13 @@ interface DeclarativeAssertion {
343
345
  required?: true;
344
346
  valueType?: "string" | "number" | "boolean" | "array" | "object" | "null";
345
347
  nonEmpty?: true;
348
+ equals?: string;
349
+ nonBlank?: true;
350
+ forbidden?: true;
346
351
  }[];
347
352
  };
348
353
  text?: {
354
+ nonBlank?: true; // v2 only
349
355
  contains?: string;
350
356
  excludes?: readonly string[];
351
357
  };
@@ -458,18 +464,39 @@ target section do not satisfy coverage. Missing target sections, missing target
458
464
  columns, and missing target-column IDs emit deterministic validation diagnostics
459
465
  source-grounded to the source ID when source evidence is available.
460
466
 
467
+ The opt-in `source.rowWhere` filter uses the same closed sibling-cell predicate
468
+ as the `tableCell` selector: a non-empty `column` plus `equals`, `includes`, or
469
+ both. Both predicates must match when both are present. Matching uses normalized,
470
+ case-sensitive cell text before ID extraction; `source.caseSensitive` affects ID
471
+ comparison only. Rows marked optional can therefore be excluded without encoding
472
+ consumer vocabulary in the engine. A missing predicate column is unresolved,
473
+ not a resolved empty source.
474
+
475
+ `allowEmptySource` is an optional boolean. Absent or `false` preserves the existing
476
+ `profile.validation.emptySelection` failure when no source IDs are extracted.
477
+ `true` permits zero extracted IDs only when the source section and column resolve
478
+ (including the predicate column when configured). With no source IDs there are no
479
+ target obligations; target resolution is not evaluated. A missing source structure
480
+ still fails. Pair this policy with consumer shape rules when tables or rows are
481
+ mandatory. This option also permits prose sentinels in reverse declared-ID checks.
482
+
461
483
  For v2 profiles, `frontmatterShape` is admitted as a flat-rule schema and
462
484
  internal compiled-plan assertion. It is compatible only with a `document`
463
485
  selector because frontmatter is document metadata. `presence` is optional and
464
486
  must be exactly `"required"` or `"forbidden"` when provided. `fields` is an
465
487
  optional non-empty array of field constraints. Each field constraint has a
466
488
  required non-empty `field` name and must include at least one effective
467
- constraint: `required: true`, `valueType`, or `nonEmpty: true`. Field names
468
- within one `frontmatterShape.fields` array must be unique. `valueType` must be
489
+ constraint: `required: true`, `valueType`, `nonEmpty: true`, `equals`,
490
+ `nonBlank: true`, or `forbidden: true`. Field names within one
491
+ `frontmatterShape.fields` array must be unique. `valueType` must be
469
492
  one of `"string"`, `"number"`, `"boolean"`, `"array"`, `"object"`, or `"null"`.
470
- `nonEmpty: true` is a string predicate; when it is combined with `valueType`,
471
- `valueType` must be `"string"`. `presence: "forbidden"` cannot be combined with
472
- `fields`.
493
+ `nonEmpty: true`, `equals`, and `nonBlank: true` are string predicates; when
494
+ any is combined with `valueType`, `valueType` must be `"string"`. `equals`
495
+ accepts any string, including the empty string, and compares it exactly.
496
+ `nonBlank: true` requires a string whose JavaScript `trim()` result is non-empty.
497
+ `forbidden: true` requires the named field to be absent and cannot be combined
498
+ with `required`, `valueType`, `nonEmpty`, `equals`, or `nonBlank`.
499
+ `presence: "forbidden"` cannot be combined with `fields`.
473
500
 
474
501
  Runtime evaluation treats frontmatter as present when
475
502
  `document.frontmatter !== undefined`. `presence: "required"` fails absent
@@ -486,8 +513,29 @@ requires a present field value to be a non-empty string and emits
486
513
  `profile.validation.frontmatterFieldEmpty` when that predicate fails. When
487
514
  `valueType: "string"` and `nonEmpty: true` are combined, a non-string value
488
515
  emits the type-mismatch diagnostic without a duplicate empty-string diagnostic.
489
-
490
- `text` must include `contains` or a non-empty `excludes` array.
516
+ `equals` compares present strings exactly without coercion and emits
517
+ `profile.validation.frontmatterFieldValueMismatch` on mismatch. `nonBlank: true`
518
+ uses JavaScript `trim()` without coercion and emits
519
+ `profile.validation.frontmatterFieldBlank` when its field is not a non-blank
520
+ string. `forbidden: true` emits `profile.validation.frontmatterFieldForbidden`
521
+ when the named field is present, regardless of value. When `valueType: "string"`
522
+ is combined with `equals` or `nonBlank`, a non-string value emits only
523
+ `profile.validation.frontmatterFieldTypeMismatch` rather than an additional
524
+ string-predicate diagnostic. Diagnostic messages do not include frontmatter
525
+ values.
526
+
527
+ `text` must include `contains`, a non-empty `excludes` array, or (v2 only)
528
+ `nonBlank: true`. `nonBlank` cannot be `false`. It requires at least one character
529
+ after JavaScript `trim()` on normalized node text excluding raw `html`, `definition`,
530
+ and `yaml` nodes. Comments, empty HTML tags, and decoded whitespace entities cannot
531
+ satisfy it. Markdown emphasis, link labels, literal code, and escaped angle brackets
532
+ retain their text. Each selected target is checked independently; an empty selector
533
+ still fails. Section selection includes its heading and direct body, as with ordinary
534
+ section text. Original source ranges are optional; the normalized nodes provide the
535
+ content. This is a structural textual-content check, not CSS visibility evaluation
536
+ or browser rendering. It does not evaluate external evidence, freshness, truth,
537
+ or implementation acceptance. Existing `contains`, `excludes`, and `textLength`
538
+ semantics are unchanged, even when combined with `nonBlank`.
491
539
  `textOccurrenceCount.count` is a finite number and counts non-overlapping
492
540
  literal occurrences per selected target.
493
541
  `textLength` must include `min`, `max`, or both. Bounds are non-negative
@@ -558,9 +606,12 @@ an unproven pass.
558
606
  | `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. |
559
607
  | `profile.validation.duplicateId` | Rule severity | An `ids.unique` assertion finds repeated IDs. |
560
608
  | `profile.validation.frontmatterForbidden` | Rule severity | A `frontmatterShape` assertion with `presence: "forbidden"` finds present frontmatter. |
609
+ | `profile.validation.frontmatterFieldBlank` | Rule severity | A frontmatter field configured with `nonBlank: true` is not a non-blank string. |
561
610
  | `profile.validation.frontmatterFieldEmpty` | Rule severity | A frontmatter field configured with `nonEmpty: true` is not a non-empty string. |
611
+ | `profile.validation.frontmatterFieldForbidden` | Rule severity | A frontmatter field configured with `forbidden: true` is present. |
562
612
  | `profile.validation.frontmatterFieldMissing` | Rule severity | A required frontmatter field is absent. |
563
613
  | `profile.validation.frontmatterFieldTypeMismatch` | Rule severity | A frontmatter field value does not match the configured `frontmatterShape.fields[].valueType` without coercion. |
614
+ | `profile.validation.frontmatterFieldValueMismatch` | Rule severity | A frontmatter field configured with `equals` does not exactly match the configured string without coercion. |
564
615
  | `profile.validation.frontmatterMissing` | Rule severity | A `frontmatterShape` assertion with `presence: "required"` finds absent frontmatter. |
565
616
  | `profile.validation.idCountTooHigh` | Rule severity | Unique ID count after filtering is higher than `ids.maxCount`. |
566
617
  | `profile.validation.idCountTooLow` | Rule severity | Unique ID count after filtering is lower than `ids.minCount`. |
@@ -571,6 +622,7 @@ an unproven pass.
571
622
  | `profile.validation.tableColumnCoverageIdMissing` | Rule severity | A source ID is absent from the configured target table column. |
572
623
  | `profile.validation.tableColumnCoverageTargetColumnMissing` | Rule severity | The configured target table column cannot be resolved. |
573
624
  | `profile.validation.tableColumnCoverageTargetSectionMissing` | Rule severity | The configured target section cannot be resolved. |
625
+ | `profile.validation.textBlank` | Rule severity | A target configured with `text.nonBlank` lacks non-whitespace text outside raw HTML. |
574
626
  | `profile.validation.textExcluded` | Rule severity | A selected target contains forbidden literal text. |
575
627
  | `profile.validation.textMissing` | Rule severity | A selected target lacks required literal text from a `text.contains` assertion. |
576
628
  | `profile.validation.assertionUnsupported` | `error` | A compiled assertion or compiled assertion feature has no evaluator implementation; this is an internal safety diagnostic. |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jasonbelmonti/markdown-engine",
3
- "version": "3.4.0",
3
+ "version": "3.6.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-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/**\"",
74
+ "test:validation:assertions": "npm run build && vitest run tests/declarative-validation-assertions.test.ts tests/declarative-validation-filtered-coverage.test.ts tests/declarative-validation-visible-text.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.4.0"
4
+ VERSION="3.6.0"
5
5
  PACKAGE="@jasonbelmonti/markdown-engine"
6
- EXPECTED_SHA256="41225925904c038c05392b19e7203bd723d1cbaaa47b48d96648fb0275d3d05b"
6
+ EXPECTED_SHA256="69b855d4d3623d7cdb91a3ed09631715dde8b96fc57f49864ae2c7c7c071ad83"
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}"