gazetta 0.5.0 → 0.7.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/admin-dist/assets/index-BO9-CXmW.css +1 -0
- package/admin-dist/assets/index-Ufu8zZH_.js +668 -0
- package/admin-dist/assets/{vendor-primevue-BnR1c_bQ.js → vendor-primevue-C0Q_YTCb.js} +330 -431
- package/admin-dist/assets/vendor-vue-D3wBSmDf.js +1 -0
- package/admin-dist/index.html +4 -4
- package/dist/admin-api/error-response.d.ts +21 -0
- package/dist/admin-api/error-response.d.ts.map +1 -0
- package/dist/admin-api/error-response.js +12 -0
- package/dist/admin-api/error-response.js.map +1 -0
- package/dist/admin-api/index.d.ts +19 -6
- package/dist/admin-api/index.d.ts.map +1 -1
- package/dist/admin-api/index.js +137 -21
- package/dist/admin-api/index.js.map +1 -1
- package/dist/admin-api/routes/assets.d.ts +16 -0
- package/dist/admin-api/routes/assets.d.ts.map +1 -0
- package/dist/admin-api/routes/assets.js +433 -0
- package/dist/admin-api/routes/assets.js.map +1 -0
- package/dist/admin-api/routes/compare.d.ts +2 -1
- package/dist/admin-api/routes/compare.d.ts.map +1 -1
- package/dist/admin-api/routes/compare.js +33 -24
- package/dist/admin-api/routes/compare.js.map +1 -1
- package/dist/admin-api/routes/fields.d.ts +2 -2
- package/dist/admin-api/routes/fields.d.ts.map +1 -1
- package/dist/admin-api/routes/fields.js +10 -3
- package/dist/admin-api/routes/fields.js.map +1 -1
- package/dist/admin-api/routes/fragments.d.ts +2 -3
- package/dist/admin-api/routes/fragments.d.ts.map +1 -1
- package/dist/admin-api/routes/fragments.js +120 -21
- package/dist/admin-api/routes/fragments.js.map +1 -1
- package/dist/admin-api/routes/history.d.ts +23 -0
- package/dist/admin-api/routes/history.d.ts.map +1 -0
- package/dist/admin-api/routes/history.js +143 -0
- package/dist/admin-api/routes/history.js.map +1 -0
- package/dist/admin-api/routes/pages.d.ts +2 -3
- package/dist/admin-api/routes/pages.d.ts.map +1 -1
- package/dist/admin-api/routes/pages.js +153 -22
- package/dist/admin-api/routes/pages.js.map +1 -1
- package/dist/admin-api/routes/preview.d.ts +2 -2
- package/dist/admin-api/routes/preview.d.ts.map +1 -1
- package/dist/admin-api/routes/preview.js +50 -15
- package/dist/admin-api/routes/preview.js.map +1 -1
- package/dist/admin-api/routes/publish.d.ts +2 -1
- package/dist/admin-api/routes/publish.d.ts.map +1 -1
- package/dist/admin-api/routes/publish.js +259 -79
- package/dist/admin-api/routes/publish.js.map +1 -1
- package/dist/admin-api/routes/site.d.ts +2 -2
- package/dist/admin-api/routes/site.d.ts.map +1 -1
- package/dist/admin-api/routes/site.js +27 -4
- package/dist/admin-api/routes/site.js.map +1 -1
- package/dist/admin-api/routes/templates.d.ts +2 -2
- package/dist/admin-api/routes/templates.d.ts.map +1 -1
- package/dist/admin-api/routes/templates.js +19 -9
- package/dist/admin-api/routes/templates.js.map +1 -1
- package/dist/admin-api/schemas/assets.d.ts +48 -0
- package/dist/admin-api/schemas/assets.d.ts.map +1 -0
- package/dist/admin-api/schemas/assets.js +44 -0
- package/dist/admin-api/schemas/assets.js.map +1 -0
- package/dist/admin-api/schemas/compare.d.ts +29 -0
- package/dist/admin-api/schemas/compare.d.ts.map +1 -0
- package/dist/admin-api/schemas/compare.js +30 -0
- package/dist/admin-api/schemas/compare.js.map +1 -0
- package/dist/admin-api/schemas/dependents.d.ts +15 -0
- package/dist/admin-api/schemas/dependents.d.ts.map +1 -0
- package/dist/admin-api/schemas/dependents.js +14 -0
- package/dist/admin-api/schemas/dependents.js.map +1 -0
- package/dist/admin-api/schemas/fetch.d.ts +12 -0
- package/dist/admin-api/schemas/fetch.d.ts.map +1 -0
- package/dist/admin-api/schemas/fetch.js +11 -0
- package/dist/admin-api/schemas/fetch.js.map +1 -0
- package/dist/admin-api/schemas/fields.d.ts +11 -0
- package/dist/admin-api/schemas/fields.d.ts.map +1 -0
- package/dist/admin-api/schemas/fields.js +11 -0
- package/dist/admin-api/schemas/fields.js.map +1 -0
- package/dist/admin-api/schemas/fragments.d.ts +27 -0
- package/dist/admin-api/schemas/fragments.d.ts.map +1 -0
- package/dist/admin-api/schemas/fragments.js +26 -0
- package/dist/admin-api/schemas/fragments.js.map +1 -0
- package/dist/admin-api/schemas/history.d.ts +73 -0
- package/dist/admin-api/schemas/history.d.ts.map +1 -0
- package/dist/admin-api/schemas/history.js +35 -0
- package/dist/admin-api/schemas/history.js.map +1 -0
- package/dist/admin-api/schemas/index.d.ts +34 -0
- package/dist/admin-api/schemas/index.d.ts.map +1 -0
- package/dist/admin-api/schemas/index.js +34 -0
- package/dist/admin-api/schemas/index.js.map +1 -0
- package/dist/admin-api/schemas/pages.d.ts +46 -0
- package/dist/admin-api/schemas/pages.d.ts.map +1 -0
- package/dist/admin-api/schemas/pages.js +47 -0
- package/dist/admin-api/schemas/pages.js.map +1 -0
- package/dist/admin-api/schemas/publish.d.ts +67 -0
- package/dist/admin-api/schemas/publish.d.ts.map +1 -0
- package/dist/admin-api/schemas/publish.js +60 -0
- package/dist/admin-api/schemas/publish.js.map +1 -0
- package/dist/admin-api/schemas/site.d.ts +28 -0
- package/dist/admin-api/schemas/site.d.ts.map +1 -0
- package/dist/admin-api/schemas/site.js +24 -0
- package/dist/admin-api/schemas/site.js.map +1 -0
- package/dist/admin-api/schemas/targets.d.ts +36 -0
- package/dist/admin-api/schemas/targets.d.ts.map +1 -0
- package/dist/admin-api/schemas/targets.js +19 -0
- package/dist/admin-api/schemas/targets.js.map +1 -0
- package/dist/admin-api/schemas/templates.d.ts +17 -0
- package/dist/admin-api/schemas/templates.d.ts.map +1 -0
- package/dist/admin-api/schemas/templates.js +16 -0
- package/dist/admin-api/schemas/templates.js.map +1 -0
- package/dist/admin-api/source-context.d.ts +158 -0
- package/dist/admin-api/source-context.d.ts.map +1 -0
- package/dist/admin-api/source-context.js +92 -0
- package/dist/admin-api/source-context.js.map +1 -0
- package/dist/app.js +1 -1
- package/dist/app.js.map +1 -1
- package/dist/assemble.d.ts.map +1 -1
- package/dist/assemble.js +4 -1
- package/dist/assemble.js.map +1 -1
- package/dist/assets/analyze-audio.d.ts +3 -0
- package/dist/assets/analyze-audio.d.ts.map +1 -0
- package/dist/assets/analyze-audio.js +80 -0
- package/dist/assets/analyze-audio.js.map +1 -0
- package/dist/assets/analyze-image.d.ts +19 -0
- package/dist/assets/analyze-image.d.ts.map +1 -0
- package/dist/assets/analyze-image.js +123 -0
- package/dist/assets/analyze-image.js.map +1 -0
- package/dist/assets/analyze.d.ts +94 -0
- package/dist/assets/analyze.d.ts.map +1 -0
- package/dist/assets/analyze.js +45 -0
- package/dist/assets/analyze.js.map +1 -0
- package/dist/assets/asset-deps.d.ts +30 -0
- package/dist/assets/asset-deps.d.ts.map +1 -0
- package/dist/assets/asset-deps.js +42 -0
- package/dist/assets/asset-deps.js.map +1 -0
- package/dist/assets/asset-paths.d.ts +155 -0
- package/dist/assets/asset-paths.d.ts.map +1 -0
- package/dist/assets/asset-paths.js +197 -0
- package/dist/assets/asset-paths.js.map +1 -0
- package/dist/assets/delete.d.ts +75 -0
- package/dist/assets/delete.d.ts.map +1 -0
- package/dist/assets/delete.js +82 -0
- package/dist/assets/delete.js.map +1 -0
- package/dist/assets/errors.d.ts +241 -0
- package/dist/assets/errors.d.ts.map +1 -0
- package/dist/assets/errors.js +300 -0
- package/dist/assets/errors.js.map +1 -0
- package/dist/assets/find-refs.d.ts +37 -0
- package/dist/assets/find-refs.d.ts.map +1 -0
- package/dist/assets/find-refs.js +35 -0
- package/dist/assets/find-refs.js.map +1 -0
- package/dist/assets/hash.d.ts +13 -0
- package/dist/assets/hash.d.ts.map +1 -0
- package/dist/assets/hash.js +43 -0
- package/dist/assets/hash.js.map +1 -0
- package/dist/assets/image-metadata.d.ts +11 -0
- package/dist/assets/image-metadata.d.ts.map +1 -0
- package/dist/assets/image-metadata.js +31 -0
- package/dist/assets/image-metadata.js.map +1 -0
- package/dist/assets/ingest-locale.d.ts +86 -0
- package/dist/assets/ingest-locale.d.ts.map +1 -0
- package/dist/assets/ingest-locale.js +209 -0
- package/dist/assets/ingest-locale.js.map +1 -0
- package/dist/assets/ingest.d.ts +96 -0
- package/dist/assets/ingest.d.ts.map +1 -0
- package/dist/assets/ingest.js +308 -0
- package/dist/assets/ingest.js.map +1 -0
- package/dist/assets/kind-compat.d.ts +34 -0
- package/dist/assets/kind-compat.d.ts.map +1 -0
- package/dist/assets/kind-compat.js +33 -0
- package/dist/assets/kind-compat.js.map +1 -0
- package/dist/assets/list.d.ts +46 -0
- package/dist/assets/list.d.ts.map +1 -0
- package/dist/assets/list.js +102 -0
- package/dist/assets/list.js.map +1 -0
- package/dist/assets/manifest-default.d.ts +56 -0
- package/dist/assets/manifest-default.d.ts.map +1 -0
- package/dist/assets/manifest-default.js +120 -0
- package/dist/assets/manifest-default.js.map +1 -0
- package/dist/assets/manifest-filename.d.ts +52 -0
- package/dist/assets/manifest-filename.d.ts.map +1 -0
- package/dist/assets/manifest-filename.js +104 -0
- package/dist/assets/manifest-filename.js.map +1 -0
- package/dist/assets/manifest-locale.d.ts +60 -0
- package/dist/assets/manifest-locale.d.ts.map +1 -0
- package/dist/assets/manifest-locale.js +206 -0
- package/dist/assets/manifest-locale.js.map +1 -0
- package/dist/assets/manifest-merge.d.ts +66 -0
- package/dist/assets/manifest-merge.d.ts.map +1 -0
- package/dist/assets/manifest-merge.js +82 -0
- package/dist/assets/manifest-merge.js.map +1 -0
- package/dist/assets/manifest.d.ts +83 -0
- package/dist/assets/manifest.d.ts.map +1 -0
- package/dist/assets/manifest.js +93 -0
- package/dist/assets/manifest.js.map +1 -0
- package/dist/assets/mime-sniff.d.ts +18 -0
- package/dist/assets/mime-sniff.d.ts.map +1 -0
- package/dist/assets/mime-sniff.js +84 -0
- package/dist/assets/mime-sniff.js.map +1 -0
- package/dist/assets/preprocess-svg.d.ts +3 -0
- package/dist/assets/preprocess-svg.d.ts.map +1 -0
- package/dist/assets/preprocess-svg.js +49 -0
- package/dist/assets/preprocess-svg.js.map +1 -0
- package/dist/assets/preprocess.d.ts +62 -0
- package/dist/assets/preprocess.d.ts.map +1 -0
- package/dist/assets/preprocess.js +86 -0
- package/dist/assets/preprocess.js.map +1 -0
- package/dist/assets/publish-plan.d.ts +41 -0
- package/dist/assets/publish-plan.d.ts.map +1 -0
- package/dist/assets/publish-plan.js +49 -0
- package/dist/assets/publish-plan.js.map +1 -0
- package/dist/assets/publish.d.ts +33 -0
- package/dist/assets/publish.d.ts.map +1 -0
- package/dist/assets/publish.js +81 -0
- package/dist/assets/publish.js.map +1 -0
- package/dist/assets/refs.d.ts +37 -0
- package/dist/assets/refs.d.ts.map +1 -0
- package/dist/assets/refs.js +33 -0
- package/dist/assets/refs.js.map +1 -0
- package/dist/assets/remove-override.d.ts +42 -0
- package/dist/assets/remove-override.d.ts.map +1 -0
- package/dist/assets/remove-override.js +53 -0
- package/dist/assets/remove-override.js.map +1 -0
- package/dist/assets/rename.d.ts +43 -0
- package/dist/assets/rename.d.ts.map +1 -0
- package/dist/assets/rename.js +271 -0
- package/dist/assets/rename.js.map +1 -0
- package/dist/assets/replace.d.ts +37 -0
- package/dist/assets/replace.d.ts.map +1 -0
- package/dist/assets/replace.js +195 -0
- package/dist/assets/replace.js.map +1 -0
- package/dist/assets/resolve.d.ts +141 -0
- package/dist/assets/resolve.d.ts.map +1 -0
- package/dist/assets/resolve.js +381 -0
- package/dist/assets/resolve.js.map +1 -0
- package/dist/assets/rewrite-manifest-asset-ref.d.ts +44 -0
- package/dist/assets/rewrite-manifest-asset-ref.d.ts.map +1 -0
- package/dist/assets/rewrite-manifest-asset-ref.js +51 -0
- package/dist/assets/rewrite-manifest-asset-ref.js.map +1 -0
- package/dist/assets/scan-manifest-for-asset.d.ts +63 -0
- package/dist/assets/scan-manifest-for-asset.d.ts.map +1 -0
- package/dist/assets/scan-manifest-for-asset.js +105 -0
- package/dist/assets/scan-manifest-for-asset.js.map +1 -0
- package/dist/assets/serve-route.d.ts +45 -0
- package/dist/assets/serve-route.d.ts.map +1 -0
- package/dist/assets/serve-route.js +123 -0
- package/dist/assets/serve-route.js.map +1 -0
- package/dist/assets/svg-sanitize.d.ts +38 -0
- package/dist/assets/svg-sanitize.d.ts.map +1 -0
- package/dist/assets/svg-sanitize.js +209 -0
- package/dist/assets/svg-sanitize.js.map +1 -0
- package/dist/assets/update-metadata.d.ts +61 -0
- package/dist/assets/update-metadata.d.ts.map +1 -0
- package/dist/assets/update-metadata.js +82 -0
- package/dist/assets/update-metadata.js.map +1 -0
- package/dist/assets/url.d.ts +82 -0
- package/dist/assets/url.d.ts.map +1 -0
- package/dist/assets/url.js +103 -0
- package/dist/assets/url.js.map +1 -0
- package/dist/assets/validate.d.ts +74 -0
- package/dist/assets/validate.d.ts.map +1 -0
- package/dist/assets/validate.js +136 -0
- package/dist/assets/validate.js.map +1 -0
- package/dist/assets/variants.d.ts +23 -0
- package/dist/assets/variants.d.ts.map +1 -0
- package/dist/assets/variants.js +74 -0
- package/dist/assets/variants.js.map +1 -0
- package/dist/cli/assets-cli.d.ts +58 -0
- package/dist/cli/assets-cli.d.ts.map +1 -0
- package/dist/cli/assets-cli.js +233 -0
- package/dist/cli/assets-cli.js.map +1 -0
- package/dist/cli/assets-display.d.ts +112 -0
- package/dist/cli/assets-display.d.ts.map +1 -0
- package/dist/cli/assets-display.js +106 -0
- package/dist/cli/assets-display.js.map +1 -0
- package/dist/cli/bootstrap.d.ts +46 -0
- package/dist/cli/bootstrap.d.ts.map +1 -0
- package/dist/cli/bootstrap.js +84 -0
- package/dist/cli/bootstrap.js.map +1 -0
- package/dist/cli/history.d.ts +45 -0
- package/dist/cli/history.d.ts.map +1 -0
- package/dist/cli/history.js +165 -0
- package/dist/cli/history.js.map +1 -0
- package/dist/cli/index.js +689 -128
- package/dist/cli/index.js.map +1 -1
- package/dist/compare.d.ts +8 -5
- package/dist/compare.d.ts.map +1 -1
- package/dist/compare.js +62 -20
- package/dist/compare.js.map +1 -1
- package/dist/content-root.d.ts +38 -0
- package/dist/content-root.d.ts.map +1 -0
- package/dist/content-root.js +29 -0
- package/dist/content-root.js.map +1 -0
- package/dist/dep-sidecars.d.ts +127 -0
- package/dist/dep-sidecars.d.ts.map +1 -0
- package/dist/dep-sidecars.js +122 -0
- package/dist/dep-sidecars.js.map +1 -0
- package/dist/editor/AssetEmbeddedWidget.d.ts +3 -0
- package/dist/editor/AssetEmbeddedWidget.d.ts.map +1 -0
- package/dist/editor/AssetEmbeddedWidget.js +146 -0
- package/dist/editor/AssetEmbeddedWidget.js.map +1 -0
- package/dist/editor/mount.d.ts +12 -1
- package/dist/editor/mount.d.ts.map +1 -1
- package/dist/editor/mount.js +96 -33
- package/dist/editor/mount.js.map +1 -1
- package/dist/format.d.ts +44 -0
- package/dist/format.d.ts.map +1 -0
- package/dist/format.js +65 -0
- package/dist/format.js.map +1 -0
- package/dist/fragment-deps.d.ts +24 -0
- package/dist/fragment-deps.d.ts.map +1 -0
- package/dist/fragment-deps.js +20 -0
- package/dist/fragment-deps.js.map +1 -0
- package/dist/hash.d.ts +34 -9
- package/dist/hash.d.ts.map +1 -1
- package/dist/hash.js +60 -21
- package/dist/hash.js.map +1 -1
- package/dist/history-provider.d.ts +49 -0
- package/dist/history-provider.d.ts.map +1 -0
- package/dist/history-provider.js +248 -0
- package/dist/history-provider.js.map +1 -0
- package/dist/history-recorder.d.ts +102 -0
- package/dist/history-recorder.d.ts.map +1 -0
- package/dist/history-recorder.js +168 -0
- package/dist/history-recorder.js.map +1 -0
- package/dist/history-restorer.d.ts +46 -0
- package/dist/history-restorer.d.ts.map +1 -0
- package/dist/history-restorer.js +137 -0
- package/dist/history-restorer.js.map +1 -0
- package/dist/history.d.ts +129 -0
- package/dist/history.d.ts.map +1 -0
- package/dist/history.js +25 -0
- package/dist/history.js.map +1 -0
- package/dist/index.d.ts +27 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -4
- package/dist/index.js.map +1 -1
- package/dist/locale.d.ts +94 -0
- package/dist/locale.d.ts.map +1 -0
- package/dist/locale.js +188 -0
- package/dist/locale.js.map +1 -0
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js +16 -1
- package/dist/manifest.js.map +1 -1
- package/dist/providers/_atomic-write.d.ts +9 -0
- package/dist/providers/_atomic-write.d.ts.map +1 -0
- package/dist/providers/_atomic-write.js +72 -0
- package/dist/providers/_atomic-write.js.map +1 -0
- package/dist/providers/_rm-ignore-missing.d.ts +31 -0
- package/dist/providers/_rm-ignore-missing.d.ts.map +1 -0
- package/dist/providers/_rm-ignore-missing.js +12 -0
- package/dist/providers/_rm-ignore-missing.js.map +1 -0
- package/dist/providers/_stream-interop.d.ts +23 -0
- package/dist/providers/_stream-interop.d.ts.map +1 -0
- package/dist/providers/_stream-interop.js +21 -0
- package/dist/providers/_stream-interop.js.map +1 -0
- package/dist/providers/azure-blob.d.ts.map +1 -1
- package/dist/providers/azure-blob.js +60 -0
- package/dist/providers/azure-blob.js.map +1 -1
- package/dist/providers/filesystem.d.ts +4 -0
- package/dist/providers/filesystem.d.ts.map +1 -1
- package/dist/providers/filesystem.js +63 -2
- package/dist/providers/filesystem.js.map +1 -1
- package/dist/providers/s3.d.ts.map +1 -1
- package/dist/providers/s3.js +107 -16
- package/dist/providers/s3.js.map +1 -1
- package/dist/publish-locale.d.ts +44 -0
- package/dist/publish-locale.d.ts.map +1 -0
- package/dist/publish-locale.js +103 -0
- package/dist/publish-locale.js.map +1 -0
- package/dist/publish-rendered.d.ts +52 -21
- package/dist/publish-rendered.d.ts.map +1 -1
- package/dist/publish-rendered.js +144 -87
- package/dist/publish-rendered.js.map +1 -1
- package/dist/publish.d.ts +18 -19
- package/dist/publish.d.ts.map +1 -1
- package/dist/publish.js +41 -56
- package/dist/publish.js.map +1 -1
- package/dist/renderer.d.ts +14 -4
- package/dist/renderer.d.ts.map +1 -1
- package/dist/renderer.js +35 -23
- package/dist/renderer.js.map +1 -1
- package/dist/resolver.d.ts +17 -2
- package/dist/resolver.d.ts.map +1 -1
- package/dist/resolver.js +112 -16
- package/dist/resolver.js.map +1 -1
- package/dist/robots.d.ts +22 -0
- package/dist/robots.d.ts.map +1 -0
- package/dist/robots.js +25 -0
- package/dist/robots.js.map +1 -0
- package/dist/schema/dimensions.d.ts +78 -0
- package/dist/schema/dimensions.d.ts.map +1 -0
- package/dist/schema/dimensions.js +97 -0
- package/dist/schema/dimensions.js.map +1 -0
- package/dist/schema/helpers.d.ts +108 -0
- package/dist/schema/helpers.d.ts.map +1 -0
- package/dist/schema/helpers.js +133 -0
- package/dist/schema/helpers.js.map +1 -0
- package/dist/schema/index.d.ts +27 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/index.js +25 -0
- package/dist/schema/index.js.map +1 -0
- package/dist/schema/types.d.ts +390 -0
- package/dist/schema/types.d.ts.map +1 -0
- package/dist/schema/types.js +25 -0
- package/dist/schema/types.js.map +1 -0
- package/dist/selector-chain.d.ts +63 -0
- package/dist/selector-chain.d.ts.map +1 -0
- package/dist/selector-chain.js +58 -0
- package/dist/selector-chain.js.map +1 -0
- package/dist/seo.d.ts +56 -0
- package/dist/seo.d.ts.map +1 -0
- package/dist/seo.js +72 -0
- package/dist/seo.js.map +1 -0
- package/dist/serve.d.ts +41 -3
- package/dist/serve.d.ts.map +1 -1
- package/dist/serve.js +206 -65
- package/dist/serve.js.map +1 -1
- package/dist/sidecars.d.ts +26 -21
- package/dist/sidecars.d.ts.map +1 -1
- package/dist/sidecars.js +143 -45
- package/dist/sidecars.js.map +1 -1
- package/dist/site-loader.d.ts +74 -6
- package/dist/site-loader.d.ts.map +1 -1
- package/dist/site-loader.js +138 -28
- package/dist/site-loader.js.map +1 -1
- package/dist/sitemap.d.ts +45 -0
- package/dist/sitemap.d.ts.map +1 -0
- package/dist/sitemap.js +67 -0
- package/dist/sitemap.js.map +1 -0
- package/dist/targets.d.ts +47 -1
- package/dist/targets.d.ts.map +1 -1
- package/dist/targets.js +83 -36
- package/dist/targets.js.map +1 -1
- package/dist/template-loader.d.ts +7 -3
- package/dist/template-loader.d.ts.map +1 -1
- package/dist/template-loader.js +27 -12
- package/dist/template-loader.js.map +1 -1
- package/dist/templates-scan-worker.js +1 -1
- package/dist/templates-scan-worker.js.map +1 -1
- package/dist/templates-scan.d.ts.map +1 -1
- package/dist/templates-scan.js +1 -1
- package/dist/templates-scan.js.map +1 -1
- package/dist/themes.d.ts +69 -0
- package/dist/themes.d.ts.map +1 -0
- package/dist/themes.js +85 -0
- package/dist/themes.js.map +1 -0
- package/dist/transforms/adapter.d.ts +115 -0
- package/dist/transforms/adapter.d.ts.map +1 -0
- package/dist/transforms/adapter.js +2 -0
- package/dist/transforms/adapter.js.map +1 -0
- package/dist/transforms/cloudflare.d.ts +17 -0
- package/dist/transforms/cloudflare.d.ts.map +1 -0
- package/dist/transforms/cloudflare.js +110 -0
- package/dist/transforms/cloudflare.js.map +1 -0
- package/dist/transforms/index.d.ts +24 -0
- package/dist/transforms/index.d.ts.map +1 -0
- package/dist/transforms/index.js +30 -0
- package/dist/transforms/index.js.map +1 -0
- package/dist/transforms/sharp.d.ts +3 -0
- package/dist/transforms/sharp.d.ts.map +1 -0
- package/dist/transforms/sharp.js +43 -0
- package/dist/transforms/sharp.js.map +1 -0
- package/dist/types.d.ts +241 -10
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +28 -5
- package/dist/types.js.map +1 -1
- package/dist/workers/cloudflare-r2.d.ts +11 -2
- package/dist/workers/cloudflare-r2.d.ts.map +1 -1
- package/dist/workers/cloudflare-r2.js +120 -55
- package/dist/workers/cloudflare-r2.js.map +1 -1
- package/package.json +30 -2
- package/admin-dist/assets/index-BZAFKsUp.js +0 -608
- package/admin-dist/assets/index-BpRotMuK.css +0 -1
- package/admin-dist/assets/vendor-vue-DSjyxCX6.js +0 -1
- package/dist/providers/r2.d.ts +0 -8
- package/dist/providers/r2.d.ts.map +0 -1
- package/dist/providers/r2.js +0 -83
- package/dist/providers/r2.js.map +0 -1
- package/dist/source-sidecars.d.ts +0 -13
- package/dist/source-sidecars.d.ts.map +0 -1
- package/dist/source-sidecars.js +0 -52
- package/dist/source-sidecars.js.map +0 -1
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scan a single page/fragment manifest for references to a given asset.
|
|
3
|
+
*
|
|
4
|
+
* Single responsibility: pure recursive walk of one manifest's content +
|
|
5
|
+
* nested inline components, emitting an `AssetRef` per match. No I/O,
|
|
6
|
+
* no site-wide knowledge — this is the inner kernel.
|
|
7
|
+
*
|
|
8
|
+
* Two consumers:
|
|
9
|
+
* - `find-refs.ts` — loads the whole site and runs this walker over
|
|
10
|
+
* every page/fragment manifest (including locale variants)
|
|
11
|
+
* - future `.refs/` index writer — on every manifest save, runs this
|
|
12
|
+
* walker over the just-saved manifest to update the index incrementally
|
|
13
|
+
*
|
|
14
|
+
* Splitting the walker from the site loader lets both use the same
|
|
15
|
+
* matching logic — the index writer can't call the site-wide scanner
|
|
16
|
+
* for one-manifest updates (wrong granularity, wrong cost).
|
|
17
|
+
*
|
|
18
|
+
* Match shape:
|
|
19
|
+
* Any object value with a string-valued `_asset` property. The design
|
|
20
|
+
* reserves the `_` prefix for Gazetta-interpreted fields, so false
|
|
21
|
+
* positives require authors to violate the convention.
|
|
22
|
+
*/
|
|
23
|
+
import type { ComponentEntry, ComponentManifest } from '../types.js';
|
|
24
|
+
import type { AssetRef } from './refs.js';
|
|
25
|
+
export interface ScanManifestInput {
|
|
26
|
+
/** The manifest being scanned (page or fragment). */
|
|
27
|
+
readonly manifest: ComponentManifest;
|
|
28
|
+
/** Content-root-relative path of this manifest (for `AssetRef.path`). */
|
|
29
|
+
readonly manifestPath: string;
|
|
30
|
+
/** Which `AssetRef.source` discriminator to tag matches with. */
|
|
31
|
+
readonly source: 'page' | 'fragment';
|
|
32
|
+
/** The asset name to look for. */
|
|
33
|
+
readonly assetName: string;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* The shape the walker operates on. Any object that might carry content
|
|
37
|
+
* or nested components — covers both `ComponentManifest` (no `name`) and
|
|
38
|
+
* inline components (`name` + the same `content`/`components`). One
|
|
39
|
+
* interface, no union — the walker treats `name` as optional and the
|
|
40
|
+
* structural-path computation falls back to the array index when it's
|
|
41
|
+
* absent.
|
|
42
|
+
*/
|
|
43
|
+
interface Walkable {
|
|
44
|
+
readonly name?: string;
|
|
45
|
+
readonly content?: Record<string, unknown>;
|
|
46
|
+
readonly components?: readonly ComponentEntry[];
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Walk `input.manifest` and return every reference to `assetName`. One
|
|
50
|
+
* entry per match — a manifest that references the asset twice (in two
|
|
51
|
+
* different inline components, or twice in the same content blob)
|
|
52
|
+
* produces two entries with different `componentPath` values.
|
|
53
|
+
*/
|
|
54
|
+
export declare function scanManifestForAsset(input: ScanManifestInput): AssetRef[];
|
|
55
|
+
/**
|
|
56
|
+
* Collect every asset name referenced by `manifest`. Companion to
|
|
57
|
+
* `scanManifestForAsset` but returns the *set* of names rather than
|
|
58
|
+
* located refs — for callers that need the dependency list, not the
|
|
59
|
+
* usage breadcrumb. Used by publish to know which assets to copy.
|
|
60
|
+
*/
|
|
61
|
+
export declare function collectAssetRefs(manifest: Walkable): Set<string>;
|
|
62
|
+
export {};
|
|
63
|
+
//# sourceMappingURL=scan-manifest-for-asset.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scan-manifest-for-asset.d.ts","sourceRoot":"","sources":["../../src/assets/scan-manifest-for-asset.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AACpE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AAEzC,MAAM,WAAW,iBAAiB;IAChC,qDAAqD;IACrD,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAA;IACpC,yEAAyE;IACzE,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,iEAAiE;IACjE,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAAA;IACpC,kCAAkC;IAClC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAC3B;AAED;;;;;;;GAOG;AACH,UAAU,QAAQ;IAChB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC1C,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,cAAc,EAAE,CAAA;CAChD;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,iBAAiB,GAAG,QAAQ,EAAE,CAIzE;AA2DD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,GAAG,GAAG,CAAC,MAAM,CAAC,CAIhE"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Walk `input.manifest` and return every reference to `assetName`. One
|
|
3
|
+
* entry per match — a manifest that references the asset twice (in two
|
|
4
|
+
* different inline components, or twice in the same content blob)
|
|
5
|
+
* produces two entries with different `componentPath` values.
|
|
6
|
+
*/
|
|
7
|
+
export function scanManifestForAsset(input) {
|
|
8
|
+
const out = [];
|
|
9
|
+
walkComponent(input.manifest, '', input, out);
|
|
10
|
+
return out;
|
|
11
|
+
}
|
|
12
|
+
/** Walk a component (page manifest, fragment manifest, or inline child). */
|
|
13
|
+
function walkComponent(comp, componentPath, input, out) {
|
|
14
|
+
if (comp.content) {
|
|
15
|
+
scanValue(comp.content, componentPath, input, out);
|
|
16
|
+
}
|
|
17
|
+
// Fragment refs like "@header" don't carry content here — they're
|
|
18
|
+
// scanned as their own manifests by the orchestrator.
|
|
19
|
+
if (comp.components) {
|
|
20
|
+
for (let i = 0; i < comp.components.length; i++) {
|
|
21
|
+
const child = comp.components[i];
|
|
22
|
+
if (typeof child === 'string')
|
|
23
|
+
continue;
|
|
24
|
+
const childPath = componentPath ? `${componentPath}.${nameFor(child, i)}` : nameFor(child, i);
|
|
25
|
+
walkComponent(child, childPath, input, out);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function nameFor(comp, index) {
|
|
30
|
+
return comp.name ?? `[${index}]`;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Recursively walk a value looking for `{ _asset: "<assetName>", ... }`.
|
|
34
|
+
* Matches only when `_asset` is a string equal to the target — avoids
|
|
35
|
+
* false positives on numeric or unrelated keys.
|
|
36
|
+
*/
|
|
37
|
+
function scanValue(value, path, input, out) {
|
|
38
|
+
if (value === null || typeof value !== 'object')
|
|
39
|
+
return;
|
|
40
|
+
if (Array.isArray(value)) {
|
|
41
|
+
for (let i = 0; i < value.length; i++) {
|
|
42
|
+
scanValue(value[i], path ? `${path}[${i}]` : `[${i}]`, input, out);
|
|
43
|
+
}
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const obj = value;
|
|
47
|
+
const assetField = obj._asset;
|
|
48
|
+
if (typeof assetField === 'string' && assetField === input.assetName) {
|
|
49
|
+
out.push({
|
|
50
|
+
source: input.source,
|
|
51
|
+
path: input.manifestPath,
|
|
52
|
+
// `null` means "the ref is at the manifest's top-level content" —
|
|
53
|
+
// no sentinel string (like "<root>") for UI code to compare against.
|
|
54
|
+
componentPath: path === '' ? null : path,
|
|
55
|
+
});
|
|
56
|
+
// Don't early-return — the asset ref object might itself contain
|
|
57
|
+
// nested refs (a future template that composes embedded refs inside
|
|
58
|
+
// an asset ref). Keep walking.
|
|
59
|
+
}
|
|
60
|
+
for (const [key, child] of Object.entries(obj)) {
|
|
61
|
+
if (key === '_asset')
|
|
62
|
+
continue;
|
|
63
|
+
scanValue(child, path ? `${path}.${key}` : key, input, out);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Collect every asset name referenced by `manifest`. Companion to
|
|
68
|
+
* `scanManifestForAsset` but returns the *set* of names rather than
|
|
69
|
+
* located refs — for callers that need the dependency list, not the
|
|
70
|
+
* usage breadcrumb. Used by publish to know which assets to copy.
|
|
71
|
+
*/
|
|
72
|
+
export function collectAssetRefs(manifest) {
|
|
73
|
+
const names = new Set();
|
|
74
|
+
collectFromComponent(manifest, names);
|
|
75
|
+
return names;
|
|
76
|
+
}
|
|
77
|
+
function collectFromComponent(comp, out) {
|
|
78
|
+
if (comp.content)
|
|
79
|
+
collectFromValue(comp.content, out);
|
|
80
|
+
if (comp.components) {
|
|
81
|
+
for (const child of comp.components) {
|
|
82
|
+
if (typeof child === 'string')
|
|
83
|
+
continue;
|
|
84
|
+
collectFromComponent(child, out);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function collectFromValue(value, out) {
|
|
89
|
+
if (value === null || typeof value !== 'object')
|
|
90
|
+
return;
|
|
91
|
+
if (Array.isArray(value)) {
|
|
92
|
+
for (const item of value)
|
|
93
|
+
collectFromValue(item, out);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const obj = value;
|
|
97
|
+
if (typeof obj._asset === 'string')
|
|
98
|
+
out.add(obj._asset);
|
|
99
|
+
for (const [key, child] of Object.entries(obj)) {
|
|
100
|
+
if (key === '_asset')
|
|
101
|
+
continue;
|
|
102
|
+
collectFromValue(child, out);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=scan-manifest-for-asset.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scan-manifest-for-asset.js","sourceRoot":"","sources":["../../src/assets/scan-manifest-for-asset.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP route: `GET /assets/*` — serve asset bytes from a storage provider.
|
|
3
|
+
*
|
|
4
|
+
* Thin adapter. Opens a `readStream` and pipes it to the response with
|
|
5
|
+
* Content-Type + cache headers. Range request support lets `<video>`
|
|
6
|
+
* seek and `<audio>` resume.
|
|
7
|
+
*
|
|
8
|
+
* Mount this at the top level of the serving app (dev server,
|
|
9
|
+
* `gazetta serve`), NOT under `/admin` — the resolver emits root-relative
|
|
10
|
+
* `/assets/...` URLs that must match this route.
|
|
11
|
+
*
|
|
12
|
+
* The factory takes a **storage resolver** + an optional **adapter
|
|
13
|
+
* resolver**. Storage gives us the bytes; adapter gives us the cache
|
|
14
|
+
* policy. Without an adapter resolver, the route falls back to the
|
|
15
|
+
* sharp adapter's policy (immutable, the v1 default) — that keeps
|
|
16
|
+
* dev/test flows that don't wire transform config working unchanged.
|
|
17
|
+
*
|
|
18
|
+
* Security:
|
|
19
|
+
* - Rejects paths containing `..` (belt-and-suspenders)
|
|
20
|
+
* - Cache headers come from `adapter.cachePolicy()` — whatever the
|
|
21
|
+
* adapter says is right for the URLs it produces. Default
|
|
22
|
+
* (sharp adapter) returns `immutable` because hash-in-path is
|
|
23
|
+
* content-addressed: new bytes mean a new URL.
|
|
24
|
+
*/
|
|
25
|
+
import { Hono } from 'hono';
|
|
26
|
+
import type { StorageProvider } from '../types.js';
|
|
27
|
+
import type { TransformAdapter } from '../transforms/adapter.js';
|
|
28
|
+
/**
|
|
29
|
+
* Resolve the storage provider to serve from, given an optional target
|
|
30
|
+
* query param. Narrower than the admin-api SourceContextResolver — this
|
|
31
|
+
* route only needs storage, not the rest of the source context.
|
|
32
|
+
*/
|
|
33
|
+
export type AssetStorageResolver = (targetName: string | undefined) => Promise<StorageProvider>;
|
|
34
|
+
/**
|
|
35
|
+
* Resolve the transform adapter for the active target. Per-target
|
|
36
|
+
* because adapter config (and thus cache policy) is per-target.
|
|
37
|
+
* Optional — when absent, the route uses the sharp adapter's policy.
|
|
38
|
+
*/
|
|
39
|
+
export type AssetAdapterResolver = (targetName: string | undefined) => Promise<TransformAdapter>;
|
|
40
|
+
export interface AssetServeRoutesOptions {
|
|
41
|
+
resolveStorage: AssetStorageResolver;
|
|
42
|
+
resolveAdapter?: AssetAdapterResolver;
|
|
43
|
+
}
|
|
44
|
+
export declare function assetServeRoutes(resolveStorageOrOptions: AssetStorageResolver | AssetServeRoutesOptions): Hono;
|
|
45
|
+
//# sourceMappingURL=serve-route.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serve-route.d.ts","sourceRoot":"","sources":["../../src/assets/serve-route.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAE3B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAClD,OAAO,KAAK,EAAiB,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AAa/E;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,KAAK,OAAO,CAAC,eAAe,CAAC,CAAA;AAE/F;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,KAAK,OAAO,CAAC,gBAAgB,CAAC,CAAA;AAEhG,MAAM,WAAW,uBAAuB;IACtC,cAAc,EAAE,oBAAoB,CAAA;IACpC,cAAc,CAAC,EAAE,oBAAoB,CAAA;CACtC;AAED,wBAAgB,gBAAgB,CAAC,uBAAuB,EAAE,oBAAoB,GAAG,uBAAuB,GAAG,IAAI,CAsD9G"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP route: `GET /assets/*` — serve asset bytes from a storage provider.
|
|
3
|
+
*
|
|
4
|
+
* Thin adapter. Opens a `readStream` and pipes it to the response with
|
|
5
|
+
* Content-Type + cache headers. Range request support lets `<video>`
|
|
6
|
+
* seek and `<audio>` resume.
|
|
7
|
+
*
|
|
8
|
+
* Mount this at the top level of the serving app (dev server,
|
|
9
|
+
* `gazetta serve`), NOT under `/admin` — the resolver emits root-relative
|
|
10
|
+
* `/assets/...` URLs that must match this route.
|
|
11
|
+
*
|
|
12
|
+
* The factory takes a **storage resolver** + an optional **adapter
|
|
13
|
+
* resolver**. Storage gives us the bytes; adapter gives us the cache
|
|
14
|
+
* policy. Without an adapter resolver, the route falls back to the
|
|
15
|
+
* sharp adapter's policy (immutable, the v1 default) — that keeps
|
|
16
|
+
* dev/test flows that don't wire transform config working unchanged.
|
|
17
|
+
*
|
|
18
|
+
* Security:
|
|
19
|
+
* - Rejects paths containing `..` (belt-and-suspenders)
|
|
20
|
+
* - Cache headers come from `adapter.cachePolicy()` — whatever the
|
|
21
|
+
* adapter says is right for the URLs it produces. Default
|
|
22
|
+
* (sharp adapter) returns `immutable` because hash-in-path is
|
|
23
|
+
* content-addressed: new bytes mean a new URL.
|
|
24
|
+
*/
|
|
25
|
+
import { Hono } from 'hono';
|
|
26
|
+
import { stream } from 'hono/streaming';
|
|
27
|
+
import { sharpAdapter } from '../transforms/sharp.js';
|
|
28
|
+
/** Where assets live, relative to the target storage root. */
|
|
29
|
+
const ASSETS_ROOT = 'assets';
|
|
30
|
+
/** MIME lookup for extensions we produce in v1 (JPEG, PNG). */
|
|
31
|
+
const MIME_BY_EXT = {
|
|
32
|
+
jpg: 'image/jpeg',
|
|
33
|
+
jpeg: 'image/jpeg',
|
|
34
|
+
png: 'image/png',
|
|
35
|
+
};
|
|
36
|
+
export function assetServeRoutes(resolveStorageOrOptions) {
|
|
37
|
+
// Accept either the legacy `resolveStorage` callable or an options
|
|
38
|
+
// object. The callable form keeps the pre-step-18 signature working
|
|
39
|
+
// for callers that haven't migrated.
|
|
40
|
+
const opts = typeof resolveStorageOrOptions === 'function'
|
|
41
|
+
? { resolveStorage: resolveStorageOrOptions }
|
|
42
|
+
: resolveStorageOrOptions;
|
|
43
|
+
const app = new Hono();
|
|
44
|
+
app.get('/assets/*', async (c) => {
|
|
45
|
+
const url = new URL(c.req.url);
|
|
46
|
+
const path = url.pathname.replace(/^\/assets\//, '');
|
|
47
|
+
if (path.includes('..') || path.startsWith('/')) {
|
|
48
|
+
return c.text('Invalid asset path', 400);
|
|
49
|
+
}
|
|
50
|
+
const targetName = c.req.query('target');
|
|
51
|
+
const storage = await opts.resolveStorage(targetName);
|
|
52
|
+
const storagePath = `${ASSETS_ROOT}/${path}`;
|
|
53
|
+
if (!(await storage.exists(storagePath))) {
|
|
54
|
+
return c.text('Not found', 404);
|
|
55
|
+
}
|
|
56
|
+
const ext = path.split('.').pop()?.toLowerCase() ?? '';
|
|
57
|
+
const mime = MIME_BY_EXT[ext] ?? 'application/octet-stream';
|
|
58
|
+
const range = parseRange(c.req.header('range'));
|
|
59
|
+
// Cache policy from the adapter. The route only has the URL path,
|
|
60
|
+
// so it constructs a minimal AssetUrlInput — name/hash/ext parsed
|
|
61
|
+
// from the filename. Today's adapters return constant policies
|
|
62
|
+
// regardless of input; future per-input policy adapters will have
|
|
63
|
+
// what they need.
|
|
64
|
+
const adapter = opts.resolveAdapter ? await opts.resolveAdapter(targetName) : sharpAdapter;
|
|
65
|
+
const policy = adapter.cachePolicy(parseAssetUrlInput(path, ext));
|
|
66
|
+
try {
|
|
67
|
+
const bodyStream = await storage.readStream(storagePath, range);
|
|
68
|
+
c.header('Content-Type', mime);
|
|
69
|
+
c.header('X-Content-Type-Options', 'nosniff');
|
|
70
|
+
c.header('Cache-Control', policy.cacheControl);
|
|
71
|
+
if (policy.vary)
|
|
72
|
+
c.header('Vary', policy.vary);
|
|
73
|
+
return stream(c, async (out) => {
|
|
74
|
+
await out.pipe(bodyStream);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return c.text('Error reading asset', 500);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
return app;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Parse a minimal `AssetUrlInput` from the URL path. Used only for the
|
|
85
|
+
* `cachePolicy()` call — the route doesn't need to recover full
|
|
86
|
+
* variants/dimensions, just whatever the adapter might key on.
|
|
87
|
+
*
|
|
88
|
+
* Filename shape: `{name}-{hash}[.{loc}][.{theme}][-{w}w].{ext}`. We
|
|
89
|
+
* extract `name` and `hash`; selector/variants/dims default to nulls.
|
|
90
|
+
* Future adapters that need more (e.g. per-locale cache TTL) would
|
|
91
|
+
* expand this parser or we'd thread the resolver-time inputs through
|
|
92
|
+
* a different path.
|
|
93
|
+
*/
|
|
94
|
+
function parseAssetUrlInput(path, ext) {
|
|
95
|
+
// Strip extension.
|
|
96
|
+
const stem = path.replace(/\.[^.]+$/, '');
|
|
97
|
+
// Match `{name}-{hash}` where hash is 8 hex chars; everything after
|
|
98
|
+
// the first hash-suffix is selector + width.
|
|
99
|
+
const m = /^(.+)-([0-9a-f]{8})/.exec(stem);
|
|
100
|
+
const name = m ? m[1] : stem;
|
|
101
|
+
const hash = m ? m[2] : '';
|
|
102
|
+
return {
|
|
103
|
+
name,
|
|
104
|
+
hash,
|
|
105
|
+
ext,
|
|
106
|
+
selector: null,
|
|
107
|
+
variants: [],
|
|
108
|
+
width: null,
|
|
109
|
+
height: null,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
/** Parse a byte-range HTTP header into the storage provider's `ByteRange`. */
|
|
113
|
+
function parseRange(header) {
|
|
114
|
+
if (!header)
|
|
115
|
+
return undefined;
|
|
116
|
+
const match = /^bytes=(\d+)-(\d+)?$/.exec(header);
|
|
117
|
+
if (!match)
|
|
118
|
+
return undefined;
|
|
119
|
+
const start = Number.parseInt(match[1], 10);
|
|
120
|
+
const end = match[2] ? Number.parseInt(match[2], 10) : Number.POSITIVE_INFINITY;
|
|
121
|
+
return Number.isFinite(end) ? { start, end } : undefined;
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=serve-route.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serve-route.js","sourceRoot":"","sources":["../../src/assets/serve-route.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export interface SvgSanitizeResult {
|
|
2
|
+
/** Sanitized SVG bytes. UTF-8 encoded. */
|
|
3
|
+
bytes: Uint8Array;
|
|
4
|
+
/** Non-fatal warnings (e.g., large embedded base64). Empty when clean. */
|
|
5
|
+
warnings: readonly SvgSanitizeWarning[];
|
|
6
|
+
}
|
|
7
|
+
export type SvgSanitizeWarning = {
|
|
8
|
+
code: 'large-base64';
|
|
9
|
+
sizeBytes: number;
|
|
10
|
+
threshold: number;
|
|
11
|
+
} | {
|
|
12
|
+
code: 'stripped-element';
|
|
13
|
+
tag: string;
|
|
14
|
+
} | {
|
|
15
|
+
code: 'stripped-attribute';
|
|
16
|
+
attr: string;
|
|
17
|
+
on: string;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Thrown when sanitization can't proceed safely. Callers map to a
|
|
21
|
+
* 400 Bad Request (it's a client-correctable input failure).
|
|
22
|
+
*/
|
|
23
|
+
export declare class SvgSanitizeError extends Error {
|
|
24
|
+
readonly code: 'malformed-xml' | 'oversized-base64' | 'empty';
|
|
25
|
+
constructor(code: 'malformed-xml' | 'oversized-base64' | 'empty', message: string);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Sanitize an SVG. Returns the cleaned bytes + any warnings.
|
|
29
|
+
*
|
|
30
|
+
* Throws `SvgSanitizeError` on inputs that can't be safely cleaned —
|
|
31
|
+
* malformed XML, oversized embedded base64, or empty results after
|
|
32
|
+
* sanitization (which would indicate the input was entirely
|
|
33
|
+
* disallowed content).
|
|
34
|
+
*/
|
|
35
|
+
export declare function sanitizeSvg(input: Uint8Array): SvgSanitizeResult;
|
|
36
|
+
/** Canonical IANA MIME for SVG. Re-exported so the preprocessor can match against it. */
|
|
37
|
+
export declare const SVG_MIME_TYPE = "image/svg+xml";
|
|
38
|
+
//# sourceMappingURL=svg-sanitize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"svg-sanitize.d.ts","sourceRoot":"","sources":["../../src/assets/svg-sanitize.ts"],"names":[],"mappings":"AA6DA,MAAM,WAAW,iBAAiB;IAChC,0CAA0C;IAC1C,KAAK,EAAE,UAAU,CAAA;IACjB,0EAA0E;IAC1E,QAAQ,EAAE,SAAS,kBAAkB,EAAE,CAAA;CACxC;AAED,MAAM,MAAM,kBAAkB,GAC1B;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAC9D;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GACzC;IAAE,IAAI,EAAE,oBAAoB,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAA;AAE5D;;;GAGG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,QAAQ,CAAC,IAAI,EAAE,eAAe,GAAG,kBAAkB,GAAG,OAAO,CAAA;gBACjD,IAAI,EAAE,eAAe,GAAG,kBAAkB,GAAG,OAAO,EAAE,OAAO,EAAE,MAAM;CAKlF;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG,iBAAiB,CA+GhE;AAED,yFAAyF;AACzF,eAAO,MAAM,aAAa,kBAAW,CAAA"}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SVG sanitization — strip script-execution surfaces, external network
|
|
3
|
+
* references, and bloat-prone embedded data before the bytes ever touch
|
|
4
|
+
* storage.
|
|
5
|
+
*
|
|
6
|
+
* Why SVG specifically: an SVG is XML and the spec allows scriptable
|
|
7
|
+
* features (event handlers, `<script>`, `<foreignObject>` HTML embedding)
|
|
8
|
+
* plus references to external resources via `href` / `xlink:href`. A
|
|
9
|
+
* malicious SVG renders fine in a browser AND can exfiltrate data, fire
|
|
10
|
+
* tracking pixels, or run JS in same-origin contexts.
|
|
11
|
+
*
|
|
12
|
+
* Design (per design-media.md → Security):
|
|
13
|
+
*
|
|
14
|
+
* Strip:
|
|
15
|
+
* - <script>
|
|
16
|
+
* - event handlers (onload, onclick, onmouseover, etc.)
|
|
17
|
+
* - <foreignObject> — embedded HTML (XSS surface)
|
|
18
|
+
* - <animate> with timed events
|
|
19
|
+
* - external `href` / `xlink:href` on <use>, <image>, <a>, <script>
|
|
20
|
+
* (any URL — privacy + XSS defense)
|
|
21
|
+
*
|
|
22
|
+
* Keep:
|
|
23
|
+
* - <svg> root + path/shape/text/group elements
|
|
24
|
+
* - inline data: URIs (those are local; no network)
|
|
25
|
+
*
|
|
26
|
+
* Warn:
|
|
27
|
+
* - embedded base64 data URI > 100 KB (bloat)
|
|
28
|
+
*
|
|
29
|
+
* Reject:
|
|
30
|
+
* - embedded base64 data URI > 1 MB (bloat heuristic)
|
|
31
|
+
* - malformed XML (caller surfaces as upload validation failure)
|
|
32
|
+
*
|
|
33
|
+
* Hash + ingest contract: the hash is computed on the SANITIZED bytes,
|
|
34
|
+
* not the input. A malicious SVG and a benign SVG that produce
|
|
35
|
+
* identical sanitized output legitimately deduplicate. This means
|
|
36
|
+
* re-uploading the "same" SVG that happens to have script tags as a
|
|
37
|
+
* decoy hashes the same as a clean version — that's correct: content
|
|
38
|
+
* addresses what we keep, not what we received.
|
|
39
|
+
*
|
|
40
|
+
* SSR-safe: `isomorphic-dompurify` works in Node without a DOM
|
|
41
|
+
* polyfill, so we can sanitize at upload (Node) and at any future
|
|
42
|
+
* server-side render path.
|
|
43
|
+
*/
|
|
44
|
+
import DOMPurify from 'isomorphic-dompurify';
|
|
45
|
+
const SVG_MIME = 'image/svg+xml';
|
|
46
|
+
/**
|
|
47
|
+
* Soft warning: embedded base64 data URI larger than this triggers a
|
|
48
|
+
* `large-base64` warning in the result. Authors might be embedding a
|
|
49
|
+
* full PNG inside an SVG — usually a mistake.
|
|
50
|
+
*/
|
|
51
|
+
const BASE64_WARN_BYTES = 100 * 1024;
|
|
52
|
+
/**
|
|
53
|
+
* Hard reject: embedded base64 data URI larger than this fails
|
|
54
|
+
* sanitization. Past this point the SVG is almost always carrying a
|
|
55
|
+
* full image as a workaround that should be a separate asset.
|
|
56
|
+
*/
|
|
57
|
+
const BASE64_REJECT_BYTES = 1024 * 1024;
|
|
58
|
+
/**
|
|
59
|
+
* Thrown when sanitization can't proceed safely. Callers map to a
|
|
60
|
+
* 400 Bad Request (it's a client-correctable input failure).
|
|
61
|
+
*/
|
|
62
|
+
export class SvgSanitizeError extends Error {
|
|
63
|
+
code;
|
|
64
|
+
constructor(code, message) {
|
|
65
|
+
super(message);
|
|
66
|
+
this.code = code;
|
|
67
|
+
this.name = 'SvgSanitizeError';
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Sanitize an SVG. Returns the cleaned bytes + any warnings.
|
|
72
|
+
*
|
|
73
|
+
* Throws `SvgSanitizeError` on inputs that can't be safely cleaned —
|
|
74
|
+
* malformed XML, oversized embedded base64, or empty results after
|
|
75
|
+
* sanitization (which would indicate the input was entirely
|
|
76
|
+
* disallowed content).
|
|
77
|
+
*/
|
|
78
|
+
export function sanitizeSvg(input) {
|
|
79
|
+
const text = new TextDecoder('utf-8', { fatal: false }).decode(input).trim();
|
|
80
|
+
if (text.length === 0) {
|
|
81
|
+
throw new SvgSanitizeError('empty', 'Empty input — not a valid SVG');
|
|
82
|
+
}
|
|
83
|
+
// Fail fast on oversized embedded base64. We could trust DOMPurify
|
|
84
|
+
// to leave it alone (data: URIs in <image> are valid), but the
|
|
85
|
+
// bloat heuristic exists so the resulting asset isn't a 5 MB SVG
|
|
86
|
+
// hiding a JPEG. Detect before sanitization to spare the parse.
|
|
87
|
+
const oversized = findOversizedBase64(text);
|
|
88
|
+
if (oversized !== null) {
|
|
89
|
+
throw new SvgSanitizeError('oversized-base64', `Embedded base64 data URI exceeds ${BASE64_REJECT_BYTES} bytes (got ${oversized}). Upload as a separate asset.`);
|
|
90
|
+
}
|
|
91
|
+
const warnings = [];
|
|
92
|
+
// Track stripped elements + attributes via DOMPurify hooks for
|
|
93
|
+
// diagnostic output, and filter URI-shaped attributes (href,
|
|
94
|
+
// xlink:href) to allow only fragment refs (#id) and inline data:
|
|
95
|
+
// URIs. NOTE: DOMPurify's `ALLOWED_URI_REGEXP` config option is
|
|
96
|
+
// applied to ALL attribute values in some versions, not just
|
|
97
|
+
// URI-shaped ones — `r="5"` on a <circle> would match against it
|
|
98
|
+
// and be stripped. Hook-based filtering is the safe alternative.
|
|
99
|
+
//
|
|
100
|
+
// DOMPurify wraps non-document inputs in `<html><body>` for parsing;
|
|
101
|
+
// its hooks fire on those wrapping elements as "stripped." Filter
|
|
102
|
+
// out the wrapper tags so warnings only reflect content the author
|
|
103
|
+
// actually wrote.
|
|
104
|
+
const HOOK_NOISE = new Set(['html', 'head', 'body']);
|
|
105
|
+
const ALLOWED_URI_PATTERN = /^(?:#|data:image\/(?:png|jpeg|gif|webp);base64,)/i;
|
|
106
|
+
const URI_BEARING_ATTRS = new Set(['href', 'xlink:href', 'src']);
|
|
107
|
+
const removedElements = new Set();
|
|
108
|
+
const removedAttributes = new Set();
|
|
109
|
+
DOMPurify.removeAllHooks();
|
|
110
|
+
DOMPurify.addHook('uponSanitizeElement', (_node, data) => {
|
|
111
|
+
if (HOOK_NOISE.has(data.tagName))
|
|
112
|
+
return;
|
|
113
|
+
if (!data.allowedTags[data.tagName]) {
|
|
114
|
+
removedElements.add(data.tagName);
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
DOMPurify.addHook('uponSanitizeAttribute', (node, data) => {
|
|
118
|
+
const ownerTag = node.nodeName.toLowerCase();
|
|
119
|
+
// URI filtering: drop hrefs that aren't fragment-only or inline data:.
|
|
120
|
+
if (URI_BEARING_ATTRS.has(data.attrName)) {
|
|
121
|
+
if (data.attrValue && !ALLOWED_URI_PATTERN.test(data.attrValue)) {
|
|
122
|
+
data.keepAttr = false;
|
|
123
|
+
removedAttributes.add(`${ownerTag}.${data.attrName}`);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (HOOK_NOISE.has(ownerTag))
|
|
128
|
+
return;
|
|
129
|
+
if (!data.allowedAttributes[data.attrName]) {
|
|
130
|
+
removedAttributes.add(`${ownerTag}.${data.attrName}`);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
let cleaned;
|
|
134
|
+
try {
|
|
135
|
+
cleaned = DOMPurify.sanitize(text, {
|
|
136
|
+
USE_PROFILES: { svg: true },
|
|
137
|
+
// Strip these even within the SVG profile — they're allowed by
|
|
138
|
+
// default but are XSS / privacy surfaces.
|
|
139
|
+
FORBID_TAGS: ['foreignObject', 'script', 'a'],
|
|
140
|
+
FORBID_ATTR: [
|
|
141
|
+
// Event handlers — DOMPurify strips these by default in safe
|
|
142
|
+
// mode but we explicit-list them so config drift doesn't reopen.
|
|
143
|
+
'onload',
|
|
144
|
+
'onclick',
|
|
145
|
+
'onmouseover',
|
|
146
|
+
'onmouseout',
|
|
147
|
+
'onfocus',
|
|
148
|
+
'onblur',
|
|
149
|
+
'onbegin',
|
|
150
|
+
'onend',
|
|
151
|
+
'onrepeat',
|
|
152
|
+
// URI-bearing attributes are filtered via the hook above.
|
|
153
|
+
],
|
|
154
|
+
KEEP_CONTENT: false,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
catch (err) {
|
|
158
|
+
DOMPurify.removeAllHooks();
|
|
159
|
+
throw new SvgSanitizeError('malformed-xml', `Could not parse SVG: ${err.message}`);
|
|
160
|
+
}
|
|
161
|
+
DOMPurify.removeAllHooks();
|
|
162
|
+
if (cleaned.trim().length === 0) {
|
|
163
|
+
throw new SvgSanitizeError('empty', 'Sanitization produced empty output — input had no allowed content');
|
|
164
|
+
}
|
|
165
|
+
// Soft warning: large embedded base64 (didn't trip the hard reject).
|
|
166
|
+
const softLarge = findLargestBase64(cleaned);
|
|
167
|
+
if (softLarge !== null && softLarge >= BASE64_WARN_BYTES) {
|
|
168
|
+
warnings.push({ code: 'large-base64', sizeBytes: softLarge, threshold: BASE64_WARN_BYTES });
|
|
169
|
+
}
|
|
170
|
+
for (const tag of removedElements) {
|
|
171
|
+
warnings.push({ code: 'stripped-element', tag });
|
|
172
|
+
}
|
|
173
|
+
for (const attr of removedAttributes) {
|
|
174
|
+
const [on, attrName] = attr.split('.');
|
|
175
|
+
warnings.push({ code: 'stripped-attribute', attr: attrName ?? attr, on: on ?? '?' });
|
|
176
|
+
}
|
|
177
|
+
return {
|
|
178
|
+
bytes: new TextEncoder().encode(cleaned),
|
|
179
|
+
warnings,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
/** Canonical IANA MIME for SVG. Re-exported so the preprocessor can match against it. */
|
|
183
|
+
export const SVG_MIME_TYPE = SVG_MIME;
|
|
184
|
+
/**
|
|
185
|
+
* Find the largest embedded base64 data URI in the text. Returns the
|
|
186
|
+
* decoded byte size, or null when no data URIs are present.
|
|
187
|
+
*
|
|
188
|
+
* Approximation — base64 encodes 3 bytes per 4 chars (≈ 0.75 ratio).
|
|
189
|
+
* Counting raw characters and multiplying gives byte estimates that
|
|
190
|
+
* are off by ≤2 bytes (padding). Good enough for a size threshold.
|
|
191
|
+
*/
|
|
192
|
+
function findLargestBase64(text) {
|
|
193
|
+
// Anchor on `;base64,` then match the run of base64 chars + padding.
|
|
194
|
+
const re = /;base64,([A-Za-z0-9+/=]+)/g;
|
|
195
|
+
let largest = 0;
|
|
196
|
+
let m;
|
|
197
|
+
while ((m = re.exec(text)) !== null) {
|
|
198
|
+
const charLen = m[1].length;
|
|
199
|
+
const bytes = Math.floor((charLen * 3) / 4);
|
|
200
|
+
if (bytes > largest)
|
|
201
|
+
largest = bytes;
|
|
202
|
+
}
|
|
203
|
+
return largest > 0 ? largest : null;
|
|
204
|
+
}
|
|
205
|
+
function findOversizedBase64(text) {
|
|
206
|
+
const largest = findLargestBase64(text);
|
|
207
|
+
return largest !== null && largest > BASE64_REJECT_BYTES ? largest : null;
|
|
208
|
+
}
|
|
209
|
+
//# sourceMappingURL=svg-sanitize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"svg-sanitize.js","sourceRoot":"","sources":["../../src/assets/svg-sanitize.ts"],"names":[],"mappings":"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"}
|