gazetta 0.6.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/index.html +2 -2
- 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 +0 -2
- package/dist/admin-api/index.d.ts.map +1 -1
- package/dist/admin-api/index.js +4 -24
- 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/fragments.d.ts.map +1 -1
- package/dist/admin-api/routes/fragments.js +30 -4
- package/dist/admin-api/routes/fragments.js.map +1 -1
- package/dist/admin-api/routes/pages.d.ts.map +1 -1
- package/dist/admin-api/routes/pages.js +37 -4
- package/dist/admin-api/routes/pages.js.map +1 -1
- package/dist/admin-api/routes/publish.d.ts.map +1 -1
- package/dist/admin-api/routes/publish.js +68 -35
- package/dist/admin-api/routes/publish.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/index.d.ts +2 -0
- package/dist/admin-api/schemas/index.d.ts.map +1 -1
- package/dist/admin-api/schemas/index.js +2 -0
- package/dist/admin-api/schemas/index.js.map +1 -1
- package/dist/admin-api/source-context.d.ts +0 -7
- package/dist/admin-api/source-context.d.ts.map +1 -1
- package/dist/admin-api/source-context.js +0 -3
- package/dist/admin-api/source-context.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 +0 -2
- package/dist/cli/bootstrap.d.ts.map +1 -1
- package/dist/cli/bootstrap.js +0 -1
- package/dist/cli/bootstrap.js.map +1 -1
- package/dist/cli/index.js +64 -18
- package/dist/cli/index.js.map +1 -1
- package/dist/compare.d.ts.map +1 -1
- package/dist/compare.js +15 -12
- package/dist/compare.js.map +1 -1
- 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 +36 -5
- 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 +0 -6
- package/dist/hash.d.ts.map +1 -1
- package/dist/hash.js +0 -18
- package/dist/hash.js.map +1 -1
- package/dist/history-provider.d.ts.map +1 -1
- package/dist/history-provider.js +30 -8
- package/dist/history-provider.js.map +1 -1
- package/dist/history-recorder.d.ts +7 -3
- package/dist/history-recorder.d.ts.map +1 -1
- package/dist/history-recorder.js +9 -1
- package/dist/history-recorder.js.map +1 -1
- package/dist/history-restorer.d.ts.map +1 -1
- package/dist/history-restorer.js +34 -2
- package/dist/history-restorer.js.map +1 -1
- package/dist/history.d.ts +26 -8
- package/dist/history.d.ts.map +1 -1
- package/dist/index.d.ts +2 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -2
- package/dist/index.js.map +1 -1
- package/dist/locale.d.ts +20 -0
- package/dist/locale.d.ts.map +1 -1
- package/dist/locale.js +38 -0
- package/dist/locale.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 +84 -1
- package/dist/providers/s3.js.map +1 -1
- package/dist/publish-rendered.d.ts +37 -17
- package/dist/publish-rendered.d.ts.map +1 -1
- package/dist/publish-rendered.js +71 -67
- package/dist/publish-rendered.js.map +1 -1
- package/dist/publish.d.ts +13 -12
- package/dist/publish.d.ts.map +1 -1
- package/dist/publish.js +23 -47
- package/dist/publish.js.map +1 -1
- package/dist/resolver.d.ts +12 -2
- package/dist/resolver.d.ts.map +1 -1
- package/dist/resolver.js +54 -9
- package/dist/resolver.js.map +1 -1
- 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/sidecars.d.ts +19 -18
- package/dist/sidecars.d.ts.map +1 -1
- package/dist/sidecars.js +70 -62
- package/dist/sidecars.js.map +1 -1
- package/dist/targets.d.ts.map +1 -1
- package/dist/targets.js +15 -37
- package/dist/targets.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 +125 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +20 -1
- package/admin-dist/assets/index-B6pVot0Y.css +0 -1
- package/admin-dist/assets/index-DniLwxJA.js +0 -609
- package/dist/providers/r2.d.ts +0 -8
- package/dist/providers/r2.d.ts.map +0 -1
- package/dist/providers/r2.js +0 -86
- package/dist/providers/r2.js.map +0 -1
- package/dist/source-sidecars.d.ts +0 -32
- package/dist/source-sidecars.d.ts.map +0 -1
- package/dist/source-sidecars.js +0 -98
- package/dist/source-sidecars.js.map +0 -1
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import { recordWrite } from '../history-recorder.js';
|
|
2
|
+
import { rmIgnoreMissing } from '../providers/_rm-ignore-missing.js';
|
|
3
|
+
import { runAnalyzers } from './analyze.js';
|
|
4
|
+
import { AssetMimeMismatchError, AssetStorageError, AssetVariantGenerationError } from './errors.js';
|
|
5
|
+
import { ASSET_HASH_LENGTH, hashBytes } from './hash.js';
|
|
6
|
+
import { extractImageDimensions } from './image-metadata.js';
|
|
7
|
+
import { assetBytesPath, assetVariantBytesPath, manifestPath, writeManifest } from './manifest.js';
|
|
8
|
+
import { sniffMimeFromStream } from './mime-sniff.js';
|
|
9
|
+
import { validateUpload } from './validate.js';
|
|
10
|
+
import { runPreprocessors } from './preprocess.js';
|
|
11
|
+
import { generateVariants } from './variants.js';
|
|
12
|
+
/**
|
|
13
|
+
* Ext-from-MIME for the v1 allowlist. Image: JPEG, PNG, SVG, GIF.
|
|
14
|
+
* Audio: MP3, WAV, FLAC, Opus, AAC, M4A, OGG. Wide rollout extends
|
|
15
|
+
* this map and the parallel `extFromMime` in `url.ts`.
|
|
16
|
+
*/
|
|
17
|
+
const EXT_BY_MIME = {
|
|
18
|
+
// Images
|
|
19
|
+
'image/jpeg': 'jpg',
|
|
20
|
+
'image/png': 'png',
|
|
21
|
+
'image/svg+xml': 'svg',
|
|
22
|
+
'image/gif': 'gif',
|
|
23
|
+
// Audio
|
|
24
|
+
'audio/mpeg': 'mp3',
|
|
25
|
+
'audio/wav': 'wav',
|
|
26
|
+
'audio/x-wav': 'wav',
|
|
27
|
+
'audio/flac': 'flac',
|
|
28
|
+
'audio/x-flac': 'flac',
|
|
29
|
+
'audio/ogg': 'ogg',
|
|
30
|
+
'audio/opus': 'opus',
|
|
31
|
+
'audio/aac': 'aac',
|
|
32
|
+
'audio/mp4': 'm4a',
|
|
33
|
+
'audio/x-m4a': 'm4a',
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Ingest an asset. On success, bytes are persisted at
|
|
37
|
+
* `{assetsRoot}/{name}-{hash}.{ext}` and the manifest at
|
|
38
|
+
* `{assetsRoot}/{name}.asset.json`.
|
|
39
|
+
*
|
|
40
|
+
* Throws:
|
|
41
|
+
* - `AssetValidationError` — name, size, or MIME failed policy
|
|
42
|
+
* - `AssetStorageError` — storage write failed
|
|
43
|
+
*/
|
|
44
|
+
export async function ingestAsset(input) {
|
|
45
|
+
// Collect bytes once so we can sniff, hash, measure, and persist from the
|
|
46
|
+
// same buffer without re-consuming the source stream.
|
|
47
|
+
const initialBuffer = await collectBytes(input.bytes);
|
|
48
|
+
// Sniff MIME. file-type needs a web stream; build one from the buffer.
|
|
49
|
+
const { mime, ext } = await sniffMimeFromStream(byteStreamFrom(initialBuffer));
|
|
50
|
+
// Validate name + size + MIME against the per-target policy. Size
|
|
51
|
+
// is checked pre-preprocess: the cap protects against worker body
|
|
52
|
+
// limits + storage exhaustion at the request boundary, before any
|
|
53
|
+
// transformation happens. SVG sanitization can shrink the bytes,
|
|
54
|
+
// but a 100 MB SVG-of-doom should be rejected at the door, not
|
|
55
|
+
// after we've parsed it.
|
|
56
|
+
validateUpload({ name: input.requestedName, claimedSize: initialBuffer.byteLength, sniffedMime: mime }, input.policy);
|
|
57
|
+
// Format-specific preprocessing — SVG sanitization today; HEIC
|
|
58
|
+
// transcode, EXIF orientation, animated-GIF poster extraction,
|
|
59
|
+
// etc. plug in via the `UploadPreprocessor` interface. Ingest
|
|
60
|
+
// doesn't know which preprocessors are registered or what they do
|
|
61
|
+
// (DIP / OCP — adding a format = new module + register, no edits
|
|
62
|
+
// here). Failures throw `AssetPreprocessError` (HTTP 400).
|
|
63
|
+
const { bytes: buffer } = await runPreprocessors(initialBuffer, mime, input.preprocessors);
|
|
64
|
+
const size = buffer.byteLength;
|
|
65
|
+
// Extension is derived from the sniffed MIME (we ignore the client-sent
|
|
66
|
+
// extension in the requested name — the validator already stripped it).
|
|
67
|
+
const bytesExt = ext ?? EXT_BY_MIME[mime ?? ''] ?? '';
|
|
68
|
+
if (!bytesExt) {
|
|
69
|
+
throw new AssetMimeMismatchError(mime, []);
|
|
70
|
+
}
|
|
71
|
+
const hash = hashBytes(buffer);
|
|
72
|
+
if (hash.length !== ASSET_HASH_LENGTH) {
|
|
73
|
+
throw new AssetStorageError('write', input.requestedName, new Error('unexpected hash length'));
|
|
74
|
+
}
|
|
75
|
+
const canonicalName = baseName(input.requestedName);
|
|
76
|
+
// Format-specific analysis — width/height for static images, plus
|
|
77
|
+
// animated detection + first-frame poster extraction for animated.
|
|
78
|
+
// Analyzers contribute manifest fields (`width`, `height`,
|
|
79
|
+
// `animated`, `frames`, `duration`, `poster`) and optional
|
|
80
|
+
// supplementary byte files (animated images add a `-poster.png`).
|
|
81
|
+
// Pluggable via `input.analyzers`; defaults handle JPEG / PNG /
|
|
82
|
+
// GIF / animated WebP / animated AVIF.
|
|
83
|
+
const analysis = (mime ?? '').startsWith('image/') && mime !== 'image/svg+xml'
|
|
84
|
+
? await runAnalyzers({ bytes: buffer, assetName: canonicalName, hash, ext: bytesExt, mime: mime ?? '' }, input.analyzers)
|
|
85
|
+
: {
|
|
86
|
+
manifestPatch: undefined,
|
|
87
|
+
supplementaryFiles: undefined,
|
|
88
|
+
};
|
|
89
|
+
// SVG dims still come from the legacy inline path — sharp handles
|
|
90
|
+
// SVG via libvips but the post-sanitize bytes need their own pass
|
|
91
|
+
// and analyzers skip SVG by design (`staticImageAnalyzer.matches`
|
|
92
|
+
// excludes it). One day SVG dims could move into a dedicated SVG
|
|
93
|
+
// analyzer; not today.
|
|
94
|
+
const svgDims = mime === 'image/svg+xml' ? await extractImageDimensions(buffer) : null;
|
|
95
|
+
// Generate variants in memory before we touch storage. Vector
|
|
96
|
+
// formats (SVG) and animated images skip variants — they don't
|
|
97
|
+
// ladder-resize correctly. The rule lives in `shouldGenerateVariants`
|
|
98
|
+
// for testability + extension.
|
|
99
|
+
const generatedVariants = [];
|
|
100
|
+
if (shouldGenerateVariants(mime, analysis.manifestPatch?.animated)) {
|
|
101
|
+
try {
|
|
102
|
+
const generated = await generateVariants(buffer);
|
|
103
|
+
generatedVariants.push(...generated);
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
throw new AssetVariantGenerationError(canonicalName, err);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
// Compose the manifest in memory. Variant paths come from the same
|
|
110
|
+
// `assetVariantBytesPath` helper used at write time — single source of
|
|
111
|
+
// truth so the manifest's `variants[i].path` always matches the
|
|
112
|
+
// on-disk filename.
|
|
113
|
+
const manifestVariants = generatedVariants.map(v => ({
|
|
114
|
+
width: v.width,
|
|
115
|
+
path: assetVariantBytesPath(canonicalName, hash, bytesExt, v.width),
|
|
116
|
+
size: v.bytes.byteLength,
|
|
117
|
+
}));
|
|
118
|
+
const bytesRel = assetBytesPath(canonicalName, hash, bytesExt);
|
|
119
|
+
const bytesPath = `${input.assetsRoot}/${bytesRel}`;
|
|
120
|
+
const manifest = {
|
|
121
|
+
version: 1,
|
|
122
|
+
name: canonicalName,
|
|
123
|
+
kind: 'embedded',
|
|
124
|
+
source: 'internal',
|
|
125
|
+
mime: mime ?? 'application/octet-stream',
|
|
126
|
+
size,
|
|
127
|
+
hash,
|
|
128
|
+
// Width/height come from analyzer for non-SVG, from the legacy
|
|
129
|
+
// inline path for SVG. Analyzer takes precedence when present.
|
|
130
|
+
width: analysis.manifestPatch?.width ?? svgDims?.width ?? null,
|
|
131
|
+
height: analysis.manifestPatch?.height ?? svgDims?.height ?? null,
|
|
132
|
+
variants: manifestVariants,
|
|
133
|
+
alt: input.alt,
|
|
134
|
+
// Spread analyzer-contributed enrichment fields (animated, frames,
|
|
135
|
+
// duration, poster). Only present when the analyzer set them, so
|
|
136
|
+
// the manifest doesn't carry undefined values for static assets.
|
|
137
|
+
...(analysis.manifestPatch?.animated !== undefined ? { animated: analysis.manifestPatch.animated } : {}),
|
|
138
|
+
...(analysis.manifestPatch?.frames !== undefined ? { frames: analysis.manifestPatch.frames } : {}),
|
|
139
|
+
...(analysis.manifestPatch?.duration !== undefined ? { duration: analysis.manifestPatch.duration } : {}),
|
|
140
|
+
...(analysis.manifestPatch?.poster !== undefined ? { poster: analysis.manifestPatch.poster } : {}),
|
|
141
|
+
uploadedAt: new Date().toISOString(),
|
|
142
|
+
uploadedBy: input.uploadedBy,
|
|
143
|
+
};
|
|
144
|
+
const manifestRelPath = manifestPath(canonicalName);
|
|
145
|
+
const manifestSerialized = `${JSON.stringify(manifest, null, 2)}\n`;
|
|
146
|
+
// Resolve absolute paths for the supplementary files contributed by
|
|
147
|
+
// the analyzer (animated poster today). Used in both the history
|
|
148
|
+
// items list and the write loop.
|
|
149
|
+
const supplementaryFiles = (analysis.supplementaryFiles ?? []).map(f => ({
|
|
150
|
+
abs: `${input.assetsRoot}/${f.path}`,
|
|
151
|
+
bytes: f.bytes,
|
|
152
|
+
}));
|
|
153
|
+
// Record history BEFORE any writes. Mirrors the replace.ts pattern:
|
|
154
|
+
// recorder's first-time baseline scan must see pre-op state. Items
|
|
155
|
+
// include the manifest (text), primary bytes (binary), every variant
|
|
156
|
+
// (binary), and any analyzer-contributed supplementary files (e.g.
|
|
157
|
+
// animated-image posters) — the snapshot covers the full asset.
|
|
158
|
+
if (input.history) {
|
|
159
|
+
if (!input.contentRoot) {
|
|
160
|
+
throw new Error('ingestAsset: history requires contentRoot');
|
|
161
|
+
}
|
|
162
|
+
const items = [
|
|
163
|
+
{ path: `${input.assetsRoot}/${manifestRelPath}`, content: manifestSerialized },
|
|
164
|
+
{ path: bytesPath, content: buffer },
|
|
165
|
+
...generatedVariants.map(v => ({
|
|
166
|
+
path: `${input.assetsRoot}/${assetVariantBytesPath(canonicalName, hash, bytesExt, v.width)}`,
|
|
167
|
+
content: v.bytes,
|
|
168
|
+
})),
|
|
169
|
+
...supplementaryFiles.map(f => ({ path: f.abs, content: f.bytes })),
|
|
170
|
+
];
|
|
171
|
+
await recordWrite({
|
|
172
|
+
history: input.history,
|
|
173
|
+
contentRoot: input.contentRoot,
|
|
174
|
+
operation: 'save',
|
|
175
|
+
author: input.author,
|
|
176
|
+
items,
|
|
177
|
+
message: `Upload ${canonicalName}`,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
// Persist primary bytes.
|
|
181
|
+
try {
|
|
182
|
+
await input.storage.writeStream(bytesPath, byteStreamFrom(buffer));
|
|
183
|
+
}
|
|
184
|
+
catch (err) {
|
|
185
|
+
throw new AssetStorageError('write', bytesPath, err);
|
|
186
|
+
}
|
|
187
|
+
// Persist variants. Failures roll back primary bytes + previously
|
|
188
|
+
// written variants so partial uploads don't leak orphans on disk.
|
|
189
|
+
const writtenPaths = [bytesPath];
|
|
190
|
+
for (const v of generatedVariants) {
|
|
191
|
+
const relPath = assetVariantBytesPath(canonicalName, hash, bytesExt, v.width);
|
|
192
|
+
const absPath = `${input.assetsRoot}/${relPath}`;
|
|
193
|
+
try {
|
|
194
|
+
await input.storage.writeStream(absPath, byteStreamFrom(v.bytes));
|
|
195
|
+
}
|
|
196
|
+
catch (err) {
|
|
197
|
+
await rollback(input.storage, writtenPaths);
|
|
198
|
+
throw new AssetStorageError('write', absPath, err);
|
|
199
|
+
}
|
|
200
|
+
writtenPaths.push(absPath);
|
|
201
|
+
}
|
|
202
|
+
// Persist supplementary files (e.g. animated poster). Same rollback
|
|
203
|
+
// discipline — failures unwind every byte file written so far.
|
|
204
|
+
for (const f of supplementaryFiles) {
|
|
205
|
+
try {
|
|
206
|
+
await input.storage.writeStream(f.abs, byteStreamFrom(f.bytes));
|
|
207
|
+
}
|
|
208
|
+
catch (err) {
|
|
209
|
+
await rollback(input.storage, writtenPaths);
|
|
210
|
+
throw new AssetStorageError('write', f.abs, err);
|
|
211
|
+
}
|
|
212
|
+
writtenPaths.push(f.abs);
|
|
213
|
+
}
|
|
214
|
+
// Write manifest last — it's the visible "asset exists" record.
|
|
215
|
+
try {
|
|
216
|
+
await writeManifest(input.storage, input.assetsRoot, manifest);
|
|
217
|
+
}
|
|
218
|
+
catch (err) {
|
|
219
|
+
await rollback(input.storage, writtenPaths);
|
|
220
|
+
throw err;
|
|
221
|
+
}
|
|
222
|
+
return { manifest, bytesPath };
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Roll back a partially-committed upload by removing every path we
|
|
226
|
+
* committed before the failure. Best-effort; each `rm` is idempotent
|
|
227
|
+
* (`rmIgnoreMissing`) so a subsequent retry won't be confused by
|
|
228
|
+
* leftovers. Rollback errors are swallowed — the caller's original
|
|
229
|
+
* error is what matters; a rollback failure becomes an orphan byte
|
|
230
|
+
* file that GC will reclaim later.
|
|
231
|
+
*
|
|
232
|
+
* Takes an absolute-path list rather than composing paths itself —
|
|
233
|
+
* caller (who already has the pieces) knows; this helper stays a
|
|
234
|
+
* dumb loop of `rmIgnoreMissing`.
|
|
235
|
+
*/
|
|
236
|
+
async function rollback(storage, paths) {
|
|
237
|
+
for (const path of paths) {
|
|
238
|
+
try {
|
|
239
|
+
await rmIgnoreMissing(storage, path);
|
|
240
|
+
}
|
|
241
|
+
catch {
|
|
242
|
+
/* best-effort */
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Whether to generate responsive variants for this asset. Three rules:
|
|
248
|
+
*
|
|
249
|
+
* 1. Non-image MIMEs don't get variants (no ladder concept)
|
|
250
|
+
* 2. SVG is vector — scales on the browser
|
|
251
|
+
* 3. Animated images can't be ladder-resized correctly without
|
|
252
|
+
* full transcoding (sharp's resize on a multi-frame source
|
|
253
|
+
* flattens to the first frame)
|
|
254
|
+
*
|
|
255
|
+
* Pure function — the rules live in one place for testability +
|
|
256
|
+
* single-source-of-truth as new format-specific opt-outs land.
|
|
257
|
+
*/
|
|
258
|
+
function shouldGenerateVariants(mime, animated) {
|
|
259
|
+
if (!mime || !mime.startsWith('image/'))
|
|
260
|
+
return false;
|
|
261
|
+
if (mime === 'image/svg+xml')
|
|
262
|
+
return false;
|
|
263
|
+
if (animated === true)
|
|
264
|
+
return false;
|
|
265
|
+
return true;
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Strip any trailing extension from a user-supplied name. The canonical
|
|
269
|
+
* asset name is extension-free; extensions live on the byte filename
|
|
270
|
+
* (`{name}-{hash}.{ext}`). Derives the same base name regardless of what
|
|
271
|
+
* the client sent.
|
|
272
|
+
*/
|
|
273
|
+
function baseName(requested) {
|
|
274
|
+
const dot = requested.lastIndexOf('.');
|
|
275
|
+
return dot > 0 ? requested.slice(0, dot) : requested;
|
|
276
|
+
}
|
|
277
|
+
/** Collect a byte stream into a single Uint8Array. */
|
|
278
|
+
async function collectBytes(stream) {
|
|
279
|
+
const reader = stream.getReader();
|
|
280
|
+
const parts = [];
|
|
281
|
+
let total = 0;
|
|
282
|
+
while (true) {
|
|
283
|
+
const { value, done } = await reader.read();
|
|
284
|
+
if (done)
|
|
285
|
+
break;
|
|
286
|
+
if (value) {
|
|
287
|
+
parts.push(value);
|
|
288
|
+
total += value.byteLength;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
const out = new Uint8Array(total);
|
|
292
|
+
let offset = 0;
|
|
293
|
+
for (const p of parts) {
|
|
294
|
+
out.set(p, offset);
|
|
295
|
+
offset += p.byteLength;
|
|
296
|
+
}
|
|
297
|
+
return out;
|
|
298
|
+
}
|
|
299
|
+
/** Wrap a Uint8Array as a one-shot ReadableStream. */
|
|
300
|
+
function byteStreamFrom(bytes) {
|
|
301
|
+
return new ReadableStream({
|
|
302
|
+
start(controller) {
|
|
303
|
+
controller.enqueue(bytes);
|
|
304
|
+
controller.close();
|
|
305
|
+
},
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
//# sourceMappingURL=ingest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ingest.js","sourceRoot":"","sources":["../../src/assets/ingest.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Asset-kind compatibility for replace operations.
|
|
3
|
+
*
|
|
4
|
+
* Per design-media.md → Delete semantics:
|
|
5
|
+
* "Replace compatibility: same kind (embedded ↔ embedded,
|
|
6
|
+
* downloadable ↔ downloadable). Within embedded, cross-subtype is
|
|
7
|
+
* blocked (image ≠ video). Same kind + different MIME is allowed
|
|
8
|
+
* (JPEG → PNG, PDF → DOCX)."
|
|
9
|
+
*
|
|
10
|
+
* Single responsibility: decide "can asset B replace asset A?" — pure,
|
|
11
|
+
* no I/O. Callers (replace, future rename) use this to validate
|
|
12
|
+
* compatibility before rewriting refs.
|
|
13
|
+
*/
|
|
14
|
+
import type { AssetManifest } from '../schema/types.js';
|
|
15
|
+
/**
|
|
16
|
+
* The coarse MIME category used for compatibility within `embedded`.
|
|
17
|
+
* We're not asking "same MIME" (PNG → JPEG is fine); we're asking
|
|
18
|
+
* "same rendering contract" (image tags can't swap with video tags).
|
|
19
|
+
*/
|
|
20
|
+
export declare function mimeCategory(mime: string): string;
|
|
21
|
+
export interface CompatCheck {
|
|
22
|
+
readonly compatible: boolean;
|
|
23
|
+
readonly oldKind: string;
|
|
24
|
+
readonly oldMimeCategory: string;
|
|
25
|
+
readonly newKind: string;
|
|
26
|
+
readonly newMimeCategory: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Is `newAsset` a valid replacement for `oldAsset`? Returns a result
|
|
30
|
+
* object either way — callers can branch on `compatible` and pass the
|
|
31
|
+
* detail fields into the typed error when it's false.
|
|
32
|
+
*/
|
|
33
|
+
export declare function checkKindCompat(oldAsset: AssetManifest, newAsset: AssetManifest): CompatCheck;
|
|
34
|
+
//# sourceMappingURL=kind-compat.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kind-compat.d.ts","sourceRoot":"","sources":["../../src/assets/kind-compat.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAEvD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAIjD;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAA;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAA;IAChC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAA;CACjC;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,aAAa,EAAE,QAAQ,EAAE,aAAa,GAAG,WAAW,CAgB7F"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The coarse MIME category used for compatibility within `embedded`.
|
|
3
|
+
* We're not asking "same MIME" (PNG → JPEG is fine); we're asking
|
|
4
|
+
* "same rendering contract" (image tags can't swap with video tags).
|
|
5
|
+
*/
|
|
6
|
+
export function mimeCategory(mime) {
|
|
7
|
+
// "image/jpeg" → "image"; "video/mp4" → "video"; "application/pdf" → "application"
|
|
8
|
+
const slash = mime.indexOf('/');
|
|
9
|
+
return slash === -1 ? mime : mime.slice(0, slash);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Is `newAsset` a valid replacement for `oldAsset`? Returns a result
|
|
13
|
+
* object either way — callers can branch on `compatible` and pass the
|
|
14
|
+
* detail fields into the typed error when it's false.
|
|
15
|
+
*/
|
|
16
|
+
export function checkKindCompat(oldAsset, newAsset) {
|
|
17
|
+
const oldCat = mimeCategory(oldAsset.mime);
|
|
18
|
+
const newCat = mimeCategory(newAsset.mime);
|
|
19
|
+
// Compatible when kind matches AND mime-category matches. The MIME
|
|
20
|
+
// check is only meaningful within `embedded` (that's where the
|
|
21
|
+
// image/video/audio distinction lives), but applying it to
|
|
22
|
+
// `downloadable` and `font` is harmless — all PDFs share the
|
|
23
|
+
// "application" category, all fonts share "font", etc.
|
|
24
|
+
const compatible = oldAsset.kind === newAsset.kind && oldCat === newCat;
|
|
25
|
+
return {
|
|
26
|
+
compatible,
|
|
27
|
+
oldKind: oldAsset.kind,
|
|
28
|
+
oldMimeCategory: oldCat,
|
|
29
|
+
newKind: newAsset.kind,
|
|
30
|
+
newMimeCategory: newCat,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=kind-compat.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kind-compat.js","sourceRoot":"","sources":["../../src/assets/kind-compat.ts"],"names":[],"mappings":"AAeA;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,mFAAmF;IACnF,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;IAC/B,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAA;AACnD,CAAC;AAUD;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,QAAuB,EAAE,QAAuB;IAC9E,MAAM,MAAM,GAAG,YAAY,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;IAC1C,MAAM,MAAM,GAAG,YAAY,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;IAC1C,mEAAmE;IACnE,+DAA+D;IAC/D,2DAA2D;IAC3D,6DAA6D;IAC7D,uDAAuD;IACvD,MAAM,UAAU,GAAG,QAAQ,CAAC,IAAI,KAAK,QAAQ,CAAC,IAAI,IAAI,MAAM,KAAK,MAAM,CAAA;IACvE,OAAO;QACL,UAAU;QACV,OAAO,EAAE,QAAQ,CAAC,IAAI;QACtB,eAAe,EAAE,MAAM;QACvB,OAAO,EAAE,QAAQ,CAAC,IAAI;QACtB,eAAe,EAAE,MAAM;KACxB,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* List operation — enumerate assets on a target.
|
|
3
|
+
*
|
|
4
|
+
* Reads every `*.asset.json` manifest in the `assets/` directory and returns
|
|
5
|
+
* a summary of each. Summary shape is the minimum the library UI needs —
|
|
6
|
+
* full manifests are fetched on-demand by the detail pane.
|
|
7
|
+
*
|
|
8
|
+
* Separate from `manifest.ts` because:
|
|
9
|
+
* - manifest.ts owns read/write of a single manifest
|
|
10
|
+
* - list.ts owns enumeration (which is directory-scan, corrupt-manifest
|
|
11
|
+
* handling, and summary projection — not shared with single-manifest I/O)
|
|
12
|
+
*
|
|
13
|
+
* Corrupt-manifest resilience: one bad manifest doesn't break the whole
|
|
14
|
+
* listing. Corrupt entries are logged and omitted. The UI showing an
|
|
15
|
+
* "assets: 42" badge shouldn't fail because one JSON file has a stray
|
|
16
|
+
* comma — that's a bug-in-authoring, not a bug in the CMS.
|
|
17
|
+
*/
|
|
18
|
+
import type { StorageProvider } from '../types.js';
|
|
19
|
+
import type { AssetManifest, AssetSummary } from '../schema/types.js';
|
|
20
|
+
export type { AssetSummary };
|
|
21
|
+
/**
|
|
22
|
+
* Project a full manifest to its library-list summary shape. Override
|
|
23
|
+
* locale/theme lists are passed in by the caller — `toSummary` is pure
|
|
24
|
+
* and doesn't do I/O. Single-asset GET passes empty arrays + an
|
|
25
|
+
* optional override-discovery pass; the bulk list passes pre-bucketed
|
|
26
|
+
* results from the directory scan.
|
|
27
|
+
*/
|
|
28
|
+
export declare function toSummary(manifest: AssetManifest, overrideLocales?: readonly string[], overrideThemes?: readonly string[]): AssetSummary;
|
|
29
|
+
export interface ListAssetsInput {
|
|
30
|
+
storage: StorageProvider;
|
|
31
|
+
/** Path prefix for assets (typically `"assets"`). */
|
|
32
|
+
assetsRoot: string;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* List every asset on the target. Returns an empty array when the `assets/`
|
|
36
|
+
* directory doesn't exist — this is valid for a target that's never received
|
|
37
|
+
* any assets (same "empty is fine" policy as pages/fragments).
|
|
38
|
+
*
|
|
39
|
+
* Override-locale/theme detection happens in the same directory scan —
|
|
40
|
+
* no extra I/O. We bucket every `*.asset.json` file by its asset name;
|
|
41
|
+
* default manifests get loaded into summaries, and locale/theme variant
|
|
42
|
+
* filenames contribute to the parent asset's `overrideLocales` /
|
|
43
|
+
* `overrideThemes` lists.
|
|
44
|
+
*/
|
|
45
|
+
export declare function listAssets(input: ListAssetsInput): Promise<AssetSummary[]>;
|
|
46
|
+
//# sourceMappingURL=list.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../src/assets/list.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAClD,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAKrE,YAAY,EAAE,YAAY,EAAE,CAAA;AAE5B;;;;;;GAMG;AACH,wBAAgB,SAAS,CACvB,QAAQ,EAAE,aAAa,EACvB,eAAe,GAAE,SAAS,MAAM,EAAO,EACvC,cAAc,GAAE,SAAS,MAAM,EAAO,GACrC,YAAY,CAqBd;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,eAAe,CAAA;IACxB,qDAAqD;IACrD,UAAU,EAAE,MAAM,CAAA;CACnB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,UAAU,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAiDhF"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { AssetStorageError } from './errors.js';
|
|
2
|
+
import { parseManifestFilename } from './manifest-filename.js';
|
|
3
|
+
import { readManifest } from './manifest.js';
|
|
4
|
+
/**
|
|
5
|
+
* Project a full manifest to its library-list summary shape. Override
|
|
6
|
+
* locale/theme lists are passed in by the caller — `toSummary` is pure
|
|
7
|
+
* and doesn't do I/O. Single-asset GET passes empty arrays + an
|
|
8
|
+
* optional override-discovery pass; the bulk list passes pre-bucketed
|
|
9
|
+
* results from the directory scan.
|
|
10
|
+
*/
|
|
11
|
+
export function toSummary(manifest, overrideLocales = [], overrideThemes = []) {
|
|
12
|
+
return {
|
|
13
|
+
name: manifest.name,
|
|
14
|
+
kind: manifest.kind,
|
|
15
|
+
mime: manifest.mime,
|
|
16
|
+
size: manifest.size,
|
|
17
|
+
hash: manifest.hash,
|
|
18
|
+
width: manifest.width,
|
|
19
|
+
height: manifest.height,
|
|
20
|
+
alt: manifest.alt,
|
|
21
|
+
// Project focalPoint when set; library cards use it for thumbnail
|
|
22
|
+
// positioning, detail pane uses it as the editor's initial value.
|
|
23
|
+
...(manifest.focalPoint !== undefined ? { focalPoint: manifest.focalPoint } : {}),
|
|
24
|
+
// Animated indicator + poster — surfaces the multi-frame state to
|
|
25
|
+
// the admin without forcing a separate detail-pane fetch.
|
|
26
|
+
...(manifest.animated !== undefined ? { animated: manifest.animated } : {}),
|
|
27
|
+
...(manifest.poster !== undefined ? { poster: manifest.poster } : {}),
|
|
28
|
+
uploadedAt: manifest.uploadedAt,
|
|
29
|
+
overrideLocales: [...overrideLocales].sort(),
|
|
30
|
+
overrideThemes: [...overrideThemes].sort(),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* List every asset on the target. Returns an empty array when the `assets/`
|
|
35
|
+
* directory doesn't exist — this is valid for a target that's never received
|
|
36
|
+
* any assets (same "empty is fine" policy as pages/fragments).
|
|
37
|
+
*
|
|
38
|
+
* Override-locale/theme detection happens in the same directory scan —
|
|
39
|
+
* no extra I/O. We bucket every `*.asset.json` file by its asset name;
|
|
40
|
+
* default manifests get loaded into summaries, and locale/theme variant
|
|
41
|
+
* filenames contribute to the parent asset's `overrideLocales` /
|
|
42
|
+
* `overrideThemes` lists.
|
|
43
|
+
*/
|
|
44
|
+
export async function listAssets(input) {
|
|
45
|
+
let entries;
|
|
46
|
+
try {
|
|
47
|
+
entries = await input.storage.readDir(input.assetsRoot);
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
// Missing directory — valid state (no assets yet). Any other error is real.
|
|
51
|
+
if (isDirectoryMissing(err))
|
|
52
|
+
return [];
|
|
53
|
+
throw new AssetStorageError('read', input.assetsRoot, err);
|
|
54
|
+
}
|
|
55
|
+
// Bucket entries by asset name in one pass: identify the default
|
|
56
|
+
// manifest filenames and collect each asset's locale/theme variants
|
|
57
|
+
// alongside. The variants are detected via parseManifestFilename —
|
|
58
|
+
// same parser used by enumerateAssetStoragePaths, so the two paths
|
|
59
|
+
// can't drift on what counts as a valid override filename.
|
|
60
|
+
const defaultNames = [];
|
|
61
|
+
const overridesByAsset = new Map();
|
|
62
|
+
for (const entry of entries) {
|
|
63
|
+
if (entry.isDirectory)
|
|
64
|
+
continue;
|
|
65
|
+
const parsed = parseManifestFilename(entry.name);
|
|
66
|
+
if (!parsed)
|
|
67
|
+
continue;
|
|
68
|
+
if (parsed.selector === null) {
|
|
69
|
+
defaultNames.push(parsed.assetName);
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
const bucket = overridesByAsset.get(parsed.assetName) ?? { locales: [], themes: [] };
|
|
73
|
+
const locale = parsed.selector.get('locale');
|
|
74
|
+
const theme = parsed.selector.get('theme');
|
|
75
|
+
if (locale !== undefined && !bucket.locales.includes(locale))
|
|
76
|
+
bucket.locales.push(locale);
|
|
77
|
+
if (theme !== undefined && !bucket.themes.includes(theme))
|
|
78
|
+
bucket.themes.push(theme);
|
|
79
|
+
overridesByAsset.set(parsed.assetName, bucket);
|
|
80
|
+
}
|
|
81
|
+
const summaries = [];
|
|
82
|
+
for (const assetName of defaultNames) {
|
|
83
|
+
try {
|
|
84
|
+
const manifest = await readManifest(input.storage, input.assetsRoot, assetName);
|
|
85
|
+
const overrides = overridesByAsset.get(assetName) ?? { locales: [], themes: [] };
|
|
86
|
+
summaries.push(toSummary(manifest, overrides.locales, overrides.themes));
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
// Don't fail the whole listing on one corrupt/missing manifest.
|
|
90
|
+
// eslint-disable-next-line no-console
|
|
91
|
+
console.warn(`[assets-list] Skipping ${assetName}: ${err.message}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// Most-recent first — default sort for the library's "recently uploaded" view.
|
|
95
|
+
summaries.sort((a, b) => (a.uploadedAt > b.uploadedAt ? -1 : 1));
|
|
96
|
+
return summaries;
|
|
97
|
+
}
|
|
98
|
+
function isDirectoryMissing(err) {
|
|
99
|
+
const msg = err?.message ?? '';
|
|
100
|
+
return msg.includes('Directory not found') || msg.includes('ENOENT');
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.js","sourceRoot":"","sources":["../../src/assets/list.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default-manifest I/O + validation.
|
|
3
|
+
*
|
|
4
|
+
* Single responsibility: read, validate, and narrow a `{name}.asset.json`
|
|
5
|
+
* file into an `AssetManifest`. Counterpart to `manifest-locale.ts` which
|
|
6
|
+
* handles `{name}.asset.{...}.json` locale variants.
|
|
7
|
+
*
|
|
8
|
+
* # Kind-discriminated validation
|
|
9
|
+
*
|
|
10
|
+
* Today's `AssetManifest` type carries a `kind: 'embedded' | 'downloadable'
|
|
11
|
+
* | 'font'` discriminator but the three kinds share the same field set —
|
|
12
|
+
* ingest writes everything as `kind: 'embedded'` (the v1 slice). Step 17
|
|
13
|
+
* adds kind-specific fields (font: `cssName`/`format`/`weight`/`style`;
|
|
14
|
+
* downloadable: `title`). When those land, this validator is the one
|
|
15
|
+
* place that enforces "if kind is X, fields A/B/C must be present."
|
|
16
|
+
*
|
|
17
|
+
* Today's per-kind contract is uniform — all three kinds require the
|
|
18
|
+
* same field set. That's fine: this validator's structure is
|
|
19
|
+
* kind-discriminated even when the current rules don't yet diverge.
|
|
20
|
+
* Adding a kind-specific check later is one entry in the rules table,
|
|
21
|
+
* not a refactor.
|
|
22
|
+
*
|
|
23
|
+
* # Why a separate module from manifest.ts
|
|
24
|
+
*
|
|
25
|
+
* `manifest.ts` owns shared filename composition (`manifestPath`,
|
|
26
|
+
* `assetBytesPath`, etc.) used by both default and locale paths. This
|
|
27
|
+
* module owns default-side I/O + validation specifically. The split
|
|
28
|
+
* means `manifest-locale.ts` doesn't have to depend on the default
|
|
29
|
+
* validation rules and vice versa.
|
|
30
|
+
*/
|
|
31
|
+
import type { StorageProvider } from '../types.js';
|
|
32
|
+
import type { AssetManifest } from '../schema/types.js';
|
|
33
|
+
/**
|
|
34
|
+
* Read a default asset manifest from storage. Throws:
|
|
35
|
+
* - `AssetManifestNotFoundError` when the manifest file doesn't exist
|
|
36
|
+
* - `AssetManifestCorruptError` when it exists but doesn't validate
|
|
37
|
+
* - `AssetStorageError` on any other storage failure
|
|
38
|
+
*
|
|
39
|
+
* Default manifests are required (vs. locale manifests which are optional);
|
|
40
|
+
* an asset that has no `{name}.asset.json` doesn't exist.
|
|
41
|
+
*/
|
|
42
|
+
export declare function readDefaultManifest(storage: StorageProvider, assetsRoot: string, assetName: string): Promise<AssetManifest>;
|
|
43
|
+
/**
|
|
44
|
+
* Type guard: narrow a parsed-JSON value to `AssetManifest`.
|
|
45
|
+
*
|
|
46
|
+
* Validation order:
|
|
47
|
+
* 1. Common identity fields (version, name, kind, source)
|
|
48
|
+
* 2. Per-kind required field rules (today: same set across kinds; the
|
|
49
|
+
* switch is in place for step 17's kind-specific divergence)
|
|
50
|
+
* 3. Common metadata fields (alt, uploadedAt, uploadedBy)
|
|
51
|
+
*
|
|
52
|
+
* Returns `false` on any failure — callers translate to
|
|
53
|
+
* `AssetManifestCorruptError` for I/O paths.
|
|
54
|
+
*/
|
|
55
|
+
export declare function isDefaultManifest(candidate: unknown): candidate is AssetManifest;
|
|
56
|
+
//# sourceMappingURL=manifest-default.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest-default.d.ts","sourceRoot":"","sources":["../../src/assets/manifest-default.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAClD,OAAO,KAAK,EAAa,aAAa,EAAgB,MAAM,oBAAoB,CAAA;AAIhF;;;;;;;;GAQG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,eAAe,EACxB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,aAAa,CAAC,CA2BxB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,OAAO,GAAG,SAAS,IAAI,aAAa,CAkChF"}
|