@preprio/toolkit 0.1.0-beta.5 → 0.1.0-beta.6
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 +14 -2
- package/dist/astro/index.cjs +1 -1
- package/dist/astro/index.js +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/nextjs/image-loader.cjs +17 -94
- package/dist/nextjs/image-loader.d.cts +8 -4
- package/dist/nextjs/image-loader.d.ts +8 -4
- package/dist/nextjs/image-loader.js +17 -94
- package/dist/nextjs/index.cjs +1 -1
- package/dist/nextjs/index.js +1 -1
- package/dist/nuxt/index.cjs +1 -1
- package/dist/nuxt/index.js +1 -1
- package/dist/sveltekit/index.cjs +1 -1
- package/dist/sveltekit/index.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -292,7 +292,11 @@ export default function RootLayout({ children }: { children: ReactNode }) {
|
|
|
292
292
|
|
|
293
293
|
### API Integration
|
|
294
294
|
|
|
295
|
-
Use `getPreprHeaders()` to forward
|
|
295
|
+
Use `getPreprHeaders()` to forward the request's Prepr personalization context to your Prepr GraphQL fetches. Prepr resolves segments and A/B variants from these headers, so a query sent without them always returns the default, unpersonalized content.
|
|
296
|
+
|
|
297
|
+
The headers are set by the middleware, so they are only present on routes its matcher covers — off-matcher requests yield an empty object.
|
|
298
|
+
|
|
299
|
+
The signature differs per framework: `getPreprHeaders()` is async and zero-argument on Next.js (it reads `headers()` for you), while the Astro, SvelteKit, and Nuxt builds are synchronous and take the request's `Headers`.
|
|
296
300
|
|
|
297
301
|
#### With the Fetch API
|
|
298
302
|
|
|
@@ -364,7 +368,15 @@ export default {
|
|
|
364
368
|
};
|
|
365
369
|
```
|
|
366
370
|
|
|
367
|
-
The
|
|
371
|
+
The API emits each asset's real dimensions, so a Prepr URL renders as-is:
|
|
372
|
+
|
|
373
|
+
```
|
|
374
|
+
https://393qibegr87z.b-cdn.net/w_800,h_600,fx_75,fy_67/b7ijwafd586-photo.jpg
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
The loader rewrites `w_` to the width Next asks for and scales `h_` alongside it, holding the emitted ratio — for a cropped asset that ratio is the authored crop box, so the crop survives the resize. Focal points (`fx`/`fy`) are percentages and carry through unchanged, as does any other option in the segment. Quality is not a CDN option and is ignored.
|
|
378
|
+
|
|
379
|
+
URLs with no transform segment pass through untouched — set `unoptimized` on those `<Image>`s.
|
|
368
380
|
|
|
369
381
|
See the runnable example at `examples/nextjs` for the full source of truth.
|
|
370
382
|
|
package/dist/astro/index.cjs
CHANGED
package/dist/astro/index.js
CHANGED
package/dist/index.cjs
CHANGED
package/dist/index.d.cts
CHANGED
|
@@ -5,7 +5,7 @@ export { f as PreprEnvironment, g as PreprEventType, h as PreprHeaderName, i as
|
|
|
5
5
|
* Package version; must match package.json. Lives in its own leaf module so
|
|
6
6
|
* core can import it without cycling through the barrel.
|
|
7
7
|
*/
|
|
8
|
-
declare const VERSION = "0.1.0-beta.
|
|
8
|
+
declare const VERSION = "0.1.0-beta.6";
|
|
9
9
|
|
|
10
10
|
interface ToolbarState {
|
|
11
11
|
locale: Locale;
|
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export { f as PreprEnvironment, g as PreprEventType, h as PreprHeaderName, i as
|
|
|
5
5
|
* Package version; must match package.json. Lives in its own leaf module so
|
|
6
6
|
* core can import it without cycling through the barrel.
|
|
7
7
|
*/
|
|
8
|
-
declare const VERSION = "0.1.0-beta.
|
|
8
|
+
declare const VERSION = "0.1.0-beta.6";
|
|
9
9
|
|
|
10
10
|
interface ToolbarState {
|
|
11
11
|
locale: Locale;
|
package/dist/index.js
CHANGED
|
@@ -23,100 +23,23 @@ __export(image_loader_exports, {
|
|
|
23
23
|
default: () => preprImageLoader
|
|
24
24
|
});
|
|
25
25
|
module.exports = __toCommonJS(image_loader_exports);
|
|
26
|
-
var
|
|
27
|
-
var
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
// crop extract: width
|
|
42
|
-
"eh"
|
|
43
|
-
// crop extract: height
|
|
44
|
-
]);
|
|
45
|
-
function isTransformSegment(segment) {
|
|
46
|
-
if (!segment) return false;
|
|
47
|
-
const parts = segment.split(",");
|
|
48
|
-
let sawKnownOption = false;
|
|
49
|
-
for (const part of parts) {
|
|
50
|
-
const underscore = part.indexOf("_");
|
|
51
|
-
if (underscore <= 0) return false;
|
|
52
|
-
const key = part.slice(0, underscore);
|
|
53
|
-
const value = part.slice(underscore + 1);
|
|
54
|
-
if (!value) return false;
|
|
55
|
-
if (KNOWN_OPTION_KEYS.has(key)) {
|
|
56
|
-
sawKnownOption = true;
|
|
26
|
+
var TRANSFORM_SEGMENT = /\/([^/]*\bw_\d+[^/]*)\//;
|
|
27
|
+
var isPositiveInteger = (value) => /^\d+$/.test(value);
|
|
28
|
+
function preprImageLoader({ src, width }) {
|
|
29
|
+
return src.replace(TRANSFORM_SEGMENT, (match, segment) => {
|
|
30
|
+
const options = segment.split(",").map((pair) => {
|
|
31
|
+
const underscore = pair.indexOf("_");
|
|
32
|
+
return [pair.slice(0, underscore), pair.slice(underscore + 1)];
|
|
33
|
+
});
|
|
34
|
+
const emittedWidth = options.find(([key]) => key === "w");
|
|
35
|
+
if (!emittedWidth || !isPositiveInteger(emittedWidth[1])) return match;
|
|
36
|
+
const emittedHeight = options.find(([key]) => key === "h");
|
|
37
|
+
if (emittedHeight && isPositiveInteger(emittedHeight[1])) {
|
|
38
|
+
emittedHeight[1] = String(
|
|
39
|
+
Math.round(width * Number(emittedHeight[1]) / Number(emittedWidth[1]))
|
|
40
|
+
);
|
|
57
41
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
function parseTransform(segment) {
|
|
62
|
-
return segment.split(",").map((part) => {
|
|
63
|
-
const underscore = part.indexOf("_");
|
|
64
|
-
return [part.slice(0, underscore), part.slice(underscore + 1)];
|
|
42
|
+
emittedWidth[1] = String(width);
|
|
43
|
+
return `/${options.map(([key, value]) => `${key}_${value}`).join(",")}/`;
|
|
65
44
|
});
|
|
66
45
|
}
|
|
67
|
-
function parsePreprUrl(rawUrl) {
|
|
68
|
-
let url;
|
|
69
|
-
try {
|
|
70
|
-
url = new URL(rawUrl);
|
|
71
|
-
} catch {
|
|
72
|
-
return null;
|
|
73
|
-
}
|
|
74
|
-
const isTransformableHost = TRANSFORMABLE_HOST_SUFFIXES.some(
|
|
75
|
-
(suffix) => url.hostname.endsWith(suffix)
|
|
76
|
-
);
|
|
77
|
-
if (!isTransformableHost) return null;
|
|
78
|
-
const rawSegments = url.pathname.split("/");
|
|
79
|
-
rawSegments.shift();
|
|
80
|
-
if (rawSegments.length === 0) return null;
|
|
81
|
-
const filename = rawSegments[rawSegments.length - 1];
|
|
82
|
-
const leading = rawSegments.slice(0, -1);
|
|
83
|
-
let transform = [];
|
|
84
|
-
const prefixSegments = [];
|
|
85
|
-
for (const segment of leading) {
|
|
86
|
-
if (transform.length === 0 && isTransformSegment(segment)) {
|
|
87
|
-
transform = parseTransform(segment);
|
|
88
|
-
} else if (segment === "") {
|
|
89
|
-
continue;
|
|
90
|
-
} else {
|
|
91
|
-
prefixSegments.push(segment);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
const prefix = url.origin + "/" + (prefixSegments.length > 0 ? prefixSegments.join("/") + "/" : "");
|
|
95
|
-
return { prefix, transform, filename: filename + url.search };
|
|
96
|
-
}
|
|
97
|
-
function buildTransformSegment(pairs) {
|
|
98
|
-
return pairs.map(([key, value]) => `${key}_${value}`).join(",");
|
|
99
|
-
}
|
|
100
|
-
function preprImageLoader({ src, width, quality }) {
|
|
101
|
-
const parsed = parsePreprUrl(src);
|
|
102
|
-
if (!parsed) return src;
|
|
103
|
-
const { prefix, transform, filename } = parsed;
|
|
104
|
-
const hasCropExtract = transform.some(([key]) => ["ex", "ey", "ew", "eh"].includes(key));
|
|
105
|
-
const next = [];
|
|
106
|
-
let setWidth = false;
|
|
107
|
-
for (const [key, value] of transform) {
|
|
108
|
-
if (key === "w") {
|
|
109
|
-
next.push(["w", String(width)]);
|
|
110
|
-
setWidth = true;
|
|
111
|
-
} else if (key === "h" && !hasCropExtract) {
|
|
112
|
-
continue;
|
|
113
|
-
} else if (key === "q") {
|
|
114
|
-
continue;
|
|
115
|
-
} else {
|
|
116
|
-
next.push([key, value]);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
if (!setWidth) next.push(["w", String(width)]);
|
|
120
|
-
if (quality) next.push(["q", String(quality)]);
|
|
121
|
-
return `${prefix}${buildTransformSegment(next)}/${filename}`;
|
|
122
|
-
}
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { ImageLoaderProps } from 'next/image';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Rewrites Prepr stream URLs to the width `next/image` asks for
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Rewrites Prepr stream URLs to the width `next/image` asks for. URLs with no
|
|
5
|
+
* transform segment (other hosts, relative paths) come back unchanged — set
|
|
6
|
+
* `unoptimized` on those `<Image>`s.
|
|
7
|
+
*
|
|
8
|
+
* No upper clamp: the emitted width is the asset's own size or its crop box,
|
|
9
|
+
* neither of which is a ceiling on what the CDN can serve, so capping there
|
|
10
|
+
* would rule out retina.
|
|
7
11
|
*/
|
|
8
|
-
declare function preprImageLoader({ src, width
|
|
12
|
+
declare function preprImageLoader({ src, width }: ImageLoaderProps): string;
|
|
9
13
|
|
|
10
14
|
export { preprImageLoader as default };
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { ImageLoaderProps } from 'next/image';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Rewrites Prepr stream URLs to the width `next/image` asks for
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Rewrites Prepr stream URLs to the width `next/image` asks for. URLs with no
|
|
5
|
+
* transform segment (other hosts, relative paths) come back unchanged — set
|
|
6
|
+
* `unoptimized` on those `<Image>`s.
|
|
7
|
+
*
|
|
8
|
+
* No upper clamp: the emitted width is the asset's own size or its crop box,
|
|
9
|
+
* neither of which is a ceiling on what the CDN can serve, so capping there
|
|
10
|
+
* would rule out retina.
|
|
7
11
|
*/
|
|
8
|
-
declare function preprImageLoader({ src, width
|
|
12
|
+
declare function preprImageLoader({ src, width }: ImageLoaderProps): string;
|
|
9
13
|
|
|
10
14
|
export { preprImageLoader as default };
|
|
@@ -1,101 +1,24 @@
|
|
|
1
1
|
// src/nextjs/image-loader.ts
|
|
2
|
-
var
|
|
3
|
-
var
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
// crop extract: width
|
|
18
|
-
"eh"
|
|
19
|
-
// crop extract: height
|
|
20
|
-
]);
|
|
21
|
-
function isTransformSegment(segment) {
|
|
22
|
-
if (!segment) return false;
|
|
23
|
-
const parts = segment.split(",");
|
|
24
|
-
let sawKnownOption = false;
|
|
25
|
-
for (const part of parts) {
|
|
26
|
-
const underscore = part.indexOf("_");
|
|
27
|
-
if (underscore <= 0) return false;
|
|
28
|
-
const key = part.slice(0, underscore);
|
|
29
|
-
const value = part.slice(underscore + 1);
|
|
30
|
-
if (!value) return false;
|
|
31
|
-
if (KNOWN_OPTION_KEYS.has(key)) {
|
|
32
|
-
sawKnownOption = true;
|
|
2
|
+
var TRANSFORM_SEGMENT = /\/([^/]*\bw_\d+[^/]*)\//;
|
|
3
|
+
var isPositiveInteger = (value) => /^\d+$/.test(value);
|
|
4
|
+
function preprImageLoader({ src, width }) {
|
|
5
|
+
return src.replace(TRANSFORM_SEGMENT, (match, segment) => {
|
|
6
|
+
const options = segment.split(",").map((pair) => {
|
|
7
|
+
const underscore = pair.indexOf("_");
|
|
8
|
+
return [pair.slice(0, underscore), pair.slice(underscore + 1)];
|
|
9
|
+
});
|
|
10
|
+
const emittedWidth = options.find(([key]) => key === "w");
|
|
11
|
+
if (!emittedWidth || !isPositiveInteger(emittedWidth[1])) return match;
|
|
12
|
+
const emittedHeight = options.find(([key]) => key === "h");
|
|
13
|
+
if (emittedHeight && isPositiveInteger(emittedHeight[1])) {
|
|
14
|
+
emittedHeight[1] = String(
|
|
15
|
+
Math.round(width * Number(emittedHeight[1]) / Number(emittedWidth[1]))
|
|
16
|
+
);
|
|
33
17
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
function parseTransform(segment) {
|
|
38
|
-
return segment.split(",").map((part) => {
|
|
39
|
-
const underscore = part.indexOf("_");
|
|
40
|
-
return [part.slice(0, underscore), part.slice(underscore + 1)];
|
|
18
|
+
emittedWidth[1] = String(width);
|
|
19
|
+
return `/${options.map(([key, value]) => `${key}_${value}`).join(",")}/`;
|
|
41
20
|
});
|
|
42
21
|
}
|
|
43
|
-
function parsePreprUrl(rawUrl) {
|
|
44
|
-
let url;
|
|
45
|
-
try {
|
|
46
|
-
url = new URL(rawUrl);
|
|
47
|
-
} catch {
|
|
48
|
-
return null;
|
|
49
|
-
}
|
|
50
|
-
const isTransformableHost = TRANSFORMABLE_HOST_SUFFIXES.some(
|
|
51
|
-
(suffix) => url.hostname.endsWith(suffix)
|
|
52
|
-
);
|
|
53
|
-
if (!isTransformableHost) return null;
|
|
54
|
-
const rawSegments = url.pathname.split("/");
|
|
55
|
-
rawSegments.shift();
|
|
56
|
-
if (rawSegments.length === 0) return null;
|
|
57
|
-
const filename = rawSegments[rawSegments.length - 1];
|
|
58
|
-
const leading = rawSegments.slice(0, -1);
|
|
59
|
-
let transform = [];
|
|
60
|
-
const prefixSegments = [];
|
|
61
|
-
for (const segment of leading) {
|
|
62
|
-
if (transform.length === 0 && isTransformSegment(segment)) {
|
|
63
|
-
transform = parseTransform(segment);
|
|
64
|
-
} else if (segment === "") {
|
|
65
|
-
continue;
|
|
66
|
-
} else {
|
|
67
|
-
prefixSegments.push(segment);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
const prefix = url.origin + "/" + (prefixSegments.length > 0 ? prefixSegments.join("/") + "/" : "");
|
|
71
|
-
return { prefix, transform, filename: filename + url.search };
|
|
72
|
-
}
|
|
73
|
-
function buildTransformSegment(pairs) {
|
|
74
|
-
return pairs.map(([key, value]) => `${key}_${value}`).join(",");
|
|
75
|
-
}
|
|
76
|
-
function preprImageLoader({ src, width, quality }) {
|
|
77
|
-
const parsed = parsePreprUrl(src);
|
|
78
|
-
if (!parsed) return src;
|
|
79
|
-
const { prefix, transform, filename } = parsed;
|
|
80
|
-
const hasCropExtract = transform.some(([key]) => ["ex", "ey", "ew", "eh"].includes(key));
|
|
81
|
-
const next = [];
|
|
82
|
-
let setWidth = false;
|
|
83
|
-
for (const [key, value] of transform) {
|
|
84
|
-
if (key === "w") {
|
|
85
|
-
next.push(["w", String(width)]);
|
|
86
|
-
setWidth = true;
|
|
87
|
-
} else if (key === "h" && !hasCropExtract) {
|
|
88
|
-
continue;
|
|
89
|
-
} else if (key === "q") {
|
|
90
|
-
continue;
|
|
91
|
-
} else {
|
|
92
|
-
next.push([key, value]);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
if (!setWidth) next.push(["w", String(width)]);
|
|
96
|
-
if (quality) next.push(["q", String(quality)]);
|
|
97
|
-
return `${prefix}${buildTransformSegment(next)}/${filename}`;
|
|
98
|
-
}
|
|
99
22
|
export {
|
|
100
23
|
preprImageLoader as default
|
|
101
24
|
};
|
package/dist/nextjs/index.cjs
CHANGED
|
@@ -38,7 +38,7 @@ module.exports = __toCommonJS(nextjs_exports);
|
|
|
38
38
|
var import_server = require("next/server");
|
|
39
39
|
|
|
40
40
|
// src/version.ts
|
|
41
|
-
var VERSION = "0.1.0-beta.
|
|
41
|
+
var VERSION = "0.1.0-beta.6";
|
|
42
42
|
|
|
43
43
|
// src/core/constants.ts
|
|
44
44
|
var COOKIE_PREVIEW_MODE = "Prepr-Preview-Mode";
|
package/dist/nextjs/index.js
CHANGED
package/dist/nuxt/index.cjs
CHANGED
package/dist/nuxt/index.js
CHANGED
package/dist/sveltekit/index.cjs
CHANGED
package/dist/sveltekit/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@preprio/toolkit",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.6",
|
|
4
4
|
"description": "Prepr personalization, A/B testing and preview toolkit for Next.js, Nuxt, Astro, SvelteKit and Express",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Prepr B.V. (https://prepr.io)",
|