canopycms 0.0.57 → 0.0.58

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 (215) hide show
  1. package/dist/ai/json-to-markdown.js +74 -3
  2. package/dist/ai/json-to-markdown.js.map +1 -1
  3. package/dist/api/__test__/mock-client.d.ts +9 -5
  4. package/dist/api/__test__/mock-client.d.ts.map +1 -1
  5. package/dist/api/__test__/mock-client.js +15 -7
  6. package/dist/api/__test__/mock-client.js.map +1 -1
  7. package/dist/api/assets.d.ts +100 -36
  8. package/dist/api/assets.d.ts.map +1 -1
  9. package/dist/api/assets.js +338 -51
  10. package/dist/api/assets.js.map +1 -1
  11. package/dist/api/client.d.ts +16 -5
  12. package/dist/api/client.d.ts.map +1 -1
  13. package/dist/api/client.js +24 -6
  14. package/dist/api/client.js.map +1 -1
  15. package/dist/api/entries.d.ts +4 -4
  16. package/dist/api/index.d.ts +1 -1
  17. package/dist/api/index.d.ts.map +1 -1
  18. package/dist/api/index.js.map +1 -1
  19. package/dist/api/route-builder.d.ts +9 -0
  20. package/dist/api/route-builder.d.ts.map +1 -1
  21. package/dist/api/route-builder.js +1 -0
  22. package/dist/api/route-builder.js.map +1 -1
  23. package/dist/api/schema.d.ts +4 -4
  24. package/dist/api/types.d.ts +11 -1
  25. package/dist/api/types.d.ts.map +1 -1
  26. package/dist/assets/asset-prefixes.d.ts +31 -0
  27. package/dist/assets/asset-prefixes.d.ts.map +1 -0
  28. package/dist/assets/asset-prefixes.js +20 -0
  29. package/dist/assets/asset-prefixes.js.map +1 -0
  30. package/dist/assets/asset-src.d.ts +23 -0
  31. package/dist/assets/asset-src.d.ts.map +1 -0
  32. package/dist/assets/asset-src.js +28 -0
  33. package/dist/assets/asset-src.js.map +1 -0
  34. package/dist/assets/asset-url.d.ts +35 -0
  35. package/dist/assets/asset-url.d.ts.map +1 -0
  36. package/dist/assets/asset-url.js +77 -0
  37. package/dist/assets/asset-url.js.map +1 -0
  38. package/dist/assets/factory.d.ts +18 -0
  39. package/dist/assets/factory.d.ts.map +1 -0
  40. package/dist/assets/factory.js +42 -0
  41. package/dist/assets/factory.js.map +1 -0
  42. package/dist/assets/finalize.d.ts +36 -0
  43. package/dist/assets/finalize.d.ts.map +1 -0
  44. package/dist/assets/finalize.js +72 -0
  45. package/dist/assets/finalize.js.map +1 -0
  46. package/dist/assets/index.d.ts +20 -0
  47. package/dist/assets/index.d.ts.map +1 -0
  48. package/dist/assets/index.js +19 -0
  49. package/dist/assets/index.js.map +1 -0
  50. package/dist/assets/keys.d.ts +56 -0
  51. package/dist/assets/keys.d.ts.map +1 -0
  52. package/dist/assets/keys.js +88 -0
  53. package/dist/assets/keys.js.map +1 -0
  54. package/dist/assets/pipeline.d.ts +50 -0
  55. package/dist/assets/pipeline.d.ts.map +1 -0
  56. package/dist/assets/pipeline.js +202 -0
  57. package/dist/assets/pipeline.js.map +1 -0
  58. package/dist/assets/store-local.d.ts +84 -0
  59. package/dist/assets/store-local.d.ts.map +1 -0
  60. package/dist/assets/store-local.js +254 -0
  61. package/dist/assets/store-local.js.map +1 -0
  62. package/dist/assets/store-s3.d.ts +71 -0
  63. package/dist/assets/store-s3.d.ts.map +1 -0
  64. package/dist/assets/store-s3.js +183 -0
  65. package/dist/assets/store-s3.js.map +1 -0
  66. package/dist/assets/svg-sanitizer.d.ts +37 -0
  67. package/dist/assets/svg-sanitizer.d.ts.map +1 -0
  68. package/dist/assets/svg-sanitizer.js +222 -0
  69. package/dist/assets/svg-sanitizer.js.map +1 -0
  70. package/dist/assets/transform-directives.d.ts +97 -0
  71. package/dist/assets/transform-directives.d.ts.map +1 -0
  72. package/dist/assets/transform-directives.js +267 -0
  73. package/dist/assets/transform-directives.js.map +1 -0
  74. package/dist/assets/transform.d.ts +51 -0
  75. package/dist/assets/transform.d.ts.map +1 -0
  76. package/dist/assets/transform.js +190 -0
  77. package/dist/assets/transform.js.map +1 -0
  78. package/dist/assets/types.d.ts +82 -0
  79. package/dist/assets/types.d.ts.map +1 -0
  80. package/dist/assets/types.js +10 -0
  81. package/dist/assets/types.js.map +1 -0
  82. package/dist/authorization/groups/schema.d.ts +2 -2
  83. package/dist/authorization/permissions/schema.d.ts +2 -2
  84. package/dist/cli/generate-ai-content.js +92 -7
  85. package/dist/cli/init.js +16 -4
  86. package/dist/config/helpers.d.ts.map +1 -1
  87. package/dist/config/helpers.js +2 -1
  88. package/dist/config/helpers.js.map +1 -1
  89. package/dist/config/index.d.ts +2 -2
  90. package/dist/config/index.d.ts.map +1 -1
  91. package/dist/config/index.js +1 -1
  92. package/dist/config/index.js.map +1 -1
  93. package/dist/config/schemas/config.d.ts +10 -0
  94. package/dist/config/schemas/config.d.ts.map +1 -1
  95. package/dist/config/schemas/field.d.ts +38 -3
  96. package/dist/config/schemas/field.d.ts.map +1 -1
  97. package/dist/config/schemas/field.js +18 -0
  98. package/dist/config/schemas/field.js.map +1 -1
  99. package/dist/config/schemas/media.d.ts +8 -0
  100. package/dist/config/schemas/media.d.ts.map +1 -1
  101. package/dist/config/schemas/media.js +9 -0
  102. package/dist/config/schemas/media.js.map +1 -1
  103. package/dist/config/types.d.ts +43 -3
  104. package/dist/config/types.d.ts.map +1 -1
  105. package/dist/config/types.js +4 -1
  106. package/dist/config/types.js.map +1 -1
  107. package/dist/editor/CanopyEditor.d.ts.map +1 -1
  108. package/dist/editor/CanopyEditor.js +1 -1
  109. package/dist/editor/CanopyEditor.js.map +1 -1
  110. package/dist/editor/Editor.d.ts +2 -0
  111. package/dist/editor/Editor.d.ts.map +1 -1
  112. package/dist/editor/Editor.js +82 -80
  113. package/dist/editor/Editor.js.map +1 -1
  114. package/dist/editor/FormRenderer.d.ts.map +1 -1
  115. package/dist/editor/FormRenderer.js +9 -0
  116. package/dist/editor/FormRenderer.js.map +1 -1
  117. package/dist/editor/components/EditorSidebar.d.ts +6 -1
  118. package/dist/editor/components/EditorSidebar.d.ts.map +1 -1
  119. package/dist/editor/components/EditorSidebar.js +4 -3
  120. package/dist/editor/components/EditorSidebar.js.map +1 -1
  121. package/dist/editor/context/AssetContext.d.ts +25 -0
  122. package/dist/editor/context/AssetContext.d.ts.map +1 -0
  123. package/dist/editor/context/AssetContext.js +26 -0
  124. package/dist/editor/context/AssetContext.js.map +1 -0
  125. package/dist/editor/context/index.d.ts +1 -0
  126. package/dist/editor/context/index.d.ts.map +1 -1
  127. package/dist/editor/context/index.js +1 -0
  128. package/dist/editor/context/index.js.map +1 -1
  129. package/dist/editor/fields/ImageField.d.ts +22 -0
  130. package/dist/editor/fields/ImageField.d.ts.map +1 -0
  131. package/dist/editor/fields/ImageField.js +100 -0
  132. package/dist/editor/fields/ImageField.js.map +1 -0
  133. package/dist/editor/fields/MarkdownField.d.ts.map +1 -1
  134. package/dist/editor/fields/MarkdownField.js +30 -4
  135. package/dist/editor/fields/MarkdownField.js.map +1 -1
  136. package/dist/editor/fields/MdxImageDialog.d.ts +16 -0
  137. package/dist/editor/fields/MdxImageDialog.d.ts.map +1 -0
  138. package/dist/editor/fields/MdxImageDialog.js +73 -0
  139. package/dist/editor/fields/MdxImageDialog.js.map +1 -0
  140. package/dist/editor/media/AssetCard.d.ts +21 -0
  141. package/dist/editor/media/AssetCard.d.ts.map +1 -0
  142. package/dist/editor/media/AssetCard.js +41 -0
  143. package/dist/editor/media/AssetCard.js.map +1 -0
  144. package/dist/editor/media/CropStep.d.ts +26 -0
  145. package/dist/editor/media/CropStep.d.ts.map +1 -0
  146. package/dist/editor/media/CropStep.js +38 -0
  147. package/dist/editor/media/CropStep.js.map +1 -0
  148. package/dist/editor/media/MediaLibrary.d.ts +22 -0
  149. package/dist/editor/media/MediaLibrary.d.ts.map +1 -0
  150. package/dist/editor/media/MediaLibrary.js +17 -0
  151. package/dist/editor/media/MediaLibrary.js.map +1 -0
  152. package/dist/editor/media/MediaLibraryBody.d.ts +17 -0
  153. package/dist/editor/media/MediaLibraryBody.d.ts.map +1 -0
  154. package/dist/editor/media/MediaLibraryBody.js +133 -0
  155. package/dist/editor/media/MediaLibraryBody.js.map +1 -0
  156. package/dist/editor/media/crop-math.d.ts +37 -0
  157. package/dist/editor/media/crop-math.d.ts.map +1 -0
  158. package/dist/editor/media/crop-math.js +62 -0
  159. package/dist/editor/media/crop-math.js.map +1 -0
  160. package/dist/editor/media/upload-asset.d.ts +31 -0
  161. package/dist/editor/media/upload-asset.d.ts.map +1 -0
  162. package/dist/editor/media/upload-asset.js +56 -0
  163. package/dist/editor/media/upload-asset.js.map +1 -0
  164. package/dist/editor/media/upload-constants.d.ts +27 -0
  165. package/dist/editor/media/upload-constants.d.ts.map +1 -0
  166. package/dist/editor/media/upload-constants.js +40 -0
  167. package/dist/editor/media/upload-constants.js.map +1 -0
  168. package/dist/editor/media/useAssetUpload.d.ts +19 -0
  169. package/dist/editor/media/useAssetUpload.d.ts.map +1 -0
  170. package/dist/editor/media/useAssetUpload.js +53 -0
  171. package/dist/editor/media/useAssetUpload.js.map +1 -0
  172. package/dist/editor/media/xhr-upload.d.ts +20 -0
  173. package/dist/editor/media/xhr-upload.d.ts.map +1 -0
  174. package/dist/editor/media/xhr-upload.js +42 -0
  175. package/dist/editor/media/xhr-upload.js.map +1 -0
  176. package/dist/editor/theme.d.ts +2 -1
  177. package/dist/editor/theme.d.ts.map +1 -1
  178. package/dist/editor/theme.js +1 -0
  179. package/dist/editor/theme.js.map +1 -1
  180. package/dist/entry-schema.d.ts +19 -0
  181. package/dist/entry-schema.d.ts.map +1 -1
  182. package/dist/entry-schema.js.map +1 -1
  183. package/dist/http/handler.d.ts +5 -2
  184. package/dist/http/handler.d.ts.map +1 -1
  185. package/dist/http/handler.js +13 -4
  186. package/dist/http/handler.js.map +1 -1
  187. package/dist/http/index.d.ts +2 -2
  188. package/dist/http/index.d.ts.map +1 -1
  189. package/dist/http/index.js +1 -1
  190. package/dist/http/index.js.map +1 -1
  191. package/dist/http/router.d.ts +14 -1
  192. package/dist/http/router.d.ts.map +1 -1
  193. package/dist/http/router.js +4 -1
  194. package/dist/http/router.js.map +1 -1
  195. package/dist/http/types.d.ts +40 -0
  196. package/dist/http/types.d.ts.map +1 -1
  197. package/dist/http/types.js +9 -0
  198. package/dist/http/types.js.map +1 -1
  199. package/dist/index.d.ts +3 -0
  200. package/dist/index.d.ts.map +1 -1
  201. package/dist/index.js +4 -0
  202. package/dist/index.js.map +1 -1
  203. package/dist/schema/schema-store.d.ts +2 -2
  204. package/dist/server.d.ts +45 -0
  205. package/dist/server.d.ts.map +1 -1
  206. package/dist/server.js +41 -0
  207. package/dist/server.js.map +1 -1
  208. package/dist/validation/entry-validator.d.ts.map +1 -1
  209. package/dist/validation/entry-validator.js +56 -1
  210. package/dist/validation/entry-validator.js.map +1 -1
  211. package/package.json +13 -1
  212. package/dist/asset-store.d.ts +0 -32
  213. package/dist/asset-store.d.ts.map +0 -1
  214. package/dist/asset-store.js +0 -85
  215. package/dist/asset-store.js.map +0 -1
