@pantheon-systems/p1-media 0.4.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/README.md +322 -0
- package/dist/index.d.mts +272 -0
- package/dist/index.d.ts +272 -0
- package/dist/index.js +2940 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2897 -0
- package/dist/index.mjs.map +1 -0
- package/dist/server.d.mts +176 -0
- package/dist/server.d.ts +176 -0
- package/dist/server.js +246 -0
- package/dist/server.js.map +1 -0
- package/dist/server.mjs +213 -0
- package/dist/server.mjs.map +1 -0
- package/package.json +54 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ReactElement, ImgHTMLAttributes, ReactNode } from 'react';
|
|
3
|
+
import { ComponentConfig } from '@puckeditor/core';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* A metadata field advertised by the Worker's `GET /media/schema` endpoint.
|
|
7
|
+
* The set is Pantheon-defined and global for v1; the plugin renders one input
|
|
8
|
+
* per entry and iterates these to render metadata generically (req. R14).
|
|
9
|
+
*/
|
|
10
|
+
interface MetadataFieldDef {
|
|
11
|
+
name: string;
|
|
12
|
+
label: string;
|
|
13
|
+
type: "string";
|
|
14
|
+
required?: boolean;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* The value written by the `p1-media` field (rich mode). Carries the pinned
|
|
18
|
+
* asset identity plus a snapshot of the metadata defaults copied at edit time.
|
|
19
|
+
* `metaSchemaVersion` records the schema that produced the snapshot (req. R12).
|
|
20
|
+
* `alt` and any schema-driven fields (byline, caption, …) live alongside as
|
|
21
|
+
* string metadata; `width`/`height` may be present as captured numeric
|
|
22
|
+
* dimensions (a CLS win when rendered).
|
|
23
|
+
*/
|
|
24
|
+
interface MediaValue {
|
|
25
|
+
assetId: string;
|
|
26
|
+
versionId: string;
|
|
27
|
+
url: string;
|
|
28
|
+
metaSchemaVersion?: number;
|
|
29
|
+
alt?: string;
|
|
30
|
+
[meta: string]: string | number | undefined;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* string = basic mode (a bare CDN URL, kept first-class forever); object =
|
|
34
|
+
* rich mode. Every render helper accepts both.
|
|
35
|
+
*/
|
|
36
|
+
type MediaFieldValue = string | MediaValue;
|
|
37
|
+
/** Props produced by getMediaProps, spreadable onto `<img>` or next/image. */
|
|
38
|
+
interface MediaProps {
|
|
39
|
+
src: string;
|
|
40
|
+
alt: string;
|
|
41
|
+
width?: number;
|
|
42
|
+
height?: number;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
interface MediaConfig {
|
|
46
|
+
workerUrl: string;
|
|
47
|
+
siteId: string;
|
|
48
|
+
workstreamId?: string;
|
|
49
|
+
getAuthToken: () => Promise<string | null> | string | null;
|
|
50
|
+
/**
|
|
51
|
+
* Fallback metadata field schema for the rich `p1-media` field, used when
|
|
52
|
+
* `GET /media/schema` is absent/unreachable (lets the plugin ship before the
|
|
53
|
+
* Worker upgrade). Defaults to `[{ name: "alt", label: "Alt text", type: "string" }]`.
|
|
54
|
+
*/
|
|
55
|
+
metadataFields?: MetadataFieldDef[];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
type GetAuthToken = () => Promise<string | null> | string | null;
|
|
59
|
+
|
|
60
|
+
interface MediaPluginOptions {
|
|
61
|
+
/** The base URL of the Cloudflare Worker media API. Defaults to the production host. */
|
|
62
|
+
workerUrl?: string;
|
|
63
|
+
/**
|
|
64
|
+
* Site identifier used to scope media to a specific site. Defaults to the
|
|
65
|
+
* ambient puck-css site context (`P1PuckProvider`) when omitted — pass this
|
|
66
|
+
* explicitly only when rendering outside that provider, or to override it.
|
|
67
|
+
*/
|
|
68
|
+
siteId?: string;
|
|
69
|
+
/**
|
|
70
|
+
* Workstream (branch) identifier. Currently accepted for forward-compat but not
|
|
71
|
+
* read by the Worker for any scoping decision — omit unless a future release
|
|
72
|
+
* documents otherwise.
|
|
73
|
+
*/
|
|
74
|
+
workstreamId?: string;
|
|
75
|
+
/**
|
|
76
|
+
* Function that returns the current auth token, or null if unauthenticated.
|
|
77
|
+
* May be async. Defaults to the ambient puck-css auth context's `getToken`
|
|
78
|
+
* (`P1AuthProvider`) when omitted — pass this explicitly only when rendering
|
|
79
|
+
* outside that provider, or to override it.
|
|
80
|
+
*/
|
|
81
|
+
getAuthToken?: GetAuthToken;
|
|
82
|
+
/** Field name patterns that trigger the media picker (defaults to common image URL patterns) */
|
|
83
|
+
fieldNamePatterns?: RegExp[];
|
|
84
|
+
/**
|
|
85
|
+
* Fallback metadata field schema for the rich `p1-media` field, used when
|
|
86
|
+
* `GET /media/schema` is unavailable. Defaults to
|
|
87
|
+
* `[{ name: "alt", label: "Alt text", type: "string" }]`.
|
|
88
|
+
*/
|
|
89
|
+
metadataFields?: MetadataFieldDef[];
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Creates a Puck plugin that adds a media library backed by Cloudflare R2 + D1.
|
|
93
|
+
* It supports two field modes:
|
|
94
|
+
* - Basic: text fields matching image/media name patterns are replaced with the
|
|
95
|
+
* picker and store a clean CDN URL string. Render with `buildImageUrl()`.
|
|
96
|
+
* - Rich: a registered `p1-media` field type stores a MediaValue object (version
|
|
97
|
+
* URL + metadata such as alt). Render with `getMediaProps()` / `MediaImage` /
|
|
98
|
+
* `MediaFigure`. The editor's crop intent is carried as `?fit=…&gravity=…`.
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* ```tsx
|
|
102
|
+
* // Inside a P1 site (rendered within P1PuckProvider + P1AuthProvider):
|
|
103
|
+
* // siteId and getAuthToken are read from context automatically.
|
|
104
|
+
* const mediaPlugin = createMediaPlugin({});
|
|
105
|
+
*
|
|
106
|
+
* // Outside a P1 site, or to override the ambient context, pass explicitly:
|
|
107
|
+
* const mediaPlugin = createMediaPlugin({
|
|
108
|
+
* siteId: "my-site",
|
|
109
|
+
* getAuthToken: () => localStorage.getItem("token"),
|
|
110
|
+
* });
|
|
111
|
+
*
|
|
112
|
+
* <Puck plugins={[mediaPlugin]} config={config} data={data} />
|
|
113
|
+
* ```
|
|
114
|
+
*/
|
|
115
|
+
declare function createMediaPlugin(options: MediaPluginOptions): {
|
|
116
|
+
name: string;
|
|
117
|
+
overrides: {
|
|
118
|
+
fieldTypes: {
|
|
119
|
+
text: (props: any) => react_jsx_runtime.JSX.Element;
|
|
120
|
+
"p1-media": (props: any) => react_jsx_runtime.JSX.Element;
|
|
121
|
+
};
|
|
122
|
+
};
|
|
123
|
+
fieldTransforms: {
|
|
124
|
+
"p1-media": ({ value }: {
|
|
125
|
+
value: MediaFieldValue;
|
|
126
|
+
}) => MediaValue | {
|
|
127
|
+
url: string;
|
|
128
|
+
alt: string;
|
|
129
|
+
};
|
|
130
|
+
};
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Default field name patterns that trigger the media library picker.
|
|
135
|
+
* Targets image/logo/icon source fields while excluding navigation URLs
|
|
136
|
+
* (buttonUrl, linkUrl, ctaUrl, etc.) and alt text fields.
|
|
137
|
+
*/
|
|
138
|
+
declare const DEFAULT_MEDIA_PATTERNS: RegExp[];
|
|
139
|
+
|
|
140
|
+
interface ImageTransformParams {
|
|
141
|
+
width?: number;
|
|
142
|
+
height?: number;
|
|
143
|
+
/** `auto` negotiates avif → webp → jpeg from the request's Accept header. */
|
|
144
|
+
format?: "auto" | "webp" | "jpeg" | "png" | "gif" | "avif";
|
|
145
|
+
quality?: number;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Merges Cloudflare Images transform params onto a CDN image URL.
|
|
149
|
+
* Preserves any existing params in the URL — e.g. the editor's crop intent
|
|
150
|
+
* (`fit=cover&gravity=auto` for smart crop, `trim.*` for a manual crop).
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* buildImageUrl(data.heroImage, { width: 1200, height: 630, format: "webp" })
|
|
154
|
+
*/
|
|
155
|
+
declare function buildImageUrl(url: string, params: ImageTransformParams): string;
|
|
156
|
+
|
|
157
|
+
interface GetMediaPropsOptions {
|
|
158
|
+
/**
|
|
159
|
+
* The CDN image origin that serves media, e.g.
|
|
160
|
+
* "https://staging.media.p1.pantheon.io". This is the public image host —
|
|
161
|
+
* NOT the Worker API URL (`…workers.dev`), whose origin would reject every
|
|
162
|
+
* real image URL.
|
|
163
|
+
*
|
|
164
|
+
* Security (required): a value's `url` is untrusted document content — anyone
|
|
165
|
+
* who can edit a document, or call the CCR `/edits` API, controls it. Without
|
|
166
|
+
* an origin check a crafted `https://evil.example/beacon.png` turns every
|
|
167
|
+
* published render into a visitor-IP exfil beacon (and an SSRF under
|
|
168
|
+
* server-fetching `next/image`). getMediaProps therefore rejects any url that
|
|
169
|
+
* is not `https` on this exact origin. If `mediaBaseUrl` is omitted it
|
|
170
|
+
* **fails closed** (empty src) rather than degrading to an insecure pass-through.
|
|
171
|
+
*
|
|
172
|
+
* Local dev exception: when the configured base is itself `http` on a
|
|
173
|
+
* loopback host (`localhost`, `127.0.0.1`, `[::1]` — a local `wrangler dev`
|
|
174
|
+
* worker), same-origin `http` urls are allowed so rich values render locally.
|
|
175
|
+
*/
|
|
176
|
+
mediaBaseUrl?: string;
|
|
177
|
+
/** Transform params merged onto the validated URL (width, height, format, quality). */
|
|
178
|
+
transform?: ImageTransformParams;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Normalizes a `string | MediaValue | null` into `{ src, alt, width?, height? }`.
|
|
182
|
+
* A string (basic mode) yields `alt: ""`. `src` is validated against the CDN
|
|
183
|
+
* origin (see GetMediaPropsOptions) and empty on rejection.
|
|
184
|
+
*/
|
|
185
|
+
declare function getMediaProps(value: MediaFieldValue | null | undefined, options?: GetMediaPropsOptions): MediaProps;
|
|
186
|
+
/**
|
|
187
|
+
* Common-case `<img>` wrapper. Renders nothing when the src is rejected/empty
|
|
188
|
+
* (a broken foreign-origin image is never emitted). `alt` may be overridden;
|
|
189
|
+
* otherwise it comes from the value.
|
|
190
|
+
*/
|
|
191
|
+
declare function MediaImage({ image, mediaBaseUrl, transform, alt, ...rest }: {
|
|
192
|
+
image: MediaFieldValue | null | undefined;
|
|
193
|
+
mediaBaseUrl?: string;
|
|
194
|
+
transform?: ImageTransformParams;
|
|
195
|
+
} & Omit<ImgHTMLAttributes<HTMLImageElement>, "src">): ReactElement | null;
|
|
196
|
+
/**
|
|
197
|
+
* `<figure>` that renders the image plus the schema-advertised text fields as
|
|
198
|
+
* ESCAPED text (React default escaping — never dangerouslySetInnerHTML, req.
|
|
199
|
+
* R6). Renders nothing when the src is rejected/empty.
|
|
200
|
+
*/
|
|
201
|
+
declare function MediaFigure({ image, schema, mediaBaseUrl, transform, className, captionClassName, }: {
|
|
202
|
+
image: MediaFieldValue | null | undefined;
|
|
203
|
+
schema?: MetadataFieldDef[];
|
|
204
|
+
mediaBaseUrl?: string;
|
|
205
|
+
transform?: ImageTransformParams;
|
|
206
|
+
className?: string;
|
|
207
|
+
captionClassName?: string;
|
|
208
|
+
}): ReactElement | null;
|
|
209
|
+
|
|
210
|
+
interface MediaFigureBlockProps {
|
|
211
|
+
photo: MediaFieldValue | null;
|
|
212
|
+
}
|
|
213
|
+
interface MediaFigureBlockOptions {
|
|
214
|
+
/**
|
|
215
|
+
* The CDN image origin used to validate value URLs — NOT the Worker API
|
|
216
|
+
* URL. See GetMediaPropsOptions. Defaults to the production origin
|
|
217
|
+
* ("https://media.p1.pantheon.io"); override for sandbox/staging/local dev.
|
|
218
|
+
*/
|
|
219
|
+
mediaBaseUrl?: string;
|
|
220
|
+
/**
|
|
221
|
+
* Render-time transform. Include BOTH width and height — the editor's crop
|
|
222
|
+
* intent only changes the output when there is a target aspect ratio.
|
|
223
|
+
* Defaults to `{ width: 1200, height: 630, format: "auto" }`.
|
|
224
|
+
*/
|
|
225
|
+
transform?: ImageTransformParams;
|
|
226
|
+
/** Component label in the Puck sidebar. Defaults to "Media Figure". */
|
|
227
|
+
label?: string;
|
|
228
|
+
/** Label of the media field. Defaults to "Photo". */
|
|
229
|
+
fieldLabel?: string;
|
|
230
|
+
/** Metadata schema passed to MediaFigure — pins figcaption field order/labels. */
|
|
231
|
+
schema?: MetadataFieldDef[];
|
|
232
|
+
className?: string;
|
|
233
|
+
captionClassName?: string;
|
|
234
|
+
/** Rendered when no photo is chosen (or its URL fails origin validation). */
|
|
235
|
+
placeholder?: ReactNode;
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Builds a registerable Puck component around the rich `p1-media` field and
|
|
239
|
+
* `MediaFigure`. Spares consumers the custom-field-type cast and the
|
|
240
|
+
* width+height transform pitfall:
|
|
241
|
+
*
|
|
242
|
+
* ```tsx
|
|
243
|
+
* const config = {
|
|
244
|
+
* components: {
|
|
245
|
+
* MediaFigureBlock: createMediaFigureBlock({ mediaBaseUrl: MEDIA_BASE }),
|
|
246
|
+
* },
|
|
247
|
+
* };
|
|
248
|
+
* ```
|
|
249
|
+
*/
|
|
250
|
+
declare function createMediaFigureBlock(options: MediaFigureBlockOptions): ComponentConfig<MediaFigureBlockProps>;
|
|
251
|
+
|
|
252
|
+
/** Narrows a MediaFieldValue to the rich object form. */
|
|
253
|
+
declare function isMediaValue(value: MediaFieldValue | null | undefined): value is MediaValue;
|
|
254
|
+
/**
|
|
255
|
+
* Builds the value the `p1-media` field writes on asset selection.
|
|
256
|
+
*
|
|
257
|
+
* R10 invariant: never synthesize a MediaValue unless BOTH `assetId` and
|
|
258
|
+
* `versionId` are present — otherwise fall back to a bare URL string (basic
|
|
259
|
+
* mode). A value with undefined identity is unfindable by "update usages" and
|
|
260
|
+
* can render `src=undefined`, so this fallback is load-bearing, not cosmetic.
|
|
261
|
+
* It also means picking from a pre-upgrade Worker (bare-array `GET /media`, no
|
|
262
|
+
* assetId) yields a string, so the plugin degrades cleanly before the cutover.
|
|
263
|
+
*/
|
|
264
|
+
declare function makeMediaValue(input: {
|
|
265
|
+
assetId?: string | null;
|
|
266
|
+
versionId?: string | null;
|
|
267
|
+
url: string;
|
|
268
|
+
metaSchemaVersion?: number;
|
|
269
|
+
metadata?: Record<string, string | number | undefined>;
|
|
270
|
+
}): MediaFieldValue;
|
|
271
|
+
|
|
272
|
+
export { DEFAULT_MEDIA_PATTERNS, type GetMediaPropsOptions, type ImageTransformParams, type MediaConfig, type MediaFieldValue, MediaFigure, type MediaFigureBlockOptions, type MediaFigureBlockProps, MediaImage, type MediaPluginOptions, type MediaProps, type MediaValue, type MetadataFieldDef, buildImageUrl, createMediaFigureBlock, createMediaPlugin, getMediaProps, isMediaValue, makeMediaValue };
|