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.
Files changed (340) hide show
  1. package/admin-dist/assets/index-BO9-CXmW.css +1 -0
  2. package/admin-dist/assets/index-Ufu8zZH_.js +668 -0
  3. package/admin-dist/index.html +2 -2
  4. package/dist/admin-api/error-response.d.ts +21 -0
  5. package/dist/admin-api/error-response.d.ts.map +1 -0
  6. package/dist/admin-api/error-response.js +12 -0
  7. package/dist/admin-api/error-response.js.map +1 -0
  8. package/dist/admin-api/index.d.ts +0 -2
  9. package/dist/admin-api/index.d.ts.map +1 -1
  10. package/dist/admin-api/index.js +4 -24
  11. package/dist/admin-api/index.js.map +1 -1
  12. package/dist/admin-api/routes/assets.d.ts +16 -0
  13. package/dist/admin-api/routes/assets.d.ts.map +1 -0
  14. package/dist/admin-api/routes/assets.js +433 -0
  15. package/dist/admin-api/routes/assets.js.map +1 -0
  16. package/dist/admin-api/routes/fragments.d.ts.map +1 -1
  17. package/dist/admin-api/routes/fragments.js +30 -4
  18. package/dist/admin-api/routes/fragments.js.map +1 -1
  19. package/dist/admin-api/routes/pages.d.ts.map +1 -1
  20. package/dist/admin-api/routes/pages.js +37 -4
  21. package/dist/admin-api/routes/pages.js.map +1 -1
  22. package/dist/admin-api/routes/publish.d.ts.map +1 -1
  23. package/dist/admin-api/routes/publish.js +68 -35
  24. package/dist/admin-api/routes/publish.js.map +1 -1
  25. package/dist/admin-api/schemas/assets.d.ts +48 -0
  26. package/dist/admin-api/schemas/assets.d.ts.map +1 -0
  27. package/dist/admin-api/schemas/assets.js +44 -0
  28. package/dist/admin-api/schemas/assets.js.map +1 -0
  29. package/dist/admin-api/schemas/index.d.ts +2 -0
  30. package/dist/admin-api/schemas/index.d.ts.map +1 -1
  31. package/dist/admin-api/schemas/index.js +2 -0
  32. package/dist/admin-api/schemas/index.js.map +1 -1
  33. package/dist/admin-api/source-context.d.ts +0 -7
  34. package/dist/admin-api/source-context.d.ts.map +1 -1
  35. package/dist/admin-api/source-context.js +0 -3
  36. package/dist/admin-api/source-context.js.map +1 -1
  37. package/dist/assets/analyze-audio.d.ts +3 -0
  38. package/dist/assets/analyze-audio.d.ts.map +1 -0
  39. package/dist/assets/analyze-audio.js +80 -0
  40. package/dist/assets/analyze-audio.js.map +1 -0
  41. package/dist/assets/analyze-image.d.ts +19 -0
  42. package/dist/assets/analyze-image.d.ts.map +1 -0
  43. package/dist/assets/analyze-image.js +123 -0
  44. package/dist/assets/analyze-image.js.map +1 -0
  45. package/dist/assets/analyze.d.ts +94 -0
  46. package/dist/assets/analyze.d.ts.map +1 -0
  47. package/dist/assets/analyze.js +45 -0
  48. package/dist/assets/analyze.js.map +1 -0
  49. package/dist/assets/asset-deps.d.ts +30 -0
  50. package/dist/assets/asset-deps.d.ts.map +1 -0
  51. package/dist/assets/asset-deps.js +42 -0
  52. package/dist/assets/asset-deps.js.map +1 -0
  53. package/dist/assets/asset-paths.d.ts +155 -0
  54. package/dist/assets/asset-paths.d.ts.map +1 -0
  55. package/dist/assets/asset-paths.js +197 -0
  56. package/dist/assets/asset-paths.js.map +1 -0
  57. package/dist/assets/delete.d.ts +75 -0
  58. package/dist/assets/delete.d.ts.map +1 -0
  59. package/dist/assets/delete.js +82 -0
  60. package/dist/assets/delete.js.map +1 -0
  61. package/dist/assets/errors.d.ts +241 -0
  62. package/dist/assets/errors.d.ts.map +1 -0
  63. package/dist/assets/errors.js +300 -0
  64. package/dist/assets/errors.js.map +1 -0
  65. package/dist/assets/find-refs.d.ts +37 -0
  66. package/dist/assets/find-refs.d.ts.map +1 -0
  67. package/dist/assets/find-refs.js +35 -0
  68. package/dist/assets/find-refs.js.map +1 -0
  69. package/dist/assets/hash.d.ts +13 -0
  70. package/dist/assets/hash.d.ts.map +1 -0
  71. package/dist/assets/hash.js +43 -0
  72. package/dist/assets/hash.js.map +1 -0
  73. package/dist/assets/image-metadata.d.ts +11 -0
  74. package/dist/assets/image-metadata.d.ts.map +1 -0
  75. package/dist/assets/image-metadata.js +31 -0
  76. package/dist/assets/image-metadata.js.map +1 -0
  77. package/dist/assets/ingest-locale.d.ts +86 -0
  78. package/dist/assets/ingest-locale.d.ts.map +1 -0
  79. package/dist/assets/ingest-locale.js +209 -0
  80. package/dist/assets/ingest-locale.js.map +1 -0
  81. package/dist/assets/ingest.d.ts +96 -0
  82. package/dist/assets/ingest.d.ts.map +1 -0
  83. package/dist/assets/ingest.js +308 -0
  84. package/dist/assets/ingest.js.map +1 -0
  85. package/dist/assets/kind-compat.d.ts +34 -0
  86. package/dist/assets/kind-compat.d.ts.map +1 -0
  87. package/dist/assets/kind-compat.js +33 -0
  88. package/dist/assets/kind-compat.js.map +1 -0
  89. package/dist/assets/list.d.ts +46 -0
  90. package/dist/assets/list.d.ts.map +1 -0
  91. package/dist/assets/list.js +102 -0
  92. package/dist/assets/list.js.map +1 -0
  93. package/dist/assets/manifest-default.d.ts +56 -0
  94. package/dist/assets/manifest-default.d.ts.map +1 -0
  95. package/dist/assets/manifest-default.js +120 -0
  96. package/dist/assets/manifest-default.js.map +1 -0
  97. package/dist/assets/manifest-filename.d.ts +52 -0
  98. package/dist/assets/manifest-filename.d.ts.map +1 -0
  99. package/dist/assets/manifest-filename.js +104 -0
  100. package/dist/assets/manifest-filename.js.map +1 -0
  101. package/dist/assets/manifest-locale.d.ts +60 -0
  102. package/dist/assets/manifest-locale.d.ts.map +1 -0
  103. package/dist/assets/manifest-locale.js +206 -0
  104. package/dist/assets/manifest-locale.js.map +1 -0
  105. package/dist/assets/manifest-merge.d.ts +66 -0
  106. package/dist/assets/manifest-merge.d.ts.map +1 -0
  107. package/dist/assets/manifest-merge.js +82 -0
  108. package/dist/assets/manifest-merge.js.map +1 -0
  109. package/dist/assets/manifest.d.ts +83 -0
  110. package/dist/assets/manifest.d.ts.map +1 -0
  111. package/dist/assets/manifest.js +93 -0
  112. package/dist/assets/manifest.js.map +1 -0
  113. package/dist/assets/mime-sniff.d.ts +18 -0
  114. package/dist/assets/mime-sniff.d.ts.map +1 -0
  115. package/dist/assets/mime-sniff.js +84 -0
  116. package/dist/assets/mime-sniff.js.map +1 -0
  117. package/dist/assets/preprocess-svg.d.ts +3 -0
  118. package/dist/assets/preprocess-svg.d.ts.map +1 -0
  119. package/dist/assets/preprocess-svg.js +49 -0
  120. package/dist/assets/preprocess-svg.js.map +1 -0
  121. package/dist/assets/preprocess.d.ts +62 -0
  122. package/dist/assets/preprocess.d.ts.map +1 -0
  123. package/dist/assets/preprocess.js +86 -0
  124. package/dist/assets/preprocess.js.map +1 -0
  125. package/dist/assets/publish-plan.d.ts +41 -0
  126. package/dist/assets/publish-plan.d.ts.map +1 -0
  127. package/dist/assets/publish-plan.js +49 -0
  128. package/dist/assets/publish-plan.js.map +1 -0
  129. package/dist/assets/publish.d.ts +33 -0
  130. package/dist/assets/publish.d.ts.map +1 -0
  131. package/dist/assets/publish.js +81 -0
  132. package/dist/assets/publish.js.map +1 -0
  133. package/dist/assets/refs.d.ts +37 -0
  134. package/dist/assets/refs.d.ts.map +1 -0
  135. package/dist/assets/refs.js +33 -0
  136. package/dist/assets/refs.js.map +1 -0
  137. package/dist/assets/remove-override.d.ts +42 -0
  138. package/dist/assets/remove-override.d.ts.map +1 -0
  139. package/dist/assets/remove-override.js +53 -0
  140. package/dist/assets/remove-override.js.map +1 -0
  141. package/dist/assets/rename.d.ts +43 -0
  142. package/dist/assets/rename.d.ts.map +1 -0
  143. package/dist/assets/rename.js +271 -0
  144. package/dist/assets/rename.js.map +1 -0
  145. package/dist/assets/replace.d.ts +37 -0
  146. package/dist/assets/replace.d.ts.map +1 -0
  147. package/dist/assets/replace.js +195 -0
  148. package/dist/assets/replace.js.map +1 -0
  149. package/dist/assets/resolve.d.ts +141 -0
  150. package/dist/assets/resolve.d.ts.map +1 -0
  151. package/dist/assets/resolve.js +381 -0
  152. package/dist/assets/resolve.js.map +1 -0
  153. package/dist/assets/rewrite-manifest-asset-ref.d.ts +44 -0
  154. package/dist/assets/rewrite-manifest-asset-ref.d.ts.map +1 -0
  155. package/dist/assets/rewrite-manifest-asset-ref.js +51 -0
  156. package/dist/assets/rewrite-manifest-asset-ref.js.map +1 -0
  157. package/dist/assets/scan-manifest-for-asset.d.ts +63 -0
  158. package/dist/assets/scan-manifest-for-asset.d.ts.map +1 -0
  159. package/dist/assets/scan-manifest-for-asset.js +105 -0
  160. package/dist/assets/scan-manifest-for-asset.js.map +1 -0
  161. package/dist/assets/serve-route.d.ts +45 -0
  162. package/dist/assets/serve-route.d.ts.map +1 -0
  163. package/dist/assets/serve-route.js +123 -0
  164. package/dist/assets/serve-route.js.map +1 -0
  165. package/dist/assets/svg-sanitize.d.ts +38 -0
  166. package/dist/assets/svg-sanitize.d.ts.map +1 -0
  167. package/dist/assets/svg-sanitize.js +209 -0
  168. package/dist/assets/svg-sanitize.js.map +1 -0
  169. package/dist/assets/update-metadata.d.ts +61 -0
  170. package/dist/assets/update-metadata.d.ts.map +1 -0
  171. package/dist/assets/update-metadata.js +82 -0
  172. package/dist/assets/update-metadata.js.map +1 -0
  173. package/dist/assets/url.d.ts +82 -0
  174. package/dist/assets/url.d.ts.map +1 -0
  175. package/dist/assets/url.js +103 -0
  176. package/dist/assets/url.js.map +1 -0
  177. package/dist/assets/validate.d.ts +74 -0
  178. package/dist/assets/validate.d.ts.map +1 -0
  179. package/dist/assets/validate.js +136 -0
  180. package/dist/assets/validate.js.map +1 -0
  181. package/dist/assets/variants.d.ts +23 -0
  182. package/dist/assets/variants.d.ts.map +1 -0
  183. package/dist/assets/variants.js +74 -0
  184. package/dist/assets/variants.js.map +1 -0
  185. package/dist/cli/assets-cli.d.ts +58 -0
  186. package/dist/cli/assets-cli.d.ts.map +1 -0
  187. package/dist/cli/assets-cli.js +233 -0
  188. package/dist/cli/assets-cli.js.map +1 -0
  189. package/dist/cli/assets-display.d.ts +112 -0
  190. package/dist/cli/assets-display.d.ts.map +1 -0
  191. package/dist/cli/assets-display.js +106 -0
  192. package/dist/cli/assets-display.js.map +1 -0
  193. package/dist/cli/bootstrap.d.ts +0 -2
  194. package/dist/cli/bootstrap.d.ts.map +1 -1
  195. package/dist/cli/bootstrap.js +0 -1
  196. package/dist/cli/bootstrap.js.map +1 -1
  197. package/dist/cli/index.js +64 -18
  198. package/dist/cli/index.js.map +1 -1
  199. package/dist/compare.d.ts.map +1 -1
  200. package/dist/compare.js +15 -12
  201. package/dist/compare.js.map +1 -1
  202. package/dist/dep-sidecars.d.ts +127 -0
  203. package/dist/dep-sidecars.d.ts.map +1 -0
  204. package/dist/dep-sidecars.js +122 -0
  205. package/dist/dep-sidecars.js.map +1 -0
  206. package/dist/editor/AssetEmbeddedWidget.d.ts +3 -0
  207. package/dist/editor/AssetEmbeddedWidget.d.ts.map +1 -0
  208. package/dist/editor/AssetEmbeddedWidget.js +146 -0
  209. package/dist/editor/AssetEmbeddedWidget.js.map +1 -0
  210. package/dist/editor/mount.d.ts +12 -1
  211. package/dist/editor/mount.d.ts.map +1 -1
  212. package/dist/editor/mount.js +36 -5
  213. package/dist/editor/mount.js.map +1 -1
  214. package/dist/format.d.ts +44 -0
  215. package/dist/format.d.ts.map +1 -0
  216. package/dist/format.js +65 -0
  217. package/dist/format.js.map +1 -0
  218. package/dist/fragment-deps.d.ts +24 -0
  219. package/dist/fragment-deps.d.ts.map +1 -0
  220. package/dist/fragment-deps.js +20 -0
  221. package/dist/fragment-deps.js.map +1 -0
  222. package/dist/hash.d.ts +0 -6
  223. package/dist/hash.d.ts.map +1 -1
  224. package/dist/hash.js +0 -18
  225. package/dist/hash.js.map +1 -1
  226. package/dist/history-provider.d.ts.map +1 -1
  227. package/dist/history-provider.js +30 -8
  228. package/dist/history-provider.js.map +1 -1
  229. package/dist/history-recorder.d.ts +7 -3
  230. package/dist/history-recorder.d.ts.map +1 -1
  231. package/dist/history-recorder.js +9 -1
  232. package/dist/history-recorder.js.map +1 -1
  233. package/dist/history-restorer.d.ts.map +1 -1
  234. package/dist/history-restorer.js +34 -2
  235. package/dist/history-restorer.js.map +1 -1
  236. package/dist/history.d.ts +26 -8
  237. package/dist/history.d.ts.map +1 -1
  238. package/dist/index.d.ts +2 -4
  239. package/dist/index.d.ts.map +1 -1
  240. package/dist/index.js +1 -2
  241. package/dist/index.js.map +1 -1
  242. package/dist/locale.d.ts +20 -0
  243. package/dist/locale.d.ts.map +1 -1
  244. package/dist/locale.js +38 -0
  245. package/dist/locale.js.map +1 -1
  246. package/dist/providers/_atomic-write.d.ts +9 -0
  247. package/dist/providers/_atomic-write.d.ts.map +1 -0
  248. package/dist/providers/_atomic-write.js +72 -0
  249. package/dist/providers/_atomic-write.js.map +1 -0
  250. package/dist/providers/_rm-ignore-missing.d.ts +31 -0
  251. package/dist/providers/_rm-ignore-missing.d.ts.map +1 -0
  252. package/dist/providers/_rm-ignore-missing.js +12 -0
  253. package/dist/providers/_rm-ignore-missing.js.map +1 -0
  254. package/dist/providers/_stream-interop.d.ts +23 -0
  255. package/dist/providers/_stream-interop.d.ts.map +1 -0
  256. package/dist/providers/_stream-interop.js +21 -0
  257. package/dist/providers/_stream-interop.js.map +1 -0
  258. package/dist/providers/azure-blob.d.ts.map +1 -1
  259. package/dist/providers/azure-blob.js +60 -0
  260. package/dist/providers/azure-blob.js.map +1 -1
  261. package/dist/providers/filesystem.d.ts +4 -0
  262. package/dist/providers/filesystem.d.ts.map +1 -1
  263. package/dist/providers/filesystem.js +63 -2
  264. package/dist/providers/filesystem.js.map +1 -1
  265. package/dist/providers/s3.d.ts.map +1 -1
  266. package/dist/providers/s3.js +84 -1
  267. package/dist/providers/s3.js.map +1 -1
  268. package/dist/publish-rendered.d.ts +37 -17
  269. package/dist/publish-rendered.d.ts.map +1 -1
  270. package/dist/publish-rendered.js +71 -67
  271. package/dist/publish-rendered.js.map +1 -1
  272. package/dist/publish.d.ts +13 -12
  273. package/dist/publish.d.ts.map +1 -1
  274. package/dist/publish.js +23 -47
  275. package/dist/publish.js.map +1 -1
  276. package/dist/resolver.d.ts +12 -2
  277. package/dist/resolver.d.ts.map +1 -1
  278. package/dist/resolver.js +54 -9
  279. package/dist/resolver.js.map +1 -1
  280. package/dist/schema/dimensions.d.ts +78 -0
  281. package/dist/schema/dimensions.d.ts.map +1 -0
  282. package/dist/schema/dimensions.js +97 -0
  283. package/dist/schema/dimensions.js.map +1 -0
  284. package/dist/schema/helpers.d.ts +108 -0
  285. package/dist/schema/helpers.d.ts.map +1 -0
  286. package/dist/schema/helpers.js +133 -0
  287. package/dist/schema/helpers.js.map +1 -0
  288. package/dist/schema/index.d.ts +27 -0
  289. package/dist/schema/index.d.ts.map +1 -0
  290. package/dist/schema/index.js +25 -0
  291. package/dist/schema/index.js.map +1 -0
  292. package/dist/schema/types.d.ts +390 -0
  293. package/dist/schema/types.d.ts.map +1 -0
  294. package/dist/schema/types.js +25 -0
  295. package/dist/schema/types.js.map +1 -0
  296. package/dist/selector-chain.d.ts +63 -0
  297. package/dist/selector-chain.d.ts.map +1 -0
  298. package/dist/selector-chain.js +58 -0
  299. package/dist/selector-chain.js.map +1 -0
  300. package/dist/sidecars.d.ts +19 -18
  301. package/dist/sidecars.d.ts.map +1 -1
  302. package/dist/sidecars.js +70 -62
  303. package/dist/sidecars.js.map +1 -1
  304. package/dist/targets.d.ts.map +1 -1
  305. package/dist/targets.js +15 -37
  306. package/dist/targets.js.map +1 -1
  307. package/dist/themes.d.ts +69 -0
  308. package/dist/themes.d.ts.map +1 -0
  309. package/dist/themes.js +85 -0
  310. package/dist/themes.js.map +1 -0
  311. package/dist/transforms/adapter.d.ts +115 -0
  312. package/dist/transforms/adapter.d.ts.map +1 -0
  313. package/dist/transforms/adapter.js +2 -0
  314. package/dist/transforms/adapter.js.map +1 -0
  315. package/dist/transforms/cloudflare.d.ts +17 -0
  316. package/dist/transforms/cloudflare.d.ts.map +1 -0
  317. package/dist/transforms/cloudflare.js +110 -0
  318. package/dist/transforms/cloudflare.js.map +1 -0
  319. package/dist/transforms/index.d.ts +24 -0
  320. package/dist/transforms/index.d.ts.map +1 -0
  321. package/dist/transforms/index.js +30 -0
  322. package/dist/transforms/index.js.map +1 -0
  323. package/dist/transforms/sharp.d.ts +3 -0
  324. package/dist/transforms/sharp.d.ts.map +1 -0
  325. package/dist/transforms/sharp.js +43 -0
  326. package/dist/transforms/sharp.js.map +1 -0
  327. package/dist/types.d.ts +125 -1
  328. package/dist/types.d.ts.map +1 -1
  329. package/dist/types.js.map +1 -1
  330. package/package.json +20 -1
  331. package/admin-dist/assets/index-B6pVot0Y.css +0 -1
  332. package/admin-dist/assets/index-DniLwxJA.js +0 -609
  333. package/dist/providers/r2.d.ts +0 -8
  334. package/dist/providers/r2.d.ts.map +0 -1
  335. package/dist/providers/r2.js +0 -86
  336. package/dist/providers/r2.js.map +0 -1
  337. package/dist/source-sidecars.d.ts +0 -32
  338. package/dist/source-sidecars.d.ts.map +0 -1
  339. package/dist/source-sidecars.js +0 -98
  340. 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"}