@@ -0,0 +1,190 @@
1
+ /**
2
+ * The shared transform engine: applies a parsed `TransformDirectives` to
3
+ * source image bytes with sharp. Server-only - never import this from
4
+ * client/editor code (this is why it lives in its own file, separate from
5
+ * the dependency-free transform-directives.ts). Used by the dev-mode lazy
6
+ * `/assets/t/*` emulation in api/assets.ts today, and will be reused
7
+ * unchanged by the prod transform Lambda (PR 7).
8
+ *
9
+ * Pipeline: a cheap metadata-only probe (`limitInputPixels: MAX_INPUT_PIXELS`,
10
+ * decompression-bomb defense #1) learns the real page count, so animated
11
+ * GIF/WebP frames can be capped at `MAX_ANIMATED_FRAMES` (decompression-bomb
12
+ * defense #2) without exceeding it and making sharp throw -> load for real
13
+ * with `{ pages: min(totalPages, MAX_ANIMATED_FRAMES), limitInputPixels }` ->
14
+ * `.rotate()` with no args (bakes EXIF orientation into pixels,
15
+ * dropping the orientation tag) -> optional crop via `.extract()` -> optional
16
+ * `.resize({ width, withoutEnlargement: true })` (never upscales) -> encode.
17
+ *
18
+ * Identity (`orig`) and any request that omits an explicit `f=` format
19
+ * re-encode through the SOURCE format rather than a fixed one: this is what
20
+ * guarantees EXIF/GPS gets stripped even when no other change is requested
21
+ * (sharp strips metadata by default on re-encode - `withMetadata()` is never
22
+ * called here, which would undo that).
23
+ *
24
+ * GIF has no EXIF segment (EXIF is a JPEG/TIFF APP1 marker; GIF has no such
25
+ * extension block), so an identity GIF has nothing to strip - it is still
26
+ * re-encoded through sharp's `.gif()` (verified: current sharp bundles cgif
27
+ * and re-encodes multi-frame GIFs through the `{ animated: true }` input
28
+ * path with no extra native dependency) purely for pipeline uniformity, not
29
+ * because GIF needs stripping.
30
+ */
31
+ import sharp from 'sharp';
32
+ import { getErrorMessage } from '../utils/error.js';
33
+ import { MAX_INPUT_PIXELS, } from './transform-directives.js';
34
+ /** Raster formats the transform engine accepts as input. svg/pdf never reach here - they're served statically. */
35
+ const ALLOWED_INPUT_EXTS = new Set(['png', 'jpg', 'jpeg', 'webp', 'gif']);
36
+ /** Defensive cap on encoded output size - prevents cache-stuffing with giant re-encodes. */
37
+ const MAX_OUTPUT_BYTES = 10 * 1024 * 1024;
38
+ /**
39
+ * Cap on decoded animated frames (GIF/WebP) - without this, `{ animated: true }`
40
+ * (sharp's `pages: -1`) decodes every frame of a maliciously-crafted
41
+ * many-thousand-frame animation into memory at once, a decompression-bomb
42
+ * vector distinct from (and not covered by) `limitInputPixels`, which only
43
+ * bounds a single frame's width x height. 60 frames covers any reasonable
44
+ * animated asset (2s at 30fps) with headroom.
45
+ */
46
+ const MAX_ANIMATED_FRAMES = 60;
47
+ const CONTENT_TYPE_BY_FORMAT = {
48
+ webp: 'image/webp',
49
+ jpeg: 'image/jpeg',
50
+ png: 'image/png',
51
+ };
52
+ const CONTENT_TYPE_BY_EXT = {
53
+ png: 'image/png',
54
+ jpg: 'image/jpeg',
55
+ jpeg: 'image/jpeg',
56
+ webp: 'image/webp',
57
+ gif: 'image/gif',
58
+ };
59
+ /**
60
+ * Dimensions AFTER `.rotate()` has auto-oriented the pixels. sharp's
61
+ * `metadata()` reports the RAW (pre-rotation) width/height plus the EXIF
62
+ * `orientation` tag; for orientation 5-8 (the 90-degree rotations that
63
+ * portrait phone photos almost always carry) the visual axes are swapped.
64
+ * The crop rect was normalized by the editor against the browser's
65
+ * auto-oriented preview, so `.extract()` - which runs on the post-rotation
66
+ * pipeline - must be computed against these swapped dims, NOT the raw ones.
67
+ * Using the raw dims makes the extract region exceed the rotated image's
68
+ * bounds and sharp throws `bad extract area`. sharp >=0.33 also exposes an
69
+ * `autoOrient` block with the corrected dims; prefer it when present.
70
+ */
71
+ function orientedDimensions(meta) {
72
+ const auto = meta.autoOrient;
73
+ if (auto && auto.width && auto.height) {
74
+ return { width: auto.width, height: auto.height };
75
+ }
76
+ if (!meta.width || !meta.height)
77
+ return null;
78
+ const swap = typeof meta.orientation === 'number' && meta.orientation >= 5 && meta.orientation <= 8;
79
+ return swap
80
+ ? { width: meta.height, height: meta.width }
81
+ : { width: meta.width, height: meta.height };
82
+ }
83
+ /**
84
+ * Compute a sharp `.extract()` region from a normalized crop rect and the
85
+ * (post-rotation) image dimensions. Rounds the left/top and right/bottom
86
+ * edges independently, then derives width/height from their difference -
87
+ * rounding width/height directly from `crop.w`/`crop.h` could drift the
88
+ * region past the image bounds when combined with independently-rounded
89
+ * left/top. Clamped defensively so float error can never produce an
90
+ * out-of-range extract (which sharp throws on).
91
+ */
92
+ function computeExtractRegion(crop, width, height) {
93
+ const left = Math.min(Math.max(Math.round(crop.x * width), 0), Math.max(width - 1, 0));
94
+ const top = Math.min(Math.max(Math.round(crop.y * height), 0), Math.max(height - 1, 0));
95
+ const right = Math.min(Math.round((crop.x + crop.w) * width), width);
96
+ const bottom = Math.min(Math.round((crop.y + crop.h) * height), height);
97
+ const extractWidth = Math.max(1, right - left);
98
+ const extractHeight = Math.max(1, bottom - top);
99
+ return { left, top, width: extractWidth, height: extractHeight };
100
+ }
101
+ function encode(pipeline, format, quality) {
102
+ const options = quality !== undefined ? { quality } : undefined;
103
+ switch (format) {
104
+ case 'webp':
105
+ return pipeline.webp(options);
106
+ case 'jpeg':
107
+ return pipeline.jpeg(options);
108
+ case 'png':
109
+ return pipeline.png(options);
110
+ }
111
+ }
112
+ /** Re-encode through the source container format - used for identity and for requests that omit `f=`. */
113
+ function encodeSourceFormat(pipeline, sourceExt) {
114
+ switch (sourceExt) {
115
+ case 'gif':
116
+ return pipeline.gif();
117
+ case 'webp':
118
+ return pipeline.webp();
119
+ case 'jpg':
120
+ case 'jpeg':
121
+ return pipeline.jpeg();
122
+ case 'png':
123
+ default:
124
+ return pipeline.png();
125
+ }
126
+ }
127
+ export async function applyTransform(input, directives) {
128
+ const sourceExt = input.ext.toLowerCase();
129
+ if (!ALLOWED_INPUT_EXTS.has(sourceExt)) {
130
+ return {
131
+ ok: false,
132
+ status: 400,
133
+ error: `Unsupported input format for transform: '${sourceExt}'`,
134
+ };
135
+ }
136
+ const resize = directives.identity ? undefined : directives;
137
+ const format = resize?.format;
138
+ const quality = resize?.quality;
139
+ try {
140
+ // A cheap header/metadata-only probe (no pixel decode - verified this
141
+ // costs effectively nothing even for a several-hundred-frame source) to
142
+ // learn the source's real page count before deciding whether to cap it.
143
+ // This has to happen before constructing the real pipeline: sharp's
144
+ // `pages` constructor option is a fixed request, not an upper bound - a
145
+ // `pages` value that EXCEEDS the source's actual page count makes sharp
146
+ // throw ("bad page number") rather than clamping, so `MAX_ANIMATED_FRAMES`
147
+ // can only be applied as `Math.min(totalPages, MAX_ANIMATED_FRAMES)`.
148
+ // `limitInputPixels` gates this probe exactly like it gates the real
149
+ // pipeline below - an oversized source throws here already, before any
150
+ // pixel buffer is ever allocated.
151
+ const probeMeta = await sharp(input.data, { limitInputPixels: MAX_INPUT_PIXELS }).metadata();
152
+ const totalPages = probeMeta.pages ?? 1;
153
+ const pagesToRead = Math.min(totalPages, MAX_ANIMATED_FRAMES);
154
+ let pipeline = sharp(input.data, {
155
+ pages: pagesToRead,
156
+ limitInputPixels: MAX_INPUT_PIXELS,
157
+ }).rotate();
158
+ if (resize?.crop) {
159
+ const oriented = orientedDimensions(await pipeline.metadata());
160
+ if (!oriented) {
161
+ return { ok: false, status: 422, error: 'Could not read image dimensions for crop' };
162
+ }
163
+ pipeline = pipeline.extract(computeExtractRegion(resize.crop, oriented.width, oriented.height));
164
+ }
165
+ if (resize?.width) {
166
+ pipeline = pipeline.resize({ width: resize.width, withoutEnlargement: true });
167
+ }
168
+ pipeline = format ? encode(pipeline, format, quality) : encodeSourceFormat(pipeline, sourceExt);
169
+ const data = await pipeline.toBuffer();
170
+ if (data.byteLength > MAX_OUTPUT_BYTES) {
171
+ return {
172
+ ok: false,
173
+ status: 413,
174
+ error: `Transformed output (${data.byteLength} bytes) exceeds the ${MAX_OUTPUT_BYTES}-byte cap`,
175
+ };
176
+ }
177
+ const outExt = format ?? sourceExt;
178
+ const contentType = format ? CONTENT_TYPE_BY_FORMAT[format] : CONTENT_TYPE_BY_EXT[sourceExt];
179
+ return {
180
+ ok: true,
181
+ data: new Uint8Array(data.buffer, data.byteOffset, data.byteLength),
182
+ contentType,
183
+ ext: outExt,
184
+ };
185
+ }
186
+ catch (err) {
187
+ return { ok: false, status: 422, error: `Transform failed: ${getErrorMessage(err)}` };
188
+ }
189
+ }
190
+ //# sourceMappingURL=transform.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transform.js","sourceRoot":"","sources":["../../src/assets/transform.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Asset store v2 contract.
3
+ *
4
+ * Pure type declarations only — no runtime imports. This lets client-side code
5
+ * (the editor) `import type` from this file without ever pulling node:fs or the
6
+ * S3 SDK into a browser bundle. The concrete stores (store-local.ts, store-s3.ts)
7
+ * and the factory are server-only and must never be imported from client code.
8
+ */
9
+ export interface AssetMeta {
10
+ hash32: string;
11
+ filename: string;
12
+ slug: string;
13
+ ext: string;
14
+ mime: string;
15
+ size: number;
16
+ width?: number;
17
+ height?: number;
18
+ kind: 'raster' | 'svg' | 'pdf';
19
+ uploadedBy?: string;
20
+ uploadedAt: string;
21
+ }
22
+ export type StagedUploadTarget = {
23
+ mode: 'direct';
24
+ url: string;
25
+ fields: Record<string, string>;
26
+ stagingKey: string;
27
+ maxBytes: number;
28
+ } | {
29
+ mode: 'proxied';
30
+ stagingKey: string;
31
+ maxBytes: number;
32
+ };
33
+ export interface BeginUploadInput {
34
+ filename: string;
35
+ contentType: string;
36
+ size?: number;
37
+ }
38
+ export interface PublicObject {
39
+ data: Uint8Array;
40
+ contentType?: string;
41
+ contentDisposition?: string;
42
+ cacheControl?: string;
43
+ }
44
+ export interface AssetStore {
45
+ readonly capabilities: {
46
+ directUpload: boolean;
47
+ };
48
+ beginUpload(input: BeginUploadInput): Promise<StagedUploadTarget>;
49
+ writeStaging(stagingKey: string, data: Uint8Array, contentType?: string): Promise<void>;
50
+ readStaging(stagingKey: string): Promise<Uint8Array | null>;
51
+ deleteStaging(stagingKey: string): Promise<void>;
52
+ putOriginal(input: {
53
+ hash32: string;
54
+ ext: string;
55
+ data: Uint8Array;
56
+ contentType: string;
57
+ }): Promise<void>;
58
+ readOriginal(hash32: string): Promise<{
59
+ data: Uint8Array;
60
+ ext: string;
61
+ contentType?: string;
62
+ } | null>;
63
+ putPublicObject(input: {
64
+ key: string;
65
+ data: Uint8Array;
66
+ contentType: string;
67
+ contentDisposition?: string;
68
+ cacheControl?: string;
69
+ }): Promise<void>;
70
+ readPublicObject(key: string): Promise<PublicObject | null>;
71
+ putMetaIfAbsent(hash32: string, meta: AssetMeta): Promise<'created' | 'already-exists'>;
72
+ getMeta(hash32: string): Promise<AssetMeta | null>;
73
+ listMeta(input?: {
74
+ cursor?: string;
75
+ limit?: number;
76
+ }): Promise<{
77
+ items: AssetMeta[];
78
+ nextCursor?: string;
79
+ }>;
80
+ deleteMeta(hash32: string): Promise<void>;
81
+ }
82
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/assets/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Asset store v2 contract.
3
+ *
4
+ * Pure type declarations only — no runtime imports. This lets client-side code
5
+ * (the editor) `import type` from this file without ever pulling node:fs or the
6
+ * S3 SDK into a browser bundle. The concrete stores (store-local.ts, store-s3.ts)
7
+ * and the factory are server-only and must never be imported from client code.
8
+ */
9
+ export {};
10
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/assets/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
@@ -28,8 +28,8 @@ export declare const GroupsFileSchema: z.ZodObject<{
28
28
  description?: string | undefined;
29
29
  }>, "many">;
