hyper-html-api 0.14.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/conformance/MANIFEST.json +2 -2
- 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/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 +7 -7
- package/dist/hyper-html-api.upgrade.min.js +2 -2
- package/package.json +5 -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 +12 -1
- package/src/engine/codecs.js +80 -0
- package/src/engine/errors.js +20 -1
- package/src/engine/extract.js +11 -0
- package/src/engine/index.js +9 -5
- package/src/engine/round-trip.js +16 -10
- package/src/engine/rule-syntax.js +16 -0
- package/src/engine/rules-tag.js +3 -2
- package/src/engine/write-plan.js +2 -0
- package/src/patch-document.js +122 -0
- package/src/read.js +163 -0
- package/src/upgrade/codec-carry.js +23 -0
- package/src/upgrade/extract-all.js +12 -3
- package/src/upgrade/migrate.js +30 -3
- package/src/upgrade/rules-tags.js +4 -4
- 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, splitRule } 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,6 +66,13 @@ 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
|
}
|
|
@@ -156,6 +165,8 @@ function validateShape(rule, value, path, mismatches) {
|
|
|
156
165
|
if (value === undefined) return
|
|
157
166
|
|
|
158
167
|
if (typeof rule === 'string') {
|
|
168
|
+
// A codec judges its own value when it writes.
|
|
169
|
+
if (codecOf(rule)) return
|
|
159
170
|
if (rule.endsWith('[]')) {
|
|
160
171
|
if (!Array.isArray(value)) {
|
|
161
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
|
@@ -2,11 +2,13 @@ import { DOM_PROPERTIES_SET } from './dom-properties.js'
|
|
|
2
2
|
import { MaxRuleDepthExceeded, MAX_RULE_DEPTH } from './errors.js'
|
|
3
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,6 +59,9 @@ 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
|
}
|
|
@@ -79,6 +84,12 @@ function extractScalar(adapter, ctx, rule, trace, opts) {
|
|
|
79
84
|
return matches.length === 0 ? null : adapter.text(matches[0])
|
|
80
85
|
}
|
|
81
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
|
+
|
|
82
93
|
// The path is copied because the caller is handed it and every nested path is
|
|
83
94
|
// built from the same array. A throwing hook must not abandon the extract.
|
|
84
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, splitRule } 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
|
|
|
@@ -71,16 +77,16 @@ function checkTextList(want, got, path) {
|
|
|
71
77
|
for (let i = 0; i < want.length; i++) checkText(want[i], got[i], [...path, i])
|
|
72
78
|
}
|
|
73
79
|
|
|
74
|
-
function checkObject(rule, want, got, path) {
|
|
80
|
+
function checkObject(rule, want, got, path, codecs) {
|
|
75
81
|
if (!isPlainObject(want) || !isPlainObject(got)) fail(path)
|
|
76
82
|
for (const [key, sub] of Object.entries(rule)) {
|
|
77
|
-
check(sub, want[key], got[key], [...path, key])
|
|
83
|
+
check(sub, want[key], got[key], [...path, key], codecs)
|
|
78
84
|
}
|
|
79
85
|
}
|
|
80
86
|
|
|
81
|
-
function checkList(shape, want, got, path) {
|
|
87
|
+
function checkList(shape, want, got, path, codecs) {
|
|
82
88
|
if (!Array.isArray(want) || !Array.isArray(got) || want.length !== got.length) fail(path)
|
|
83
|
-
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)
|
|
84
90
|
}
|
|
85
91
|
|
|
86
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
|
|
@@ -122,3 +124,17 @@ function isSingleCompound(selector, start) {
|
|
|
122
124
|
}
|
|
123
125
|
return true
|
|
124
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
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
|
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import cheerioAdapter from './adapters/cheerio.js'
|
|
2
|
+
import { checkCodecRules } from './engine/codecs.js'
|
|
3
|
+
import { CodecRefused, NoRulesTag } from './engine/errors.js'
|
|
4
|
+
import { findRulesIn } from './engine/rules-tag.js'
|
|
5
|
+
import { codecOf } from './engine/rule-syntax.js'
|
|
6
|
+
import { pickTab, sheet, tabNodes } from './codecs/sheet.js'
|
|
7
|
+
import { CodecMiss, QueryRefused, holeMarker, patchGrid, scanGrid } from './codecs/sheet.vendor.js'
|
|
8
|
+
import { holesHold } from './read.js'
|
|
9
|
+
|
|
10
|
+
export const NOT_CANONICAL_MESSAGE =
|
|
11
|
+
'This sheet was edited outside the sheet editor, so the API cannot patch it: open the sheet in the editor and save once.'
|
|
12
|
+
|
|
13
|
+
// Applies a patch list to a sheet document's source, rewriting only the rows it
|
|
14
|
+
// touches. `load` is cheerio.load. patch is
|
|
15
|
+
// [{ sheet?, range: "B3", values: [[450]] } | { sheet?, append: [["Porto", 12]] }]
|
|
16
|
+
// where `sheet` is a tab name or id (none means the first tab). Values are typed
|
|
17
|
+
// input: null clears, a number or boolean is that value, a string starting with
|
|
18
|
+
// "=" is a formula, any other string is text.
|
|
19
|
+
//
|
|
20
|
+
// options.newId() names appended rows (default: random ids); a test or the
|
|
21
|
+
// conformance runner passes a counter so the output is the same on every run.
|
|
22
|
+
//
|
|
23
|
+
// Returns { html, changed: { cells, rows }, stale: true }: the host cannot
|
|
24
|
+
// recalculate, so every patched table carries `stale` until a browser saves it.
|
|
25
|
+
// Throws before producing output: NoRulesTag; RulesParseError or UnknownCodec for
|
|
26
|
+
// a rules tag that cannot hold a sheet; CodecRefused 'no-sheet' when no single
|
|
27
|
+
// sheet rule matches; QueryRefused (bad_patch, bad_value, bad_range,
|
|
28
|
+
// outside_sheet, covered_cell, unknown_sheet) for a patch the sheet cannot take;
|
|
29
|
+
// CodecRefused 'sheet-not-canonical' (hosts answer 409) for a table edited
|
|
30
|
+
// outside the editor.
|
|
31
|
+
export function patchDocument(load, src, patch, options = {}) {
|
|
32
|
+
const { token = 'api', newId } = options
|
|
33
|
+
if (!Array.isArray(patch) || patch.length === 0) {
|
|
34
|
+
throw new QueryRefused('bad_patch', 'patch is a non-empty list of operations')
|
|
35
|
+
}
|
|
36
|
+
const tabs = locateTabs(load, src, token)
|
|
37
|
+
|
|
38
|
+
const groups = new Map()
|
|
39
|
+
for (const op of patch) {
|
|
40
|
+
if (!op || typeof op !== 'object' || Array.isArray(op)) {
|
|
41
|
+
throw new QueryRefused('bad_patch', 'an operation is { sheet, range, values } or { sheet, append }')
|
|
42
|
+
}
|
|
43
|
+
const { sheet: wanted, ...gridOp } = op
|
|
44
|
+
const tab = pickTab(tabs, wanted)
|
|
45
|
+
if (!groups.has(tab)) groups.set(tab, [])
|
|
46
|
+
groups.get(tab).push(gridOp)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const edits = []
|
|
50
|
+
let cells = 0
|
|
51
|
+
let rows = 0
|
|
52
|
+
for (const [tab, ops] of groups) {
|
|
53
|
+
if (!tab.region) throw new CodecRefused('sheet-not-canonical', NOT_CANONICAL_MESSAGE)
|
|
54
|
+
const { bytes, changed } = patchGrid(src.slice(tab.region.start, tab.region.end), ops, newId ? { newId } : {})
|
|
55
|
+
edits.push({ ...tab.region, bytes })
|
|
56
|
+
cells += changed.cells
|
|
57
|
+
rows += changed.rows
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
edits.sort((a, b) => b.start - a.start)
|
|
61
|
+
let html = src
|
|
62
|
+
for (const edit of edits) html = html.slice(0, edit.start) + edit.bytes + html.slice(edit.end)
|
|
63
|
+
return { html, changed: { cells, rows }, stale: true }
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// [{ id, name, region: { start, end } | null }]: region is where the tab's
|
|
67
|
+
// canonical table sits in src, or null when the table is missing or not canonical.
|
|
68
|
+
// The cut route first, as readDocument reads; when its holes do not verify, one
|
|
69
|
+
// parse with source positions, then a strict scan at each table's start.
|
|
70
|
+
function locateTabs(load, src, token) {
|
|
71
|
+
const marker = holeMarker(src)
|
|
72
|
+
const { rest, regions } = sheet.cut(src, marker)
|
|
73
|
+
if (regions.length > 0) {
|
|
74
|
+
const $ = load(rest)
|
|
75
|
+
const root = $.root()
|
|
76
|
+
const found = findRulesIn(cheerioAdapter, root, token)
|
|
77
|
+
if (!found) throw new NoRulesTag(token)
|
|
78
|
+
const container = sheetContainer(root, found)
|
|
79
|
+
if (holesHold($, root, found.rules, 'sheet', marker, regions.length)) {
|
|
80
|
+
return tabNodes(cheerioAdapter, container).map(({ id, name, table }) => ({
|
|
81
|
+
id, name, region: table ? holeRegion(table, marker, regions) : null,
|
|
82
|
+
}))
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const root = load(src, { sourceCodeLocationInfo: true }).root()
|
|
87
|
+
const found = findRulesIn(cheerioAdapter, root, token)
|
|
88
|
+
if (!found) throw new NoRulesTag(token)
|
|
89
|
+
const container = sheetContainer(root, found)
|
|
90
|
+
return tabNodes(cheerioAdapter, container).map(({ id, name, table }) => ({
|
|
91
|
+
id, name, region: table ? scannedRegion(table, src) : null,
|
|
92
|
+
}))
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function sheetContainer(root, found) {
|
|
96
|
+
checkCodecRules(found.rules, { version: found.version, codecs: { sheet } })
|
|
97
|
+
const rules = found.rules
|
|
98
|
+
const forms = rules && typeof rules === 'object' && !Array.isArray(rules)
|
|
99
|
+
? Object.values(rules).map(codecOf).filter((form) => form && form.name === 'sheet')
|
|
100
|
+
: []
|
|
101
|
+
if (forms.length !== 1) throw new CodecRefused('no-sheet', 'this document has no single @codec(sheet) rule to patch')
|
|
102
|
+
const node = forms[0].selector ? cheerioAdapter.find(root, forms[0].selector)[0] : root
|
|
103
|
+
if (!node) throw new CodecRefused('no-sheet', "the document's @codec(sheet) rule matches no element")
|
|
104
|
+
return node
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function holeRegion(table, marker, regions) {
|
|
108
|
+
const hole = cheerioAdapter.attr(table, 'data-hole')
|
|
109
|
+
if (!hole || !hole.startsWith(`${marker}-`)) return null
|
|
110
|
+
return regions[Number(hole.slice(marker.length + 1))] ?? null
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function scannedRegion(table, src) {
|
|
114
|
+
const start = table.get(0)?.sourceCodeLocation?.startOffset
|
|
115
|
+
if (start == null) return null
|
|
116
|
+
try {
|
|
117
|
+
return { start, end: scanGrid(src, start) }
|
|
118
|
+
} catch (error) {
|
|
119
|
+
if (error instanceof CodecMiss) return null
|
|
120
|
+
throw error
|
|
121
|
+
}
|
|
122
|
+
}
|