hyper-html-api 0.13.0 → 0.15.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.
- package/README.md +4 -1
- package/conformance/MANIFEST.json +2 -2
- package/conformance/cases/all-matches-read-quoted.expected.json +3 -0
- package/conformance/cases/all-matches-read-quoted.html +5 -0
- package/conformance/cases/all-matches-read-quoted.meta +7 -0
- package/conformance/cases/all-matches-read-quoted.parsed.json +3 -0
- package/conformance/cases/all-matches-read.expected.json +3 -0
- package/conformance/cases/all-matches-read.html +6 -0
- package/conformance/cases/all-matches-read.meta +7 -0
- package/conformance/cases/all-matches-read.parsed.json +3 -0
- package/conformance/cases/codec-form-nested.error.json +5 -0
- package/conformance/cases/codec-form-nested.html +4 -0
- package/conformance/cases/codec-form-nested.meta +6 -0
- package/conformance/cases/codec-form-nested.parsed.json +5 -0
- package/conformance/cases/codec-form-star.error.json +5 -0
- package/conformance/cases/codec-form-star.html +4 -0
- package/conformance/cases/codec-form-star.meta +6 -0
- package/conformance/cases/codec-form-star.parsed.json +3 -0
- package/conformance/cases/codec-form-under-v1.error.json +5 -0
- package/conformance/cases/codec-form-under-v1.html +4 -0
- package/conformance/cases/codec-form-under-v1.meta +6 -0
- package/conformance/cases/codec-form-under-v1.parsed.json +3 -0
- package/conformance/cases/codec-unknown-name.error.json +5 -0
- package/conformance/cases/codec-unknown-name.html +4 -0
- package/conformance/cases/codec-unknown-name.meta +6 -0
- package/conformance/cases/codec-unknown-name.parsed.json +3 -0
- package/conformance/cases/tag-version-2-plain.expected.json +7 -0
- package/conformance/cases/tag-version-2-plain.html +5 -0
- package/conformance/cases/tag-version-2-plain.meta +6 -0
- package/conformance/cases/tag-version-2-plain.parsed.json +4 -0
- package/conformance/cases/tag-wrong-version.error.json +1 -1
- package/conformance/cases/tag-wrong-version.html +1 -1
- package/conformance/cases/write-all-matches-attr.after.html +1 -0
- package/conformance/cases/write-all-matches-attr.after.json +3 -0
- package/conformance/cases/write-all-matches-attr.data.json +1 -0
- package/conformance/cases/write-all-matches-attr.html +1 -0
- package/conformance/cases/write-all-matches-attr.meta +6 -0
- package/conformance/cases/write-all-matches-attr.write.json +4 -0
- package/conformance/cases/write-all-matches-group.after.html +1 -0
- package/conformance/cases/write-all-matches-group.after.json +3 -0
- package/conformance/cases/write-all-matches-group.data.json +1 -0
- package/conformance/cases/write-all-matches-group.html +1 -0
- package/conformance/cases/write-all-matches-group.meta +6 -0
- package/conformance/cases/write-all-matches-group.write.json +4 -0
- package/conformance/cases/write-all-matches-text.after.html +1 -0
- package/conformance/cases/write-all-matches-text.after.json +3 -0
- package/conformance/cases/write-all-matches-text.data.json +1 -0
- package/conformance/cases/write-all-matches-text.html +1 -0
- package/conformance/cases/write-all-matches-text.meta +6 -0
- package/conformance/cases/write-all-matches-text.write.json +4 -0
- package/conformance/cases/write-all-matches-unmatched.data.json +1 -0
- package/conformance/cases/write-all-matches-unmatched.error.json +14 -0
- package/conformance/cases/write-all-matches-unmatched.html +1 -0
- package/conformance/cases/write-all-matches-unmatched.meta +6 -0
- package/conformance/sheet/body-data-write.body.json +3 -0
- package/conformance/sheet/body-data-write.data.json +3 -0
- package/conformance/sheet/body-data-write.html +25 -0
- package/conformance/sheet/body-data-write.meta +5 -0
- package/conformance/sheet/body-patch-key-in-rules.body.json +3 -0
- package/conformance/sheet/body-patch-key-in-rules.data.json +13 -0
- package/conformance/sheet/body-patch-key-in-rules.html +18 -0
- package/conformance/sheet/body-patch-key-in-rules.meta +5 -0
- package/conformance/sheet/body-patch-not-array.body.json +3 -0
- package/conformance/sheet/body-patch-not-array.data.json +3 -0
- package/conformance/sheet/body-patch-not-array.html +25 -0
- package/conformance/sheet/body-patch-not-array.meta +5 -0
- package/conformance/sheet/body-patch-on-sheet.body.json +3 -0
- package/conformance/sheet/body-patch-on-sheet.data.json +14 -0
- package/conformance/sheet/body-patch-on-sheet.html +25 -0
- package/conformance/sheet/body-patch-on-sheet.meta +5 -0
- package/conformance/sheet/body-patch-version-1.body.json +3 -0
- package/conformance/sheet/body-patch-version-1.data.json +13 -0
- package/conformance/sheet/body-patch-version-1.html +4 -0
- package/conformance/sheet/body-patch-version-1.meta +5 -0
- package/conformance/sheet/body-patch-with-other-key.body.json +3 -0
- package/conformance/sheet/body-patch-with-other-key.data.json +14 -0
- package/conformance/sheet/body-patch-with-other-key.html +25 -0
- package/conformance/sheet/body-patch-with-other-key.meta +5 -0
- package/conformance/sheet/body-patch-without-operation-id.body.json +3 -0
- package/conformance/sheet/body-patch-without-operation-id.data.json +13 -0
- package/conformance/sheet/body-patch-without-operation-id.html +25 -0
- package/conformance/sheet/body-patch-without-operation-id.meta +5 -0
- package/conformance/sheet/params-bad-range.error.json +6 -0
- package/conformance/sheet/params-bad-range.html +25 -0
- package/conformance/sheet/params-bad-range.meta +6 -0
- package/conformance/sheet/params-bad-render.error.json +6 -0
- package/conformance/sheet/params-bad-render.html +25 -0
- package/conformance/sheet/params-bad-render.meta +6 -0
- package/conformance/sheet/params-bad-shape.error.json +6 -0
- package/conformance/sheet/params-bad-shape.html +25 -0
- package/conformance/sheet/params-bad-shape.meta +6 -0
- package/conformance/sheet/params-cache-buster.expected.json +128 -0
- package/conformance/sheet/params-cache-buster.html +25 -0
- package/conformance/sheet/params-cache-buster.meta +6 -0
- package/conformance/sheet/params-data-alone.expected.json +128 -0
- package/conformance/sheet/params-data-alone.html +25 -0
- package/conformance/sheet/params-data-alone.meta +6 -0
- package/conformance/sheet/params-empty.error.json +6 -0
- package/conformance/sheet/params-empty.html +25 -0
- package/conformance/sheet/params-empty.meta +6 -0
- package/conformance/sheet/params-index.expected.json +19 -0
- package/conformance/sheet/params-index.html +25 -0
- package/conformance/sheet/params-index.meta +6 -0
- package/conformance/sheet/params-none.expected.json +128 -0
- package/conformance/sheet/params-none.html +25 -0
- package/conformance/sheet/params-none.meta +6 -0
- package/conformance/sheet/params-range.expected.json +17 -0
- package/conformance/sheet/params-range.html +25 -0
- package/conformance/sheet/params-range.meta +6 -0
- package/conformance/sheet/params-render-without-range.error.json +6 -0
- package/conformance/sheet/params-render-without-range.html +25 -0
- package/conformance/sheet/params-render-without-range.meta +6 -0
- package/conformance/sheet/params-repeated.error.json +6 -0
- package/conformance/sheet/params-repeated.html +25 -0
- package/conformance/sheet/params-repeated.meta +6 -0
- package/conformance/sheet/params-unknown-sheet.error.json +6 -0
- package/conformance/sheet/params-unknown-sheet.html +25 -0
- package/conformance/sheet/params-unknown-sheet.meta +6 -0
- package/conformance/sheet/params-with-data.error.json +6 -0
- package/conformance/sheet/params-with-data.html +25 -0
- package/conformance/sheet/params-with-data.meta +6 -0
- package/conformance/sheet/sheet-contract-cells.expected.json +57 -0
- package/conformance/sheet/sheet-contract-cells.html +10 -0
- package/conformance/sheet/sheet-contract-cells.meta +6 -0
- package/conformance/sheet/sheet-contract-formulas.expected.json +29 -0
- package/conformance/sheet/sheet-contract-formulas.html +10 -0
- package/conformance/sheet/sheet-contract-formulas.meta +6 -0
- package/conformance/sheet/sheet-contract-values.expected.json +29 -0
- package/conformance/sheet/sheet-contract-values.html +10 -0
- package/conformance/sheet/sheet-contract-values.meta +6 -0
- package/conformance/sheet/sheet-contract-whole.expected.json +59 -0
- package/conformance/sheet/sheet-contract-whole.html +10 -0
- package/conformance/sheet/sheet-contract-whole.meta +5 -0
- package/conformance/sheet/sheet-hostile-01.expected.json +18 -0
- package/conformance/sheet/sheet-hostile-01.html +6 -0
- package/conformance/sheet/sheet-hostile-01.meta +5 -0
- package/conformance/sheet/sheet-hostile-02.expected.json +18 -0
- package/conformance/sheet/sheet-hostile-02.html +6 -0
- package/conformance/sheet/sheet-hostile-02.meta +5 -0
- package/conformance/sheet/sheet-hostile-03.expected.json +18 -0
- package/conformance/sheet/sheet-hostile-03.html +6 -0
- package/conformance/sheet/sheet-hostile-03.meta +5 -0
- package/conformance/sheet/sheet-hostile-04.expected.json +18 -0
- package/conformance/sheet/sheet-hostile-04.html +6 -0
- package/conformance/sheet/sheet-hostile-04.meta +5 -0
- package/conformance/sheet/sheet-hostile-05.expected.json +18 -0
- package/conformance/sheet/sheet-hostile-05.html +6 -0
- package/conformance/sheet/sheet-hostile-05.meta +5 -0
- package/conformance/sheet/sheet-hostile-06.expected.json +21 -0
- package/conformance/sheet/sheet-hostile-06.html +6 -0
- package/conformance/sheet/sheet-hostile-06.meta +5 -0
- package/conformance/sheet/sheet-hostile-07.expected.json +21 -0
- package/conformance/sheet/sheet-hostile-07.html +6 -0
- package/conformance/sheet/sheet-hostile-07.meta +5 -0
- package/conformance/sheet/sheet-hostile-08.expected.json +21 -0
- package/conformance/sheet/sheet-hostile-08.html +6 -0
- package/conformance/sheet/sheet-hostile-08.meta +5 -0
- package/conformance/sheet/sheet-hostile-09.expected.json +21 -0
- package/conformance/sheet/sheet-hostile-09.html +6 -0
- package/conformance/sheet/sheet-hostile-09.meta +5 -0
- package/conformance/sheet/sheet-hostile-10.expected.json +21 -0
- package/conformance/sheet/sheet-hostile-10.html +6 -0
- package/conformance/sheet/sheet-hostile-10.meta +5 -0
- package/conformance/sheet/sheet-hostile-11.expected.json +22 -0
- package/conformance/sheet/sheet-hostile-11.html +6 -0
- package/conformance/sheet/sheet-hostile-11.meta +5 -0
- package/conformance/sheet/sheet-hostile-12.expected.json +18 -0
- package/conformance/sheet/sheet-hostile-12.html +6 -0
- package/conformance/sheet/sheet-hostile-12.meta +5 -0
- package/conformance/sheet/sheet-hostile-13.expected.json +18 -0
- package/conformance/sheet/sheet-hostile-13.html +6 -0
- package/conformance/sheet/sheet-hostile-13.meta +5 -0
- package/conformance/sheet/sheet-hostile-14.expected.json +21 -0
- package/conformance/sheet/sheet-hostile-14.html +6 -0
- package/conformance/sheet/sheet-hostile-14.meta +5 -0
- package/conformance/sheet/sheet-hostile-15.expected.json +18 -0
- package/conformance/sheet/sheet-hostile-15.html +0 -0
- package/conformance/sheet/sheet-hostile-15.meta +5 -0
- package/conformance/sheet/sheet-hostile-16.expected.json +18 -0
- package/conformance/sheet/sheet-hostile-16.html +6 -0
- package/conformance/sheet/sheet-hostile-16.meta +5 -0
- package/conformance/sheet/sheet-hostile-17.expected.json +18 -0
- package/conformance/sheet/sheet-hostile-17.html +6 -0
- package/conformance/sheet/sheet-hostile-17.meta +5 -0
- package/conformance/sheet/sheet-hostile-18.expected.json +18 -0
- package/conformance/sheet/sheet-hostile-18.html +6 -0
- package/conformance/sheet/sheet-hostile-18.meta +5 -0
- package/conformance/sheet/sheet-hostile-19.expected.json +18 -0
- package/conformance/sheet/sheet-hostile-19.html +6 -0
- package/conformance/sheet/sheet-hostile-19.meta +5 -0
- package/conformance/sheet/sheet-hostile-20.expected.json +16 -0
- package/conformance/sheet/sheet-hostile-20.html +6 -0
- package/conformance/sheet/sheet-hostile-20.meta +5 -0
- package/conformance/sheet/sheet-hostile-21.expected.json +18 -0
- package/conformance/sheet/sheet-hostile-21.html +6 -0
- package/conformance/sheet/sheet-hostile-21.meta +5 -0
- package/conformance/sheet/sheet-hostile-22.expected.json +21 -0
- package/conformance/sheet/sheet-hostile-22.html +6 -0
- package/conformance/sheet/sheet-hostile-22.meta +5 -0
- package/conformance/sheet/sheet-hostile-23.expected.json +18 -0
- package/conformance/sheet/sheet-hostile-23.html +6 -0
- package/conformance/sheet/sheet-hostile-23.meta +5 -0
- package/conformance/sheet/sheet-hostile-24.expected.json +18 -0
- package/conformance/sheet/sheet-hostile-24.html +6 -0
- package/conformance/sheet/sheet-hostile-24.meta +5 -0
- package/conformance/sheet/sheet-hostile-25.expected.json +18 -0
- package/conformance/sheet/sheet-hostile-25.html +6 -0
- package/conformance/sheet/sheet-hostile-25.meta +5 -0
- package/conformance/sheet/sheet-hostile-26.expected.json +18 -0
- package/conformance/sheet/sheet-hostile-26.html +6 -0
- package/conformance/sheet/sheet-hostile-26.meta +5 -0
- package/conformance/sheet/sheet-hostile-27.expected.json +24 -0
- package/conformance/sheet/sheet-hostile-27.html +7 -0
- package/conformance/sheet/sheet-hostile-27.meta +5 -0
- package/conformance/sheet/sheet-hostile-28.expected.json +18 -0
- package/conformance/sheet/sheet-hostile-28.html +6 -0
- package/conformance/sheet/sheet-hostile-28.meta +5 -0
- package/conformance/sheet/sheet-hostile-29.expected.json +18 -0
- package/conformance/sheet/sheet-hostile-29.html +6 -0
- package/conformance/sheet/sheet-hostile-29.meta +5 -0
- package/conformance/sheet/sheet-hostile-30.expected.json +18 -0
- package/conformance/sheet/sheet-hostile-30.html +6 -0
- package/conformance/sheet/sheet-hostile-30.meta +5 -0
- package/conformance/sheet/sheet-hostile-31.expected.json +19 -0
- package/conformance/sheet/sheet-hostile-31.html +6 -0
- package/conformance/sheet/sheet-hostile-31.meta +5 -0
- package/conformance/sheet/sheet-hostile-32.expected.json +24 -0
- package/conformance/sheet/sheet-hostile-32.html +6 -0
- package/conformance/sheet/sheet-hostile-32.meta +5 -0
- package/conformance/sheet/sheet-hostile-33.expected.json +18 -0
- package/conformance/sheet/sheet-hostile-33.html +6 -0
- package/conformance/sheet/sheet-hostile-33.meta +5 -0
- package/conformance/sheet/sheet-hostile-34.expected.json +24 -0
- package/conformance/sheet/sheet-hostile-34.html +8 -0
- package/conformance/sheet/sheet-hostile-34.meta +5 -0
- package/conformance/sheet/sheet-hostile-35.expected.json +24 -0
- package/conformance/sheet/sheet-hostile-35.html +8 -0
- package/conformance/sheet/sheet-hostile-35.meta +5 -0
- package/conformance/sheet/sheet-patch-append.after.html +27 -0
- package/conformance/sheet/sheet-patch-append.after.json +143 -0
- package/conformance/sheet/sheet-patch-append.data.json +15 -0
- package/conformance/sheet/sheet-patch-append.html +25 -0
- package/conformance/sheet/sheet-patch-append.meta +5 -0
- package/conformance/sheet/sheet-patch-append.write.json +7 -0
- package/conformance/sheet/sheet-patch-bad-range.data.json +11 -0
- package/conformance/sheet/sheet-patch-bad-range.error.json +6 -0
- package/conformance/sheet/sheet-patch-bad-range.html +25 -0
- package/conformance/sheet/sheet-patch-bad-range.meta +5 -0
- package/conformance/sheet/sheet-patch-bad-value.data.json +13 -0
- package/conformance/sheet/sheet-patch-bad-value.error.json +6 -0
- package/conformance/sheet/sheet-patch-bad-value.html +25 -0
- package/conformance/sheet/sheet-patch-bad-value.meta +5 -0
- package/conformance/sheet/sheet-patch-clean-tab-beside-edited.after.html +25 -0
- package/conformance/sheet/sheet-patch-clean-tab-beside-edited.after.json +129 -0
- package/conformance/sheet/sheet-patch-clean-tab-beside-edited.data.json +11 -0
- package/conformance/sheet/sheet-patch-clean-tab-beside-edited.html +25 -0
- package/conformance/sheet/sheet-patch-clean-tab-beside-edited.meta +5 -0
- package/conformance/sheet/sheet-patch-clean-tab-beside-edited.write.json +7 -0
- package/conformance/sheet/sheet-patch-covered-cell.data.json +10 -0
- package/conformance/sheet/sheet-patch-covered-cell.error.json +6 -0
- package/conformance/sheet/sheet-patch-covered-cell.html +10 -0
- package/conformance/sheet/sheet-patch-covered-cell.meta +5 -0
- package/conformance/sheet/sheet-patch-empty.data.json +1 -0
- package/conformance/sheet/sheet-patch-empty.error.json +6 -0
- package/conformance/sheet/sheet-patch-empty.html +25 -0
- package/conformance/sheet/sheet-patch-empty.meta +5 -0
- package/conformance/sheet/sheet-patch-fallback-route.after.html +24 -0
- package/conformance/sheet/sheet-patch-fallback-route.after.json +86 -0
- package/conformance/sheet/sheet-patch-fallback-route.data.json +10 -0
- package/conformance/sheet/sheet-patch-fallback-route.html +24 -0
- package/conformance/sheet/sheet-patch-fallback-route.meta +5 -0
- package/conformance/sheet/sheet-patch-fallback-route.write.json +7 -0
- package/conformance/sheet/sheet-patch-first-tab.after.html +25 -0
- package/conformance/sheet/sheet-patch-first-tab.after.json +129 -0
- package/conformance/sheet/sheet-patch-first-tab.data.json +10 -0
- package/conformance/sheet/sheet-patch-first-tab.html +25 -0
- package/conformance/sheet/sheet-patch-first-tab.meta +5 -0
- package/conformance/sheet/sheet-patch-first-tab.write.json +7 -0
- package/conformance/sheet/sheet-patch-formula.after.html +25 -0
- package/conformance/sheet/sheet-patch-formula.after.json +128 -0
- package/conformance/sheet/sheet-patch-formula.data.json +11 -0
- package/conformance/sheet/sheet-patch-formula.html +25 -0
- package/conformance/sheet/sheet-patch-formula.meta +5 -0
- package/conformance/sheet/sheet-patch-formula.write.json +7 -0
- package/conformance/sheet/sheet-patch-no-sheet-rule.data.json +10 -0
- package/conformance/sheet/sheet-patch-no-sheet-rule.error.json +6 -0
- package/conformance/sheet/sheet-patch-no-sheet-rule.html +18 -0
- package/conformance/sheet/sheet-patch-no-sheet-rule.meta +5 -0
- package/conformance/sheet/sheet-patch-no-values.data.json +6 -0
- package/conformance/sheet/sheet-patch-no-values.error.json +6 -0
- package/conformance/sheet/sheet-patch-no-values.html +25 -0
- package/conformance/sheet/sheet-patch-no-values.meta +5 -0
- package/conformance/sheet/sheet-patch-not-canonical.data.json +11 -0
- package/conformance/sheet/sheet-patch-not-canonical.error.json +6 -0
- package/conformance/sheet/sheet-patch-not-canonical.html +25 -0
- package/conformance/sheet/sheet-patch-not-canonical.meta +5 -0
- package/conformance/sheet/sheet-patch-outside-sheet.data.json +11 -0
- package/conformance/sheet/sheet-patch-outside-sheet.error.json +6 -0
- package/conformance/sheet/sheet-patch-outside-sheet.html +25 -0
- package/conformance/sheet/sheet-patch-outside-sheet.meta +5 -0
- package/conformance/sheet/sheet-patch-range.after.html +25 -0
- package/conformance/sheet/sheet-patch-range.after.json +126 -0
- package/conformance/sheet/sheet-patch-range.data.json +11 -0
- package/conformance/sheet/sheet-patch-range.html +25 -0
- package/conformance/sheet/sheet-patch-range.meta +5 -0
- package/conformance/sheet/sheet-patch-range.write.json +7 -0
- package/conformance/sheet/sheet-patch-two-tabs.after.html +25 -0
- package/conformance/sheet/sheet-patch-two-tabs.after.json +130 -0
- package/conformance/sheet/sheet-patch-two-tabs.data.json +20 -0
- package/conformance/sheet/sheet-patch-two-tabs.html +25 -0
- package/conformance/sheet/sheet-patch-two-tabs.meta +5 -0
- package/conformance/sheet/sheet-patch-two-tabs.write.json +7 -0
- package/conformance/sheet/sheet-patch-typed.after.html +25 -0
- package/conformance/sheet/sheet-patch-typed.after.json +121 -0
- package/conformance/sheet/sheet-patch-typed.data.json +13 -0
- package/conformance/sheet/sheet-patch-typed.html +25 -0
- package/conformance/sheet/sheet-patch-typed.meta +5 -0
- package/conformance/sheet/sheet-patch-typed.write.json +7 -0
- package/conformance/sheet/sheet-patch-unknown-sheet.data.json +11 -0
- package/conformance/sheet/sheet-patch-unknown-sheet.error.json +6 -0
- package/conformance/sheet/sheet-patch-unknown-sheet.html +25 -0
- package/conformance/sheet/sheet-patch-unknown-sheet.meta +5 -0
- package/conformance/sheet/sheet-read-bad-range.error.json +6 -0
- package/conformance/sheet/sheet-read-bad-range.html +25 -0
- package/conformance/sheet/sheet-read-bad-range.meta +6 -0
- package/conformance/sheet/sheet-read-bad-render.error.json +6 -0
- package/conformance/sheet/sheet-read-bad-render.html +25 -0
- package/conformance/sheet/sheet-read-bad-render.meta +6 -0
- package/conformance/sheet/sheet-read-digest.expected.json +91 -0
- package/conformance/sheet/sheet-read-digest.html +25 -0
- package/conformance/sheet/sheet-read-digest.meta +6 -0
- package/conformance/sheet/sheet-read-empty.expected.json +11 -0
- package/conformance/sheet/sheet-read-empty.html +6 -0
- package/conformance/sheet/sheet-read-empty.meta +5 -0
- package/conformance/sheet/sheet-read-escapes.expected.json +108 -0
- package/conformance/sheet/sheet-read-escapes.html +22 -0
- package/conformance/sheet/sheet-read-escapes.meta +5 -0
- package/conformance/sheet/sheet-read-formulas-formatted.expected.json +298 -0
- package/conformance/sheet/sheet-read-formulas-formatted.html +16 -0
- package/conformance/sheet/sheet-read-formulas-formatted.meta +5 -0
- package/conformance/sheet/sheet-read-grid-in-comment.expected.json +85 -0
- package/conformance/sheet/sheet-read-grid-in-comment.html +24 -0
- package/conformance/sheet/sheet-read-grid-in-comment.meta +5 -0
- package/conformance/sheet/sheet-read-grid-in-script.expected.json +85 -0
- package/conformance/sheet/sheet-read-grid-in-script.html +24 -0
- package/conformance/sheet/sheet-read-grid-in-script.meta +5 -0
- package/conformance/sheet/sheet-read-grid-in-template.expected.json +85 -0
- package/conformance/sheet/sheet-read-grid-in-template.html +24 -0
- package/conformance/sheet/sheet-read-grid-in-template.meta +5 -0
- package/conformance/sheet/sheet-read-grid-in-textarea.expected.json +85 -0
- package/conformance/sheet/sheet-read-grid-in-textarea.html +24 -0
- package/conformance/sheet/sheet-read-grid-in-textarea.meta +5 -0
- package/conformance/sheet/sheet-read-grid-outside-read-by-rule.expected.json +94 -0
- package/conformance/sheet/sheet-read-grid-outside-read-by-rule.html +24 -0
- package/conformance/sheet/sheet-read-grid-outside-read-by-rule.meta +5 -0
- package/conformance/sheet/sheet-read-grid-outside-sheet.expected.json +85 -0
- package/conformance/sheet/sheet-read-grid-outside-sheet.html +24 -0
- package/conformance/sheet/sheet-read-grid-outside-sheet.meta +5 -0
- package/conformance/sheet/sheet-read-hand-edited.expected.json +85 -0
- package/conformance/sheet/sheet-read-hand-edited.html +18 -0
- package/conformance/sheet/sheet-read-hand-edited.meta +5 -0
- package/conformance/sheet/sheet-read-index.expected.json +19 -0
- package/conformance/sheet/sheet-read-index.html +25 -0
- package/conformance/sheet/sheet-read-index.meta +6 -0
- package/conformance/sheet/sheet-read-merged.expected.json +59 -0
- package/conformance/sheet/sheet-read-merged.html +10 -0
- package/conformance/sheet/sheet-read-merged.meta +5 -0
- package/conformance/sheet/sheet-read-no-tag.error.json +5 -0
- package/conformance/sheet/sheet-read-no-tag.html +18 -0
- package/conformance/sheet/sheet-read-no-tag.meta +5 -0
- package/conformance/sheet/sheet-read-padded-name-index.expected.json +19 -0
- package/conformance/sheet/sheet-read-padded-name-index.html +25 -0
- package/conformance/sheet/sheet-read-padded-name-index.meta +6 -0
- package/conformance/sheet/sheet-read-padded-name-lookup.expected.json +83 -0
- package/conformance/sheet/sheet-read-padded-name-lookup.html +25 -0
- package/conformance/sheet/sheet-read-padded-name-lookup.meta +6 -0
- package/conformance/sheet/sheet-read-padded-name-trimmed-lookup.error.json +6 -0
- package/conformance/sheet/sheet-read-padded-name-trimmed-lookup.html +25 -0
- package/conformance/sheet/sheet-read-padded-name-trimmed-lookup.meta +6 -0
- package/conformance/sheet/sheet-read-padded-name.expected.json +128 -0
- package/conformance/sheet/sheet-read-padded-name.html +25 -0
- package/conformance/sheet/sheet-read-padded-name.meta +5 -0
- package/conformance/sheet/sheet-read-range-cells.expected.json +55 -0
- package/conformance/sheet/sheet-read-range-cells.html +25 -0
- package/conformance/sheet/sheet-read-range-cells.meta +6 -0
- package/conformance/sheet/sheet-read-range-first-tab.expected.json +12 -0
- package/conformance/sheet/sheet-read-range-first-tab.html +25 -0
- package/conformance/sheet/sheet-read-range-first-tab.meta +6 -0
- package/conformance/sheet/sheet-read-range-formulas.expected.json +24 -0
- package/conformance/sheet/sheet-read-range-formulas.html +25 -0
- package/conformance/sheet/sheet-read-range-formulas.meta +6 -0
- package/conformance/sheet/sheet-read-range-values.expected.json +24 -0
- package/conformance/sheet/sheet-read-range-values.html +25 -0
- package/conformance/sheet/sheet-read-range-values.meta +6 -0
- package/conformance/sheet/sheet-read-sheet-by-id.expected.json +46 -0
- package/conformance/sheet/sheet-read-sheet-by-id.html +25 -0
- package/conformance/sheet/sheet-read-sheet-by-id.meta +6 -0
- package/conformance/sheet/sheet-read-sheet-by-name.expected.json +46 -0
- package/conformance/sheet/sheet-read-sheet-by-name.html +25 -0
- package/conformance/sheet/sheet-read-sheet-by-name.meta +6 -0
- package/conformance/sheet/sheet-read-styled.expected.json +48 -0
- package/conformance/sheet/sheet-read-styled.html +10 -0
- package/conformance/sheet/sheet-read-styled.meta +5 -0
- package/conformance/sheet/sheet-read-tiny.expected.json +85 -0
- package/conformance/sheet/sheet-read-tiny.html +18 -0
- package/conformance/sheet/sheet-read-tiny.meta +5 -0
- package/conformance/sheet/sheet-read-two-tabs.expected.json +128 -0
- package/conformance/sheet/sheet-read-two-tabs.html +25 -0
- package/conformance/sheet/sheet-read-two-tabs.meta +5 -0
- package/conformance/sheet/sheet-read-unknown-sheet.error.json +6 -0
- package/conformance/sheet/sheet-read-unknown-sheet.html +25 -0
- package/conformance/sheet/sheet-read-unknown-sheet.meta +6 -0
- package/conformance/sheet/sheet-read-version-1-codec.error.json +5 -0
- package/conformance/sheet/sheet-read-version-1-codec.html +25 -0
- package/conformance/sheet/sheet-read-version-1-codec.meta +5 -0
- package/conformance/sheet/sheet-write-whole-refused.data.json +3 -0
- package/conformance/sheet/sheet-write-whole-refused.error.json +7 -0
- package/conformance/sheet/sheet-write-whole-refused.html +25 -0
- package/conformance/sheet/sheet-write-whole-refused.meta +5 -0
- package/conformance/sheet-guard-extraction.json +616 -0
- package/conformance/sheet-guard.json +32362 -0
- package/conformance/sheet-read-shapes.json +1 -0
- package/dist/hyper-html-api.engine.esm.js +1 -1
- package/dist/hyper-html-api.engine.min.js +1 -1
- package/dist/hyper-html-api.min.js +8 -8
- package/dist/hyper-html-api.upgrade.min.js +2 -2
- package/package.json +6 -3
- package/src/codecs/sheet.js +127 -0
- package/src/codecs/sheet.vendor.js +887 -0
- package/src/dom-api.js +20 -11
- package/src/engine/apply.js +17 -7
- package/src/engine/codecs.js +80 -0
- package/src/engine/errors.js +20 -1
- package/src/engine/extract.js +17 -3
- package/src/engine/index.js +9 -5
- package/src/engine/round-trip.js +24 -10
- package/src/engine/rule-syntax.js +29 -3
- package/src/engine/rules-tag.js +3 -2
- package/src/engine/write-plan.js +4 -3
- package/src/page.js +15 -5
- package/src/patch-document.js +122 -0
- package/src/read.js +163 -0
- package/src/site.js +164 -0
- package/src/upgrade/codec-carry.js +23 -0
- package/src/upgrade/extract-all.js +12 -3
- package/src/upgrade/migrate.js +31 -3
- package/src/upgrade/rules-tags.js +4 -4
- package/src/upgrade/shape-match.js +32 -13
- package/src/write.js +44 -5
package/src/dom-api.js
CHANGED
|
@@ -4,9 +4,12 @@ import domAdapter, { createDomAdapter } from './adapters/dom.js'
|
|
|
4
4
|
// The DOM-bound engine: every engine call pre-wired with the DOM adapter. Lives
|
|
5
5
|
// here (instead of inline in hyper-html-api.js) so both the full CDN bundle and
|
|
6
6
|
// the lean `data.js` entry share one definition.
|
|
7
|
+
// Calls default `codecs` to the ones registered with engine.codecs.register.
|
|
8
|
+
const withCodecs = (opts) => ({ codecs: engineMod.registeredCodecs(), ...opts })
|
|
9
|
+
|
|
7
10
|
export const engine = {
|
|
8
|
-
extract: (root, rules, opts = {}) => engineMod.extract(createDomAdapter(root, opts), root, rules, opts),
|
|
9
|
-
apply: (root, rules, data, opts = {}) => engineMod.apply(createDomAdapter(root, { ...opts, _write: true }), root, rules, data, opts),
|
|
11
|
+
extract: (root, rules, opts = {}) => engineMod.extract(createDomAdapter(root, opts), root, rules, withCodecs(opts)),
|
|
12
|
+
apply: (root, rules, data, opts = {}) => engineMod.apply(createDomAdapter(root, { ...opts, _write: true }), root, rules, data, withCodecs(opts)),
|
|
10
13
|
findRulesIn: (root, token) => engineMod.findRulesIn(domAdapter, root, token),
|
|
11
14
|
findRules: (root, source) => engineMod.resolveRules(domAdapter, root, source),
|
|
12
15
|
bind: (root, source, opts = {}) => {
|
|
@@ -15,12 +18,14 @@ export const engine = {
|
|
|
15
18
|
const what = typeof source === 'string' ? `data-rules-name~="${source}"` : 'the provided rules object'
|
|
16
19
|
throw new Error(`hyper-html-api: could not resolve rules for ${what}`)
|
|
17
20
|
}
|
|
21
|
+
const callOpts = withCodecs({ version: found.version, ...opts })
|
|
18
22
|
return {
|
|
19
23
|
...found,
|
|
20
|
-
get: () => engineMod.extract(createDomAdapter(root, opts), root, found.rules,
|
|
21
|
-
set: data => engineMod.apply(createDomAdapter(root, { ...opts, _write: true }), root, found.rules, data,
|
|
24
|
+
get: () => engineMod.extract(createDomAdapter(root, opts), root, found.rules, callOpts),
|
|
25
|
+
set: data => engineMod.apply(createDomAdapter(root, { ...opts, _write: true }), root, found.rules, data, callOpts),
|
|
22
26
|
}
|
|
23
27
|
},
|
|
28
|
+
codecs: { register: engineMod.registerCodec, get: engineMod.getCodec },
|
|
24
29
|
parseStrict: engineMod.parseStrict,
|
|
25
30
|
parseRelaxed: engineMod.parseRelaxed,
|
|
26
31
|
// Adapter-free, like the parsers above: a consumer that has to split a rule
|
|
@@ -38,7 +43,7 @@ function defaultRules(root) {
|
|
|
38
43
|
const searchRoot = root && root.ownerDocument ? root.ownerDocument : root
|
|
39
44
|
const found = engineMod.findRulesIn(domAdapter, searchRoot, 'api')
|
|
40
45
|
if (!found) throw new engineMod.errors.NoRulesTag('api')
|
|
41
|
-
return found.rules
|
|
46
|
+
return { rules: found.rules, version: found.version }
|
|
42
47
|
}
|
|
43
48
|
|
|
44
49
|
function containingTree(root) {
|
|
@@ -119,8 +124,8 @@ function assertNoReadOnlyTargets(rule, value) {
|
|
|
119
124
|
}
|
|
120
125
|
}
|
|
121
126
|
|
|
122
|
-
function guardedApply(root, rules, data) {
|
|
123
|
-
const opts = { templateAttr: 'cms-template' }
|
|
127
|
+
function guardedApply(root, rules, data, version) {
|
|
128
|
+
const opts = withCodecs({ templateAttr: 'cms-template', version })
|
|
124
129
|
const liveTree = containingTree(root)
|
|
125
130
|
const rootPath = childIndexPath(liveTree, root)
|
|
126
131
|
const detachedTree = cloneContainingTree(liveTree)
|
|
@@ -145,7 +150,8 @@ function guardedApply(root, rules, data) {
|
|
|
145
150
|
|
|
146
151
|
function exposeWriteError(error) {
|
|
147
152
|
if (!(error instanceof Error)) return error
|
|
148
|
-
const
|
|
153
|
+
const keepsCode = error instanceof engineMod.errors.UnknownCodec || error instanceof engineMod.errors.CodecRefused
|
|
154
|
+
const code = keepsCode ? error.code : error.name || 'Error'
|
|
149
155
|
try {
|
|
150
156
|
error.code = code
|
|
151
157
|
} catch {
|
|
@@ -182,7 +188,10 @@ function exposeWriteError(error) {
|
|
|
182
188
|
export function extractData(a, b) {
|
|
183
189
|
const root = isNode(a) ? a : document
|
|
184
190
|
const source = isNode(a) ? b : a
|
|
185
|
-
if (source === undefined)
|
|
191
|
+
if (source === undefined) {
|
|
192
|
+
const { rules, version } = defaultRules(root)
|
|
193
|
+
return engineMod.extract(createDomAdapter(root), root, rules, withCodecs({ version }))
|
|
194
|
+
}
|
|
186
195
|
return engine.bind(root, source).get()
|
|
187
196
|
}
|
|
188
197
|
|
|
@@ -193,8 +202,8 @@ export function applyData(root, data, source) {
|
|
|
193
202
|
if (!isNode(root)) {
|
|
194
203
|
throw new Error('hyper-html-api: applyData(root, data, source?) needs a DOM root as the first argument.')
|
|
195
204
|
}
|
|
196
|
-
const rules = source === undefined ? defaultRules(root) : engine.bind(root, source)
|
|
197
|
-
guardedApply(root, rules, data)
|
|
205
|
+
const { rules, version } = source === undefined ? defaultRules(root) : engine.bind(root, source)
|
|
206
|
+
guardedApply(root, rules, data, version)
|
|
198
207
|
} catch (error) {
|
|
199
208
|
throw exposeWriteError(error)
|
|
200
209
|
}
|
package/src/engine/apply.js
CHANGED
|
@@ -10,13 +10,15 @@ import {
|
|
|
10
10
|
ShapeMismatch,
|
|
11
11
|
} from './errors.js'
|
|
12
12
|
import { listDiff } from './diff.js'
|
|
13
|
-
import { ruleAttrIndex } from './rule-syntax.js'
|
|
13
|
+
import { codecOf, ruleAttrIndex, splitRule } from './rule-syntax.js'
|
|
14
14
|
import { isCustomPropertyName, setStyleVar } from './style-vars.js'
|
|
15
|
+
import { checkCodecRules, codecForRule } from './codecs.js'
|
|
15
16
|
|
|
16
17
|
export function apply(adapter, root, rules, data, opts = {}) {
|
|
17
18
|
if (Object.prototype.hasOwnProperty.call(opts, 'exclude') && adapter.semanticExclude === false) {
|
|
18
19
|
throw new Error('This adapter does not support semantic exclude queries; omit exclude or use the DOM adapter.')
|
|
19
20
|
}
|
|
21
|
+
checkCodecRules(rules, opts)
|
|
20
22
|
const mismatches = []
|
|
21
23
|
validateShape(rules, data, [], mismatches)
|
|
22
24
|
if (mismatches.length) throw new ShapeMismatch(mismatches)
|
|
@@ -64,17 +66,23 @@ function applyScalar(adapter, ctx, rule, value, trace, opts) {
|
|
|
64
66
|
return ctx
|
|
65
67
|
}
|
|
66
68
|
|
|
69
|
+
const codec = codecForRule(rule, opts)
|
|
70
|
+
if (codec) {
|
|
71
|
+
const matches = codec.selector ? adapter.find(ctx, codec.selector, opts) : [ctx]
|
|
72
|
+
if (matches.length) codec.codec.write(adapter, matches[0], value, { query: opts.query })
|
|
73
|
+
return ctx
|
|
74
|
+
}
|
|
75
|
+
|
|
67
76
|
if (rule.startsWith('@')) {
|
|
68
77
|
return writePropOrAttr(adapter, ctx, rule.slice(1), value)
|
|
69
78
|
}
|
|
70
79
|
|
|
71
80
|
const at = ruleAttrIndex(rule)
|
|
72
81
|
if (at !== -1) {
|
|
73
|
-
const selector = rule
|
|
82
|
+
const { selector, all } = splitRule(rule)
|
|
74
83
|
const name = rule.slice(at + 1)
|
|
75
84
|
const matches = selector ? adapter.find(ctx, selector, opts) : [ctx]
|
|
76
|
-
|
|
77
|
-
writePropOrAttr(adapter, matches[0], name, value)
|
|
85
|
+
for (const node of all ? matches : matches.slice(0, 1)) writePropOrAttr(adapter, node, name, value)
|
|
78
86
|
return ctx
|
|
79
87
|
}
|
|
80
88
|
|
|
@@ -83,9 +91,9 @@ function applyScalar(adapter, ctx, rule, value, trace, opts) {
|
|
|
83
91
|
return ctx
|
|
84
92
|
}
|
|
85
93
|
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
writeText(adapter,
|
|
94
|
+
const { selector, all } = splitRule(rule)
|
|
95
|
+
const matches = adapter.find(ctx, selector, opts)
|
|
96
|
+
for (const node of all ? matches : matches.slice(0, 1)) writeText(adapter, node, value)
|
|
89
97
|
return ctx
|
|
90
98
|
}
|
|
91
99
|
|
|
@@ -157,6 +165,8 @@ function validateShape(rule, value, path, mismatches) {
|
|
|
157
165
|
if (value === undefined) return
|
|
158
166
|
|
|
159
167
|
if (typeof rule === 'string') {
|
|
168
|
+
// A codec judges its own value when it writes.
|
|
169
|
+
if (codecOf(rule)) return
|
|
160
170
|
if (rule.endsWith('[]')) {
|
|
161
171
|
if (!Array.isArray(value)) {
|
|
162
172
|
mismatches.push({ path: pathStr(path), expected: 'array', got: typeofX(value) })
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { MAX_RULE_DEPTH, RulesParseError, UnknownCodec } from './errors.js'
|
|
2
|
+
import { codecOf } from './rule-syntax.js'
|
|
3
|
+
|
|
4
|
+
// A codec reads and writes one element's whole value as JSON, for content the
|
|
5
|
+
// rule grammar cannot describe (a sheet's grid). It is named by the rule form
|
|
6
|
+
// "selector@codec(name)", valid only under data-rules-version="2" and only as the
|
|
7
|
+
// value of a top level key.
|
|
8
|
+
//
|
|
9
|
+
// codec = {
|
|
10
|
+
// name,
|
|
11
|
+
// read(adapter, node, ctx) -> JSON value ctx = { query?, view?, region? }
|
|
12
|
+
// write(adapter, node, value, ctx) -> void | { regions }
|
|
13
|
+
// patch?(adapter, node, ops, ctx) -> { changed, stale, regions }
|
|
14
|
+
// same(written, reread) -> boolean
|
|
15
|
+
// styles?: string[]
|
|
16
|
+
// cut?(src, marker) -> { rest, regions }
|
|
17
|
+
// }
|
|
18
|
+
|
|
19
|
+
const NAME_RE = /^[a-z][a-z0-9-]*$/
|
|
20
|
+
const registry = new Map()
|
|
21
|
+
|
|
22
|
+
// The browser registry. Hosts pass `codecs` per call instead, so no state is
|
|
23
|
+
// shared between requests.
|
|
24
|
+
export function registerCodec(name, codec) {
|
|
25
|
+
if (typeof name !== 'string' || !NAME_RE.test(name)) {
|
|
26
|
+
throw new Error(`hyper-html-api: invalid codec name ${JSON.stringify(name)} (must match ${NAME_RE})`)
|
|
27
|
+
}
|
|
28
|
+
if (!codec || typeof codec.read !== 'function' || typeof codec.write !== 'function' || typeof codec.same !== 'function') {
|
|
29
|
+
throw new Error(`hyper-html-api: codec "${name}" needs read, write and same functions`)
|
|
30
|
+
}
|
|
31
|
+
registry.set(name, codec)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function getCodec(name) {
|
|
35
|
+
return registry.get(name) ?? null
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function registeredCodecs() {
|
|
39
|
+
return Object.fromEntries(registry)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const NEEDS_VERSION_2 = 'The @codec(name) rule form needs a rules tag with data-rules-version="2".'
|
|
43
|
+
export const NEEDS_TOP_LEVEL = 'A @codec(name) rule must be the value of a top level key.'
|
|
44
|
+
|
|
45
|
+
// The codec a rule names, or null for any other rule. Assumes checkCodecRules
|
|
46
|
+
// already accepted the tree.
|
|
47
|
+
export function codecForRule(rule, opts) {
|
|
48
|
+
const form = codecOf(rule)
|
|
49
|
+
return form ? { ...form, codec: opts.codecs[form.name] } : null
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Walks the whole rule tree once, before anything is read or written, so a
|
|
53
|
+
// rules tree the call cannot honor refuses without a partial write. A codec
|
|
54
|
+
// rule read as a plain attribute would return null for the whole value, and a
|
|
55
|
+
// null that reaches a draft store or a guard baseline looks like the owner
|
|
56
|
+
// emptied it, which is why every refusal here is loud.
|
|
57
|
+
export function checkCodecRules(rules, opts = {}) {
|
|
58
|
+
const version = opts.version ?? '1'
|
|
59
|
+
const codecs = opts.codecs ?? {}
|
|
60
|
+
walk(rules, 0)
|
|
61
|
+
|
|
62
|
+
function walk(rule, depth) {
|
|
63
|
+
if (depth > MAX_RULE_DEPTH) return
|
|
64
|
+
if (typeof rule === 'string') {
|
|
65
|
+
const form = codecOf(rule)
|
|
66
|
+
if (!form) return
|
|
67
|
+
if (version !== '2') throw new RulesParseError(NEEDS_VERSION_2)
|
|
68
|
+
if (depth !== 1) throw new RulesParseError(NEEDS_TOP_LEVEL)
|
|
69
|
+
if (!Object.prototype.hasOwnProperty.call(codecs, form.name)) throw new UnknownCodec(form.name)
|
|
70
|
+
return
|
|
71
|
+
}
|
|
72
|
+
if (Array.isArray(rule)) {
|
|
73
|
+
walk(rule[1], depth + 1)
|
|
74
|
+
return
|
|
75
|
+
}
|
|
76
|
+
if (rule && typeof rule === 'object') {
|
|
77
|
+
for (const sub of Object.values(rule)) walk(sub, depth + 1)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
package/src/engine/errors.js
CHANGED
|
@@ -8,7 +8,7 @@ export class RulesParseError extends Error {
|
|
|
8
8
|
|
|
9
9
|
export class UnknownRulesVersion extends Error {
|
|
10
10
|
constructor(version) {
|
|
11
|
-
super(`unknown rules version: ${version}. Library supports "1".`)
|
|
11
|
+
super(`unknown rules version: ${version}. Library supports "1" and "2".`)
|
|
12
12
|
this.name = 'UnknownRulesVersion'
|
|
13
13
|
this.version = version
|
|
14
14
|
}
|
|
@@ -92,3 +92,22 @@ export class WriteRejected extends Error {
|
|
|
92
92
|
this.unmatched = unmatched
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
|
+
|
|
96
|
+
export class UnknownCodec extends Error {
|
|
97
|
+
constructor(name) {
|
|
98
|
+
super(`unknown codec "${name}": no codec by that name is given to this call`)
|
|
99
|
+
this.name = 'UnknownCodec'
|
|
100
|
+
this.code = 'unknown-codec'
|
|
101
|
+
this.codec = name
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Thrown by a codec that will not take a write, with its own code (for
|
|
106
|
+
// example "sheet-not-canonical"). A client error, like UnknownCodec.
|
|
107
|
+
export class CodecRefused extends Error {
|
|
108
|
+
constructor(code, message) {
|
|
109
|
+
super(message)
|
|
110
|
+
this.name = 'CodecRefused'
|
|
111
|
+
this.code = code
|
|
112
|
+
}
|
|
113
|
+
}
|
package/src/engine/extract.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { DOM_PROPERTIES_SET } from './dom-properties.js'
|
|
2
2
|
import { MaxRuleDepthExceeded, MAX_RULE_DEPTH } from './errors.js'
|
|
3
|
-
import { ruleAttrIndex } from './rule-syntax.js'
|
|
3
|
+
import { ruleAttrIndex, splitRule } from './rule-syntax.js'
|
|
4
4
|
import { isCustomPropertyName, readStyleVar } from './style-vars.js'
|
|
5
|
+
import { checkCodecRules, codecForRule } from './codecs.js'
|
|
5
6
|
|
|
6
7
|
export function extract(adapter, root, rules, opts = {}) {
|
|
7
8
|
if (Object.prototype.hasOwnProperty.call(opts, 'exclude') && adapter.semanticExclude === false) {
|
|
8
9
|
throw new Error('This adapter does not support semantic exclude queries; omit exclude or use the DOM adapter.')
|
|
9
10
|
}
|
|
11
|
+
checkCodecRules(rules, opts)
|
|
10
12
|
return extractAt(adapter, root, rules, { depth: 0, path: [] }, opts)
|
|
11
13
|
}
|
|
12
14
|
|
|
@@ -57,25 +59,37 @@ function extractScalar(adapter, ctx, rule, trace, opts) {
|
|
|
57
59
|
return matches.map((n) => adapter.text(n))
|
|
58
60
|
}
|
|
59
61
|
|
|
62
|
+
const codec = codecForRule(rule, opts)
|
|
63
|
+
if (codec) return readCodec(adapter, ctx, codec, opts)
|
|
64
|
+
|
|
60
65
|
if (rule.startsWith('@')) {
|
|
61
66
|
return readPropOrAttr(adapter, ctx, rule.slice(1))
|
|
62
67
|
}
|
|
63
68
|
|
|
64
69
|
const at = ruleAttrIndex(rule)
|
|
65
70
|
if (at !== -1) {
|
|
66
|
-
const selector = rule
|
|
71
|
+
const { selector, all } = splitRule(rule)
|
|
67
72
|
const name = rule.slice(at + 1)
|
|
68
73
|
const matches = selector ? adapter.find(ctx, selector, opts) : [ctx]
|
|
74
|
+
if (all && opts.allMatches) return matches.map((n) => readPropOrAttr(adapter, n, name))
|
|
69
75
|
if (matches.length === 0) return null
|
|
70
76
|
return readPropOrAttr(adapter, matches[0], name)
|
|
71
77
|
}
|
|
72
78
|
|
|
73
79
|
if (rule === '.') return adapter.text(ctx)
|
|
74
80
|
|
|
75
|
-
const
|
|
81
|
+
const { selector, all } = splitRule(rule)
|
|
82
|
+
const matches = adapter.find(ctx, selector, opts)
|
|
83
|
+
if (all && opts.allMatches) return matches.map((n) => adapter.text(n))
|
|
76
84
|
return matches.length === 0 ? null : adapter.text(matches[0])
|
|
77
85
|
}
|
|
78
86
|
|
|
87
|
+
function readCodec(adapter, ctx, { selector, codec }, opts) {
|
|
88
|
+
const matches = selector ? adapter.find(ctx, selector, opts) : [ctx]
|
|
89
|
+
if (matches.length === 0) return null
|
|
90
|
+
return codec.read(adapter, matches[0], { query: opts.query, view: opts.view, region: opts.region })
|
|
91
|
+
}
|
|
92
|
+
|
|
79
93
|
// The path is copied because the caller is handed it and every nested path is
|
|
80
94
|
// built from the same array. A throwing hook must not abandon the extract.
|
|
81
95
|
function reportRows(opts, trace, nodes) {
|
package/src/engine/index.js
CHANGED
|
@@ -6,7 +6,9 @@ export { extract } from './extract.js'
|
|
|
6
6
|
export { apply } from './apply.js'
|
|
7
7
|
export { findRulesIn } from './rules-tag.js'
|
|
8
8
|
export { parseStrict, parseRelaxed } from './rules.js'
|
|
9
|
-
export { ruleAttrIndex, splitRule } from './rule-syntax.js'
|
|
9
|
+
export { ruleAttrIndex, splitRule, codecOf } from './rule-syntax.js'
|
|
10
|
+
export { registerCodec, getCodec, registeredCodecs, checkCodecRules } from './codecs.js'
|
|
11
|
+
export { SUPPORTED_VERSION, SUPPORTED_VERSIONS } from './rules-tag.js'
|
|
10
12
|
export * as errors from './errors.js'
|
|
11
13
|
export { DOM_PROPERTIES, DOM_PROPERTIES_SET, DOM_PROPERTIES_READ_ONLY_SET } from './dom-properties.js'
|
|
12
14
|
export { WRITE_POLICY, guardAdapter, checkAttribute, checkCustomPropertyValue, urlScheme } from './write-policy.js'
|
|
@@ -21,7 +23,7 @@ export { trackAdapter, spliceDocument } from './splice.js'
|
|
|
21
23
|
// For a Document root or the cheerio $.root(), ownerDocument is absent → no-op.
|
|
22
24
|
export function resolveRules(adapter, root, source) {
|
|
23
25
|
if (source && typeof source === 'object') {
|
|
24
|
-
return { rules: source, tagNode: null }
|
|
26
|
+
return { rules: source, tagNode: null, version: '1' }
|
|
25
27
|
}
|
|
26
28
|
if (typeof source === 'string') {
|
|
27
29
|
const searchRoot = (root && root.ownerDocument) ? root.ownerDocument : root
|
|
@@ -36,11 +38,13 @@ export function bind(adapter, root, source, opts) {
|
|
|
36
38
|
const what = typeof source === 'string' ? `data-rules-name~="${source}"` : 'the provided rules object'
|
|
37
39
|
throw new Error(`hyper-html-api: could not resolve rules for ${what}`)
|
|
38
40
|
}
|
|
39
|
-
const { rules, tagNode } = found
|
|
41
|
+
const { rules, tagNode, version } = found
|
|
42
|
+
const callOpts = { version, ...opts }
|
|
40
43
|
return {
|
|
41
44
|
rules,
|
|
42
45
|
tagNode, // informational only; null for a literal object
|
|
43
|
-
|
|
44
|
-
|
|
46
|
+
version,
|
|
47
|
+
get: () => extract(adapter, root, rules, callOpts), // opts (skip/templateAttr/codecs) forwarded
|
|
48
|
+
set: (data) => apply(adapter, root, rules, data, callOpts),
|
|
45
49
|
}
|
|
46
50
|
}
|
package/src/engine/round-trip.js
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
coercePropValue,
|
|
5
5
|
} from './dom-properties.js'
|
|
6
6
|
import { WriteNotRoundTripped } from './errors.js'
|
|
7
|
-
import { ruleAttrIndex } from './rule-syntax.js'
|
|
7
|
+
import { codecOf, ruleAttrIndex, splitRule } from './rule-syntax.js'
|
|
8
8
|
import { isCustomPropertyName } from './style-vars.js'
|
|
9
9
|
|
|
10
10
|
// Props whose read-side value keeps its whitespace. Every other text rule reads
|
|
@@ -18,22 +18,28 @@ const UNTRIMMED_PROPS = new Set(['textContent', 'innerText'])
|
|
|
18
18
|
// WriteNotRoundTripped instead of returning a false success. Only the supplied
|
|
19
19
|
// projection is checked: omitted keys and undefined are skipped exactly as the
|
|
20
20
|
// writer skips them, which is what keeps partial updates working.
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
// A codec value is compared by its codec's own same(), from `codecs`.
|
|
22
|
+
export function assertRoundTrip(rules, requested, actual, codecs = {}) {
|
|
23
|
+
check(rules, requested, actual, [], codecs)
|
|
23
24
|
}
|
|
24
25
|
|
|
25
|
-
function check(rule, want, got, path) {
|
|
26
|
+
function check(rule, want, got, path, codecs) {
|
|
26
27
|
if (want === undefined) return
|
|
27
28
|
if (typeof rule === 'string') {
|
|
29
|
+
const form = codecOf(rule)
|
|
30
|
+
if (form) {
|
|
31
|
+
if (!codecs[form.name].same(want, got)) fail(path)
|
|
32
|
+
return
|
|
33
|
+
}
|
|
28
34
|
checkScalarRule(rule, want, got, path)
|
|
29
35
|
return
|
|
30
36
|
}
|
|
31
37
|
if (Array.isArray(rule)) {
|
|
32
|
-
checkList(rule[1], want, got, path)
|
|
38
|
+
checkList(rule[1], want, got, path, codecs)
|
|
33
39
|
return
|
|
34
40
|
}
|
|
35
41
|
if (typeof rule === 'object' && rule !== null) {
|
|
36
|
-
checkObject(rule, want, got, path)
|
|
42
|
+
checkObject(rule, want, got, path, codecs)
|
|
37
43
|
}
|
|
38
44
|
}
|
|
39
45
|
|
|
@@ -47,6 +53,14 @@ function checkScalarRule(rule, want, got, path) {
|
|
|
47
53
|
return
|
|
48
54
|
}
|
|
49
55
|
const at = ruleAttrIndex(rule)
|
|
56
|
+
if (splitRule(rule).all) {
|
|
57
|
+
if (!Array.isArray(got) || got.length === 0) fail(path)
|
|
58
|
+
for (const one of got) {
|
|
59
|
+
if (at !== -1) checkPropOrAttr(rule.slice(at + 1), want, one, path)
|
|
60
|
+
else checkText(want, one, path)
|
|
61
|
+
}
|
|
62
|
+
return
|
|
63
|
+
}
|
|
50
64
|
if (at !== -1) {
|
|
51
65
|
checkPropOrAttr(rule.slice(at + 1), want, got, path)
|
|
52
66
|
return
|
|
@@ -63,16 +77,16 @@ function checkTextList(want, got, path) {
|
|
|
63
77
|
for (let i = 0; i < want.length; i++) checkText(want[i], got[i], [...path, i])
|
|
64
78
|
}
|
|
65
79
|
|
|
66
|
-
function checkObject(rule, want, got, path) {
|
|
80
|
+
function checkObject(rule, want, got, path, codecs) {
|
|
67
81
|
if (!isPlainObject(want) || !isPlainObject(got)) fail(path)
|
|
68
82
|
for (const [key, sub] of Object.entries(rule)) {
|
|
69
|
-
check(sub, want[key], got[key], [...path, key])
|
|
83
|
+
check(sub, want[key], got[key], [...path, key], codecs)
|
|
70
84
|
}
|
|
71
85
|
}
|
|
72
86
|
|
|
73
|
-
function checkList(shape, want, got, path) {
|
|
87
|
+
function checkList(shape, want, got, path, codecs) {
|
|
74
88
|
if (!Array.isArray(want) || !Array.isArray(got) || want.length !== got.length) fail(path)
|
|
75
|
-
for (let i = 0; i < want.length; i++) check(shape, want[i], got[i], [...path, i])
|
|
89
|
+
for (let i = 0; i < want.length; i++) check(shape, want[i], got[i], [...path, i], codecs)
|
|
76
90
|
}
|
|
77
91
|
|
|
78
92
|
function checkPropOrAttr(name, want, got, path) {
|
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
// rule into `a[href="mailto:hi` and `example.com"]`, which reads downstream as
|
|
11
11
|
// an invalid selector when the rule the author wrote was correct.
|
|
12
12
|
|
|
13
|
+
import { RulesParseError } from './errors.js'
|
|
14
|
+
|
|
13
15
|
// Index of the separator "@", or -1 when the rule has no prop part.
|
|
14
16
|
export function ruleAttrIndex(rule) {
|
|
15
17
|
let depth = 0
|
|
@@ -34,12 +36,22 @@ export function ruleAttrIndex(rule) {
|
|
|
34
36
|
return found
|
|
35
37
|
}
|
|
36
38
|
|
|
39
|
+
const ALL = '[*]'
|
|
40
|
+
|
|
37
41
|
// "<selector>@<prop>" split at that separator. selector is "" for a leading "@"
|
|
38
|
-
// (the context node itself); prop is null when the rule names no prop.
|
|
42
|
+
// (the context node itself); prop is null when the rule names no prop. A selector
|
|
43
|
+
// that ends in "[*]" names every match instead of the first: `all` is true and
|
|
44
|
+
// the marker is removed, because "[*]" is not CSS and no adapter can run it.
|
|
39
45
|
export function splitRule(rule) {
|
|
40
46
|
const at = ruleAttrIndex(rule)
|
|
41
|
-
|
|
42
|
-
|
|
47
|
+
const head = at === -1 ? rule : rule.slice(0, at)
|
|
48
|
+
const all = head.endsWith(ALL)
|
|
49
|
+
const split = {
|
|
50
|
+
selector: all ? head.slice(0, -ALL.length) : head,
|
|
51
|
+
prop: at === -1 ? null : rule.slice(at + 1) || null,
|
|
52
|
+
}
|
|
53
|
+
if (all) split.all = true
|
|
54
|
+
return split
|
|
43
55
|
}
|
|
44
56
|
|
|
45
57
|
// The list grammar needs a second split: "container > row" says where a row
|
|
@@ -112,3 +124,17 @@ function isSingleCompound(selector, start) {
|
|
|
112
124
|
}
|
|
113
125
|
return true
|
|
114
126
|
}
|
|
127
|
+
|
|
128
|
+
const CODEC_PROP = /^codec\(([a-z][a-z0-9-]*)\)$/
|
|
129
|
+
|
|
130
|
+
// { selector, name } when a scalar rule calls a codec, else null.
|
|
131
|
+
export function codecOf(rule) {
|
|
132
|
+
if (typeof rule !== 'string') return null
|
|
133
|
+
const at = ruleAttrIndex(rule)
|
|
134
|
+
if (at === -1) return null
|
|
135
|
+
const match = CODEC_PROP.exec(rule.slice(at + 1))
|
|
136
|
+
if (!match) return null
|
|
137
|
+
const { selector, all } = splitRule(rule)
|
|
138
|
+
if (all) throw new RulesParseError('A @codec(name) rule cannot use [*].')
|
|
139
|
+
return { selector, name: match[1] }
|
|
140
|
+
}
|
package/src/engine/rules-tag.js
CHANGED
|
@@ -2,6 +2,7 @@ import { parseRelaxed } from './rules.js'
|
|
|
2
2
|
import { UnknownRulesVersion } from './errors.js'
|
|
3
3
|
|
|
4
4
|
export const SUPPORTED_VERSION = '1'
|
|
5
|
+
export const SUPPORTED_VERSIONS = ['1', '2']
|
|
5
6
|
const TOKEN_RE = /^[a-zA-Z0-9_-]+$/
|
|
6
7
|
|
|
7
8
|
// findRulesIn(adapter, root) → first script[data-rules-name] (tooling/no-token)
|
|
@@ -29,12 +30,12 @@ export function findRulesIn(adapter, root, token) {
|
|
|
29
30
|
|
|
30
31
|
const tagNode = candidates[0]
|
|
31
32
|
const version = adapter.attr(tagNode, 'data-rules-version')
|
|
32
|
-
if (version
|
|
33
|
+
if (!SUPPORTED_VERSIONS.includes(version)) throw new UnknownRulesVersion(version)
|
|
33
34
|
|
|
34
35
|
// Script tag bodies accept the same relaxed JSON syntax as the ?data=
|
|
35
36
|
// URL parameter: unquoted keys, single-quoted strings, trailing commas.
|
|
36
37
|
// parseRelaxed tries strict JSON.parse first, so valid JSON keeps
|
|
37
38
|
// parsing identically.
|
|
38
39
|
const rules = parseRelaxed(adapter.text(tagNode))
|
|
39
|
-
return { rules, tagNode }
|
|
40
|
+
return { rules, tagNode, version }
|
|
40
41
|
}
|
package/src/engine/write-plan.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { splitRule } from './rule-syntax.js'
|
|
2
|
+
import { checkCodecRules } from './codecs.js'
|
|
2
3
|
|
|
3
4
|
function isPlainObject(v) {
|
|
4
5
|
return v !== null && typeof v === 'object' && !Array.isArray(v)
|
|
@@ -13,6 +14,7 @@ function label(path) {
|
|
|
13
14
|
// Shape problems are left to apply(), which already reports them. Inside list
|
|
14
15
|
// items only unknown keys are checked, because new rows do not exist yet.
|
|
15
16
|
export function planWrite(adapter, root, rules, data, opts = {}) {
|
|
17
|
+
checkCodecRules(rules, opts)
|
|
16
18
|
const unknownKeys = []
|
|
17
19
|
const unmatched = []
|
|
18
20
|
|
|
@@ -34,8 +36,7 @@ export function planWrite(adapter, root, rules, data, opts = {}) {
|
|
|
34
36
|
if (value === undefined) return
|
|
35
37
|
if (typeof rule === 'string') {
|
|
36
38
|
if (rule.endsWith('[]') || rule === '.' || rule.startsWith('@')) return
|
|
37
|
-
const
|
|
38
|
-
const selector = at === -1 ? rule : rule.slice(0, at)
|
|
39
|
+
const { selector } = splitRule(rule)
|
|
39
40
|
if (!selector) return
|
|
40
41
|
if (adapter.find(ctx, selector, opts).length === 0) unmatched.push({ path: label(path), selector })
|
|
41
42
|
return
|
package/src/page.js
CHANGED
|
@@ -5,7 +5,7 @@ import { NoRulesTag, RulesParseError } from './engine/errors.js'
|
|
|
5
5
|
import { parseRelaxed } from './engine/rules.js'
|
|
6
6
|
import { writeDocument } from './write.js'
|
|
7
7
|
|
|
8
|
-
const PAGE = /^
|
|
8
|
+
const PAGE = /^blockeditor(?:\.[0-9]+)?$/
|
|
9
9
|
const TEXT = new Set(['paragraph', 'heading', 'list_item', 'callout', 'code_block', 'toggle'])
|
|
10
10
|
const MARKS = ['b', 'i', 's', 'c', 'a']
|
|
11
11
|
|
|
@@ -21,10 +21,19 @@ function refuse(message) {
|
|
|
21
21
|
throw new PageWriteRefused('invalid-content', message)
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
// Inert content (a starter article kept in a <template>) is not part of the page, as in a browser.
|
|
25
|
+
// Cheerio parents a template's content under a root node, which closest() does not climb past.
|
|
26
|
+
function live(_, node) {
|
|
27
|
+
for (let up = node.parent; up; up = up.parent) {
|
|
28
|
+
if (up.type === 'tag' && (up.name === 'template' || up.name === 'noscript')) return false
|
|
29
|
+
}
|
|
30
|
+
return true
|
|
31
|
+
}
|
|
32
|
+
|
|
24
33
|
function pageNodes($) {
|
|
25
|
-
const tags = $('script[merge][clay~="live"]').filter((_, node) => PAGE.test($(node).attr('merge')))
|
|
34
|
+
const tags = $('script[merge][clay~="live"]').filter((_, node) => PAGE.test($(node).attr('merge'))).filter(live)
|
|
26
35
|
if (!tags.length) return null
|
|
27
|
-
const articles = $('article[data-
|
|
36
|
+
const articles = $('article[data-blockeditor]').filter(live)
|
|
28
37
|
if (tags.length !== 1 || articles.length !== 1) refuse('Expected one page store and one page article')
|
|
29
38
|
return { tag: tags[0], article: articles[0] }
|
|
30
39
|
}
|
|
@@ -316,8 +325,9 @@ export async function writePageDocument(load, html, data, options = {}) {
|
|
|
316
325
|
const next$ = load(candidate.html, { sourceCodeLocationInfo: true })
|
|
317
326
|
const nextNodes = pageNodes(next$)
|
|
318
327
|
const aligned$ = load(aligned)
|
|
319
|
-
const
|
|
320
|
-
const
|
|
328
|
+
const alignedArticle = pageNodes(aligned$).article
|
|
329
|
+
const changes = pageChanges(next$, nextNodes.article, aligned$, alignedArticle, page.blocks, before.model, projection)
|
|
330
|
+
const unchangedAttrs = canon(next$(nextNodes.article).attr()) === canon(aligned$(alignedArticle).attr())
|
|
321
331
|
const proposed = projectionOf(load, proposedBlocks(page.blocks, changes), projection)
|
|
322
332
|
if (!unchangedAttrs || !sameArticle(next$, nextNodes.article, proposed)) {
|
|
323
333
|
refuse('The article edit cannot be represented in the page store')
|