30
30
  }, "strip", z.ZodTypeAny, {
31
- updatedAt: string;
32
31
  version: 1;
32
+ updatedAt: string;
33
33
  groups: {
34
34
  name: string;
35
35
  members: string[];
@@ -39,8 +39,8 @@ export declare const GroupsFileSchema: z.ZodObject<{
39
39
  updatedBy: string;
40
40
  contentVersion?: number | undefined;
41
41
  }, {
42
- updatedAt: string;
43
42
  version: 1;
43
+ updatedAt: string;
44
44
  groups: {
45
45
  name: string;
46
46
  members: string[];
@@ -81,8 +81,8 @@ export declare const PermissionsFileSchema: z.ZodObject<{
81
81
  } | undefined;
82
82
  }>, "many">;
83
83
  }, "strip", z.ZodTypeAny, {
84
- updatedAt: string;
85
84
  version: 1;
85
+ updatedAt: string;
86
86
  updatedBy: string;
87
87
  pathPermissions: {
88
88
  path: string & {
@@ -103,8 +103,8 @@ export declare const PermissionsFileSchema: z.ZodObject<{
103
103
  }[];
104
104
  contentVersion?: number | undefined;
105
105
  }, {
106
- updatedAt: string;
107
106
  version: 1;
107
+ updatedAt: string;
108
108
  updatedBy: string;
109
109
  pathPermissions: {
110
110
  path: string & {
@@ -257,11 +257,11 @@ var init_types2 = __esm({
257
257
  "rich-text",
258
258
  "markdown",
259
259
  "mdx",
260
- "image",
261
260
  "code"
262
261
  ];
263
262
  fieldTypes = [
264
263
  ...primitiveFieldTypes,
264
+ "image",
265
265
  "select",
266
266
  "reference",
267
267
  "object",
@@ -273,7 +273,7 @@ var init_types2 = __esm({
273
273
 
274
274
  // dist/config/schemas/field.js
275
275
  import { z as z2 } from "zod";
276
- var fieldBaseSchema, selectOptionSchema, referenceOptionSchema, primitiveFieldSchema, selectFieldSchema, referenceFieldSchema, fieldHolder, blockSchema, blockFieldSchema, objectFieldSchema, inlineGroupFieldSchema, customFieldSchema, knownFieldSchema, fieldSchema;
276
+ var fieldBaseSchema, selectOptionSchema, referenceOptionSchema, primitiveFieldSchema, ASPECT_RATIO_RE, imageFieldSchema, selectFieldSchema, referenceFieldSchema, fieldHolder, blockSchema, blockFieldSchema, objectFieldSchema, inlineGroupFieldSchema, customFieldSchema, knownFieldSchema, fieldSchema;
277
277
  var init_field = __esm({
278
278
  "dist/config/schemas/field.js"() {
279
279
  "use strict";
@@ -304,6 +304,12 @@ var init_field = __esm({
304
304
  primitiveFieldSchema = fieldBaseSchema.extend({
305
305
  type: z2.enum(primitiveFieldTypes)
306
306
  });
307
+ ASPECT_RATIO_RE = /^[1-9][0-9]*:[1-9][0-9]*$/;
308
+ imageFieldSchema = fieldBaseSchema.extend({
309
+ type: z2.literal("image"),
310
+ aspect: z2.string().regex(ASPECT_RATIO_RE, 'aspect must be "W:H" with positive integers (e.g. "16:9")').optional(),
311
+ altOptional: z2.boolean().optional()
312
+ });
307
313
  selectFieldSchema = fieldBaseSchema.extend({
308
314
  type: z2.literal("select"),
309
315
  options: z2.array(selectOptionSchema).min(1)
@@ -346,6 +352,7 @@ var init_field = __esm({
346
352
  primitiveFieldSchema,
347
353
  selectFieldSchema,
348
354
  referenceFieldSchema,
355
+ imageFieldSchema,
349
356
  objectFieldSchema,
350
357
  blockFieldSchema,
351
358
  inlineGroupFieldSchema
@@ -396,16 +403,21 @@ var mediaSchema;
396
403
  var init_media = __esm({
397
404
  "dist/config/schemas/media.js"() {
398
405
  "use strict";
406
+ init_collection();
399
407
  mediaSchema = z4.discriminatedUnion("adapter", [
400
408
  z4.object({
401
409
  adapter: z4.literal("local"),
402
- publicBaseUrl: z4.string().url().optional()
410
+ publicBaseUrl: z4.string().url().optional(),
411
+ /** Root directory for local asset storage. Defaults to the caller's dev-assets dir. */
412
+ directory: relativePathSchema.optional()
403
413
  }),
404
414
  z4.object({
405
415
  adapter: z4.literal("s3"),
406
416
  bucket: z4.string().min(1),
407
417
  region: z4.string().min(1),
408
- publicBaseUrl: z4.string().url().optional()
418
+ publicBaseUrl: z4.string().url().optional(),
419
+ /** Max upload size in bytes for presigned direct uploads. Defaults to 50 MiB. */
420
+ maxUploadBytes: z4.number().int().positive().optional()
409
421
  }),
410
422
  z4.object({
411
423
  adapter: z4.literal("lfs"),
@@ -3025,7 +3037,7 @@ ${String(value)}`;
3025
3037
  case "image":
3026
3038
  return `${heading} ${label}${descriptionLine}
3027
3039
 
3028
- ![${label}](${String(value)})`;
3040
+ ${formatImageMarkdown(value, label)}`;
3029
3041
  case "code":
3030
3042
  return `${heading} ${label}${descriptionLine}
3031
3043
 
@@ -3127,7 +3139,7 @@ function formatCellValue(field, value) {
3127
3139
  case "select":
3128
3140
  return Array.isArray(value) ? value.map((v) => resolveSelectLabel(field, v)).join(", ") : resolveSelectLabel(field, value);
3129
3141
  case "image":
3130
- return `![](${String(value)})`;
3142
+ return formatImageMarkdown(value, "");
3131
3143
  default:
3132
3144
  return String(value);
3133
3145
  }
@@ -3169,6 +3181,22 @@ ${items}`;
3169
3181
 
3170
3182
  ${formatReference(value)}`;
3171
3183
  }
3184
+ function isImageValueLike(value) {
3185
+ return typeof value === "object" && value !== null && !Array.isArray(value) && typeof value.src === "string";
3186
+ }
3187
+ function sanitizeMarkdownAltText(text) {
3188
+ return text.replace(/[[\]\\]/g, "").replace(/\r?\n+/g, " ").trim();
3189
+ }
3190
+ function encodeMarkdownLinkDestination(src) {
3191
+ return src.replace(/[\r\n]+/g, "").replace(/ /g, "%20").replace(/\(/g, "%28").replace(/\)/g, "%29");
3192
+ }
3193
+ function formatImageMarkdown(value, altFallback) {
3194
+ if (!isImageValueLike(value)) {
3195
+ return String(value);
3196
+ }
3197
+ const alt = typeof value.alt === "string" && value.alt.trim() !== "" ? value.alt : altFallback;
3198
+ return `![${sanitizeMarkdownAltText(alt)}](${encodeMarkdownLinkDestination(value.src)})`;
3199
+ }
3172
3200
  function formatReference(value) {
3173
3201
  if (typeof value === "object" && value !== null) {
3174
3202
  const ref = value;
@@ -3246,6 +3274,8 @@ function formatInlineValue(field, value) {
3246
3274
  return value ? "Yes" : "No";
3247
3275
  if (field.type === "reference")
3248
3276
  return formatReference(value);
3277
+ if (field.type === "image")
3278
+ return formatImageMarkdown(value, field.label || field.name);
3249
3279
  return String(value);
3250
3280
  }
3251
3281
  function yamlValue(value) {
@@ -5171,6 +5201,20 @@ var listCollectionEntries = async (root, collection) => {
5171
5201
  // dist/validation/entry-validator.js
5172
5202
  init_config2();
5173
5203
 
5204
+ // dist/assets/transform-directives.js
5205
+ var MAX_INPUT_PIXELS = 4096 * 4096;
5206
+ function isValidCropRect(x, y, w, h) {
5207
+ for (const n of [x, y, w, h]) {
5208
+ if (!Number.isFinite(n) || n < 0 || n > 1)
5209
+ return false;
5210
+ }
5211
+ if (w <= 0 || h <= 0)
5212
+ return false;
5213
+ if (x + w > 1 || y + h > 1)
5214
+ return false;
5215
+ return true;
5216
+ }
5217
+
5174
5218
  // dist/validation/field-traversal.js
5175
5219
  function isPlainRecord(value) {
5176
5220
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -5188,10 +5232,49 @@ function resolveBlockItem(blockField, item) {
5188
5232
 
5189
5233
  // dist/validation/entry-validator.js
5190
5234
  var KNOWN_FIELD_TYPES = new Set(fieldTypes);
5191
- var STRING_FIELD_TYPES = /* @__PURE__ */ new Set(["string", "rich-text", "markdown", "mdx", "image", "code"]);
5235
+ var STRING_FIELD_TYPES = /* @__PURE__ */ new Set(["string", "rich-text", "markdown", "mdx", "code"]);
5192
5236
  function isPlainRecord2(value) {
5193
5237
  return typeof value === "object" && value !== null && !Array.isArray(value);
5194
5238
  }
5239
+ function isPositiveInt(value) {
5240
+ return typeof value === "number" && Number.isInteger(value) && value > 0;
5241
+ }
5242
+ function isValidImageCropValue(value) {
5243
+ if (!isPlainRecord2(value))
5244
+ return false;
5245
+ const { x, y, w, h } = value;
5246
+ if (typeof x !== "number" || typeof y !== "number" || typeof w !== "number" || typeof h !== "number") {
5247
+ return false;
5248
+ }
5249
+ return isValidCropRect(x, y, w, h);
5250
+ }
5251
+ function validateImageValue(field, value, path19) {
5252
+ if (!isPlainRecord2(value)) {
5253
+ return [{ fieldPath: path19, message: "Expected an image object with { src, alt }" }];
5254
+ }
5255
+ const errors = [];
5256
+ if (typeof value.src !== "string" || value.src.trim() === "") {
5257
+ errors.push({ fieldPath: `${path19}.src`, message: "Image src is required" });
5258
+ }
5259
+ if (typeof value.alt !== "string") {
5260
+ errors.push({ fieldPath: `${path19}.alt`, message: "Image alt text is required" });
5261
+ } else if (value.alt.trim() === "" && field.altOptional !== true) {
5262
+ errors.push({ fieldPath: `${path19}.alt`, message: "Image alt text is required" });
5263
+ }
5264
+ if (value.width !== void 0 && !isPositiveInt(value.width)) {
5265
+ errors.push({ fieldPath: `${path19}.width`, message: "Image width must be a positive integer" });
5266
+ }
5267
+ if (value.height !== void 0 && !isPositiveInt(value.height)) {
5268
+ errors.push({
5269
+ fieldPath: `${path19}.height`,
5270
+ message: "Image height must be a positive integer"
5271
+ });
5272
+ }
5273
+ if (value.crop !== void 0 && !isValidImageCropValue(value.crop)) {
5274
+ errors.push({ fieldPath: `${path19}.crop`, message: "Invalid image crop rect" });
5275
+ }
5276
+ return errors;
5277
+ }
5195
5278
  function referenceValueId(value) {
5196
5279
  if (typeof value === "string")
5197
5280
  return value;
@@ -5232,6 +5315,8 @@ function validateScalar(field, value, path19) {
5232
5315
  return [{ fieldPath: path19, message: "Expected a reference id" }];
5233
5316
  }
5234
5317
  return [];
5318
+ case "image":
5319
+ return validateImageValue(field, value, path19);
5235
5320
  default:
5236
5321
  if (STRING_FIELD_TYPES.has(field.type)) {
5237
5322
  if (typeof value !== "string") {
package/dist/cli/init.js CHANGED
@@ -22,11 +22,11 @@ var init_types = __esm({
22
22
  "rich-text",
23
23
  "markdown",
24
24
  "mdx",
25
- "image",
26
25
  "code"
27
26
  ];
28
27
  fieldTypes = [
29
28
  ...primitiveFieldTypes,
29
+ "image",
30
30
  "select",
31
31
  "reference",
32
32
  "object",
@@ -38,7 +38,7 @@ var init_types = __esm({
38
38
 
39
39
  // dist/config/schemas/field.js
40
40
  import { z } from "zod";
41
- var fieldBaseSchema, selectOptionSchema, referenceOptionSchema, primitiveFieldSchema, selectFieldSchema, referenceFieldSchema, fieldHolder, blockSchema, blockFieldSchema, objectFieldSchema, inlineGroupFieldSchema, customFieldSchema, knownFieldSchema, fieldSchema;
41
+ var fieldBaseSchema, selectOptionSchema, referenceOptionSchema, primitiveFieldSchema, ASPECT_RATIO_RE, imageFieldSchema, selectFieldSchema, referenceFieldSchema, fieldHolder, blockSchema, blockFieldSchema, objectFieldSchema, inlineGroupFieldSchema, customFieldSchema, knownFieldSchema, fieldSchema;
42
42
  var init_field = __esm({
43
43
  "dist/config/schemas/field.js"() {
44
44
  "use strict";
@@ -69,6 +69,12 @@ var init_field = __esm({
69
69
  primitiveFieldSchema = fieldBaseSchema.extend({
70
70
  type: z.enum(primitiveFieldTypes)
71
71
  });
72
+ ASPECT_RATIO_RE = /^[1-9][0-9]*:[1-9][0-9]*$/;
73
+ imageFieldSchema = fieldBaseSchema.extend({
74
+ type: z.literal("image"),
75
+ aspect: z.string().regex(ASPECT_RATIO_RE, 'aspect must be "W:H" with positive integers (e.g. "16:9")').optional(),
76
+ altOptional: z.boolean().optional()
77
+ });
72
78
  selectFieldSchema = fieldBaseSchema.extend({
73
79
  type: z.literal("select"),
74
80
  options: z.array(selectOptionSchema).min(1)
@@ -111,6 +117,7 @@ var init_field = __esm({
111
117
  primitiveFieldSchema,
112
118
  selectFieldSchema,
113
119
  referenceFieldSchema,
120
+ imageFieldSchema,
114
121
  objectFieldSchema,
115
122
  blockFieldSchema,
116
123
  inlineGroupFieldSchema
@@ -161,16 +168,21 @@ var mediaSchema;
161
168
  var init_media = __esm({
162
169
  "dist/config/schemas/media.js"() {
163
170
  "use strict";
171
+ init_collection();
164
172
  mediaSchema = z3.discriminatedUnion("adapter", [
165
173
  z3.object({
166
174
  adapter: z3.literal("local"),
167
- publicBaseUrl: z3.string().url().optional()
175
+ publicBaseUrl: z3.string().url().optional(),
176
+ /** Root directory for local asset storage. Defaults to the caller's dev-assets dir. */
177
+ directory: relativePathSchema.optional()
168
178
  }),
169
179
  z3.object({
170
180
  adapter: z3.literal("s3"),
171
181
  bucket: z3.string().min(1),
172
182
  region: z3.string().min(1),
173
- publicBaseUrl: z3.string().url().optional()
183
+ publicBaseUrl: z3.string().url().optional(),
184
+ /** Max upload size in bytes for presigned direct uploads. Defaults to 50 MiB. */
185
+ maxUploadBytes: z3.number().int().positive().optional()
174
186
  }),
175
187
  z3.object({
176
188
  adapter: z3.literal("lfs"),
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/config/helpers.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EACV,YAAY,EACZ,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EACjB,MAAM,SAAS,CAAA;AAEhB,MAAM,MAAM,qBAAqB,GAAG,iBAAiB,CAAA;AAErD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,iBAAiB,GAAG,qBAAqB;;+BASrD,gBAAgB,KAAG,kBAAkB;EAwBnE;AAiBD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,mBAAmB,GAAI,GAAG,WAAW,oBAAoB,EAAE,KAAG,YAoB1E,CAAA"}
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/config/helpers.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EACV,YAAY,EACZ,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EACjB,MAAM,SAAS,CAAA;AAEhB,MAAM,MAAM,qBAAqB,GAAG,iBAAiB,CAAA;AAErD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,iBAAiB,GAAG,qBAAqB;;+BASrD,gBAAgB,KAAG,kBAAkB;EAgCnE;AAiBD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,mBAAmB,GAAI,GAAG,WAAW,oBAAoB,EAAE,KAAG,YAoB1E,CAAA"}
@@ -34,7 +34,7 @@ export function defineCanopyConfig(config) {
34
34
  // Client config helper - extracts safe subset and merges overrides
35
35
  // Note: flatSchema is loaded dynamically by the editor via API (from .collection.json files)
36
36
  client: (clientOverrides) => {
37
- const { defaultBaseBranch, defaultActiveBranch, contentRoot, editor, mode, entryLinkUrl } = validated;
37
+ const { defaultBaseBranch, defaultActiveBranch, contentRoot, editor, mode, entryLinkUrl, media, } = validated;
38
38
  const clientConfig = {
39
39
  defaultBaseBranch,
40
40
  defaultActiveBranch,
@@ -42,6 +42,7 @@ export function defineCanopyConfig(config) {
42
42
  editor,
43
43
  mode,
44
44
  entryLinkUrl,
45
+ assetBaseUrl: media?.publicBaseUrl,
45
46
  flatSchema: [], // Loaded dynamically by editor via API
46
47
  };
47
48
  // Merge client overrides (e.g., auth handlers from useClerkAuthConfig)
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../src/config/helpers.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AAWnD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,UAAU,kBAAkB,CAAC,MAAiD;IAClF,MAAM,SAAS,GAAG,oBAAoB,CAAC,MAA2B,CAAC,CAAA;IAEnE,OAAO;QACL,2DAA2D;QAC3D,MAAM,EAAE,SAAS;QAEjB,mEAAmE;QACnE,6FAA6F;QAC7F,MAAM,EAAE,CAAC,eAAkC,EAAsB,EAAE;YACjE,MAAM,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,GACvF,SAAS,CAAA;YACX,MAAM,YAAY,GAAuB;gBACvC,iBAAiB;gBACjB,mBAAmB;gBACnB,WAAW;gBACX,MAAM;gBACN,IAAI;gBACJ,YAAY;gBACZ,UAAU,EAAE,EAAE,EAAE,uCAAuC;aACxD,CAAA;YAED,uEAAuE;YACvE,IAAI,eAAe,EAAE,MAAM,EAAE,CAAC;gBAC5B,YAAY,CAAC,MAAM,GAAG;oBACpB,GAAG,YAAY,CAAC,MAAM;oBACtB,GAAG,eAAe,CAAC,MAAM;iBAC1B,CAAA;YACH,CAAC;YAED,OAAO,YAAY,CAAA;QACrB,CAAC;KACF,CAAA;AACH,CAAC;AAED;;;;GAIG;AACH,SAAS,mBAAmB,CAC1B,MAAkC,EAClC,GAAM,EACN,KAA8B;IAE9B,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAA;IACrB,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,GAAG,SAAiC,EAAgB,EAAE;IACxF,MAAM,MAAM,GAA+B,EAAE,CAAA;IAE7C,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QACjC,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAmC,EAAE,CAAC;YAC1E,mBAAmB,CAAC,MAAM,EAAE,GAAG,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAA;QACjD,CAAC;IACH,CAAC;IAED,OAAO,oBAAoB,CAAC;QAC1B,GAAG,MAAM;QACT,sFAAsF;QACtF,sFAAsF;QACtF,0EAA0E;QAC1E,gBAAgB,EAAE,MAAM,CAAC,gBAAgB,IAAI,EAAE;QAC/C,iBAAiB,EAAE,MAAM,CAAC,iBAAiB,IAAI,EAAE;QACjD,mFAAmF;QACnF,wEAAwE;QACxE,mFAAmF;KACpF,CAAC,CAAA;AACJ,CAAC,CAAA"}
1
+ {"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../src/config/helpers.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AAWnD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,UAAU,kBAAkB,CAAC,MAAiD;IAClF,MAAM,SAAS,GAAG,oBAAoB,CAAC,MAA2B,CAAC,CAAA;IAEnE,OAAO;QACL,2DAA2D;QAC3D,MAAM,EAAE,SAAS;QAEjB,mEAAmE;QACnE,6FAA6F;QAC7F,MAAM,EAAE,CAAC,eAAkC,EAAsB,EAAE;YACjE,MAAM,EACJ,iBAAiB,EACjB,mBAAmB,EACnB,WAAW,EACX,MAAM,EACN,IAAI,EACJ,YAAY,EACZ,KAAK,GACN,GAAG,SAAS,CAAA;YACb,MAAM,YAAY,GAAuB;gBACvC,iBAAiB;gBACjB,mBAAmB;gBACnB,WAAW;gBACX,MAAM;gBACN,IAAI;gBACJ,YAAY;gBACZ,YAAY,EAAE,KAAK,EAAE,aAAa;gBAClC,UAAU,EAAE,EAAE,EAAE,uCAAuC;aACxD,CAAA;YAED,uEAAuE;YACvE,IAAI,eAAe,EAAE,MAAM,EAAE,CAAC;gBAC5B,YAAY,CAAC,MAAM,GAAG;oBACpB,GAAG,YAAY,CAAC,MAAM;oBACtB,GAAG,eAAe,CAAC,MAAM;iBAC1B,CAAA;YACH,CAAC;YAED,OAAO,YAAY,CAAA;QACrB,CAAC;KACF,CAAA;AACH,CAAC;AAED;;;;GAIG;AACH,SAAS,mBAAmB,CAC1B,MAAkC,EAClC,GAAM,EACN,KAA8B;IAE9B,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAA;IACrB,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,GAAG,SAAiC,EAAgB,EAAE;IACxF,MAAM,MAAM,GAA+B,EAAE,CAAA;IAE7C,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QACjC,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAmC,EAAE,CAAC;YAC1E,mBAAmB,CAAC,MAAM,EAAE,GAAG,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAA;QACjD,CAAC;IACH,CAAC;IAED,OAAO,oBAAoB,CAAC;QAC1B,GAAG,MAAM;QACT,sFAAsF;QACtF,sFAAsF;QACtF,0EAA0E;QAC1E,gBAAgB,EAAE,MAAM,CAAC,gBAAgB,IAAI,EAAE;QAC/C,iBAAiB,EAAE,MAAM,CAAC,iBAAiB,IAAI,EAAE;QACjD,mFAAmF;QACnF,wEAAwE;QACxE,mFAAmF;KACpF,CAAC,CAAA;AACJ,CAAC,CAAA"}
@@ -8,10 +8,10 @@
8
8
  * import { defineCanopyConfig, type FieldConfig } from 'canopycms/config'
9
9
  * ```
10
10
  */
11
- export type { PrimitiveFieldType, FieldType, ContentFormat, MediaAdapterKind, PermissionLevel, PermissionTarget, PathPermission, SelectOption, ReferenceOption, FieldConfig, BlockConfig, BlockFieldConfig, SelectFieldConfig, ReferenceFieldConfig, ObjectFieldConfig, InlineGroupFieldConfig, CustomFieldConfig, EntrySchema, BranchSchema, EntryTypeConfig, CollectionConfig, RootCollectionConfig, MediaConfig, CanopyEditorConfig, DefaultBranchAccess, DefaultPathAccess, DefaultBaseBranch, DefaultRemoteName, DefaultRemoteUrl, GitBotAuthorName, GitBotAuthorEmail, GithubTokenEnvVar, CanopyOperatingMode, ContentRoot, SourceRoot, CanopyConfig, CanopyConfigInput, CanopyConfigFragment, FlatSchemaItem, CanopyClientConfig, ClientOnlyFields, EntryValidationIssue, ValidateEntryInput, ValidateEntryHook, } from './types';
11
+ export type { PrimitiveFieldType, FieldType, ContentFormat, MediaAdapterKind, PermissionLevel, PermissionTarget, PathPermission, SelectOption, ReferenceOption, FieldConfig, BlockConfig, BlockFieldConfig, SelectFieldConfig, ReferenceFieldConfig, ImageFieldConfig, ImageFieldValue, ObjectFieldConfig, InlineGroupFieldConfig, CustomFieldConfig, EntrySchema, BranchSchema, EntryTypeConfig, CollectionConfig, RootCollectionConfig, MediaConfig, CanopyEditorConfig, DefaultBranchAccess, DefaultPathAccess, DefaultBaseBranch, DefaultRemoteName, DefaultRemoteUrl, GitBotAuthorName, GitBotAuthorEmail, GithubTokenEnvVar, CanopyOperatingMode, ContentRoot, SourceRoot, CanopyConfig, CanopyConfigInput, CanopyConfigFragment, FlatSchemaItem, CanopyClientConfig, ClientOnlyFields, EntryValidationIssue, ValidateEntryInput, ValidateEntryHook, } from './types';
12
12
  export { primitiveFieldTypes, fieldTypes } from './types';
13
13
  export { CanopyConfigSchema, getConfigDefaults, DEFAULT_PROD_WORKSPACE } from './schemas/config';
14
- export { fieldSchema, blockSchema, selectOptionSchema, referenceOptionSchema, } from './schemas/field';
14
+ export { fieldSchema, blockSchema, selectOptionSchema, referenceOptionSchema, imageFieldSchema, } from './schemas/field';
15
15
  export { collectionSchema, entryTypeSchema, relativePathSchema } from './schemas/collection';
16
16
  export { permissionTargetSchema, pathPermissionSchema } from './schemas/permissions';
17
17
  export { mediaSchema } from './schemas/media';