@doswiftly/storefront-sdk 22.5.1 → 22.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +47 -0
- package/README.md +53 -0
- package/dist/core/image.d.ts +43 -4
- package/dist/core/image.d.ts.map +1 -1
- package/dist/core/image.js +122 -4
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +4 -2
- package/dist/next/image-loader.d.ts +49 -0
- package/dist/next/image-loader.d.ts.map +1 -0
- package/dist/next/image-loader.js +69 -0
- package/dist/next/index.d.ts +9 -0
- package/dist/next/index.d.ts.map +1 -0
- package/dist/next/index.js +11 -0
- package/package.json +5 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,52 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 22.7.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 1bbf3e0: `<Image>` now optimizes images imported in code (`import hero from './hero.webp'`), not just `public/` ones. Such imports are bundled under `/_next/static/media/` and were previously served at full size — one large file for every `srcset` width. The loader now routes them through the image CDN, so they are resized per breakpoint and format-negotiated, typically a 90%+ byte reduction on large gallery/hero images. No code change is required: existing `import` + `<Image>` usage is optimized automatically on the next deploy. A dev-only console hint additionally flags `<Image>` sources that still bypass the CDN (for example external URLs).
|
|
8
|
+
|
|
9
|
+
## 22.6.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- b1e622d: Add a zero-config Next.js image loader. Re-export `createImageLoader()` from the new
|
|
14
|
+
`@doswiftly/storefront-sdk/next` entry in your `images.loaderFile` and every `<Image>`
|
|
15
|
+
is routed through the image CDN: product images get a real responsive `srcset` (a width
|
|
16
|
+
per entry, so you can drop `transform: { maxWidth }`) and local `public/` images are
|
|
17
|
+
resized and format-negotiated (AVIF/WebP) instead of shipped at full size. Build assets,
|
|
18
|
+
external / protocol-relative URLs and `data:` URIs pass through untouched, and
|
|
19
|
+
`<Image quality>` is a no-op (quality is applied server-side; the format is auto-negotiated). The pure
|
|
20
|
+
`buildImageLoaderUrl` helper is also exported from the package root for non-`<Image>` use.
|
|
21
|
+
|
|
22
|
+
Usage:
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
// lib/image-loader.ts
|
|
26
|
+
import { createImageLoader } from "@doswiftly/storefront-sdk/next";
|
|
27
|
+
export default createImageLoader();
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
// next.config.ts — bound the generated widths for fewer transforms / better cache hits
|
|
32
|
+
export default {
|
|
33
|
+
images: {
|
|
34
|
+
loader: "custom",
|
|
35
|
+
loaderFile: "./lib/image-loader.ts",
|
|
36
|
+
deviceSizes: [640, 750, 828, 1080, 1200, 1920],
|
|
37
|
+
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Migration:
|
|
43
|
+
- New install — nothing to do; existing `<Image>` usage keeps working.
|
|
44
|
+
- Optional — drop `transform: { maxWidth }` from product-image queries; the loader now sets the width per `srcset` entry.
|
|
45
|
+
|
|
46
|
+
### Patch Changes
|
|
47
|
+
|
|
48
|
+
- 91c3b25: Ship TypeScript types for the codegen recipe (`@doswiftly/storefront-operations/codegen`). A `codegen.ts` that imports `createCodegenConfig` now type-checks under `strict` mode, so `next build` no longer fails with a missing-declaration (implicit `any`) error on the recipe import.
|
|
49
|
+
|
|
3
50
|
## 22.5.1
|
|
4
51
|
|
|
5
52
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -229,6 +229,7 @@ retried.
|
|
|
229
229
|
| `@doswiftly/storefront-sdk` | Core: transport, middleware, clients, recovery, errors, format, enums, cookie contracts | **0** |
|
|
230
230
|
| `@doswiftly/storefront-sdk/react` | Providers, hooks, stores, pre-built UI components | react, zustand |
|
|
231
231
|
| `@doswiftly/storefront-sdk/react/server` | Server client factory, SDK-BFF auth route, cookie readers | react (peer; `getInitialAuth` additionally requires Next.js) |
|
|
232
|
+
| `@doswiftly/storefront-sdk/next` | Zero-config Next.js adapters — `createImageLoader()` for `images.loaderFile` | **0** |
|
|
232
233
|
| `@doswiftly/storefront-sdk/cache` | Cache strategy functions | **0** |
|
|
233
234
|
|
|
234
235
|
## Authentication
|
|
@@ -735,6 +736,58 @@ import { Money, PriceDisplay, CartCount } from '@doswiftly/storefront-sdk/react'
|
|
|
735
736
|
<CartCount count={3} label="items" />
|
|
736
737
|
```
|
|
737
738
|
|
|
739
|
+
## Images — zero-config `next/image` loader
|
|
740
|
+
|
|
741
|
+
`createImageLoader()` is a drop-in loader for Next.js `images.loaderFile`. It
|
|
742
|
+
routes every `<Image>` through the image CDN so each one is resized to the
|
|
743
|
+
viewport (real responsive `srcset`) and format-negotiated (AVIF/WebP from the
|
|
744
|
+
browser `Accept` header) — no per-image configuration.
|
|
745
|
+
|
|
746
|
+
Create the loader file (zero arguments):
|
|
747
|
+
|
|
748
|
+
```ts
|
|
749
|
+
// lib/image-loader.ts
|
|
750
|
+
import { createImageLoader } from '@doswiftly/storefront-sdk/next';
|
|
751
|
+
|
|
752
|
+
export default createImageLoader();
|
|
753
|
+
```
|
|
754
|
+
|
|
755
|
+
Point Next.js at it, and bound the generated sizes so a fixed set of widths is
|
|
756
|
+
produced (fewer transforms, better cache hit rate):
|
|
757
|
+
|
|
758
|
+
```ts
|
|
759
|
+
// next.config.ts
|
|
760
|
+
const nextConfig = {
|
|
761
|
+
images: {
|
|
762
|
+
loader: 'custom',
|
|
763
|
+
loaderFile: './lib/image-loader.ts',
|
|
764
|
+
deviceSizes: [640, 750, 828, 1080, 1200, 1920],
|
|
765
|
+
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
|
|
766
|
+
},
|
|
767
|
+
};
|
|
768
|
+
export default nextConfig;
|
|
769
|
+
```
|
|
770
|
+
|
|
771
|
+
The loader is global and handles each `<Image src>` by category:
|
|
772
|
+
|
|
773
|
+
| `src` | Handling |
|
|
774
|
+
|-------|----------|
|
|
775
|
+
| Product image (CDN URL from the GraphQL API) | Width set per `srcset` entry — drop `transform: { maxWidth }`, the loader owns the width |
|
|
776
|
+
| Local `public/` image (e.g. `/hero.webp`) | Routed through the CDN, resized + format-negotiated |
|
|
777
|
+
| Image imported in code (`import hero from './hero.webp'`) | Routed through the CDN — the framework bundles it under `/_next/static/media/`, the loader resizes it per `srcset` (no code change needed) |
|
|
778
|
+
| Other build asset (`/_next/*` JS/CSS, `/_nuxt/`, `/_astro/`, `/_app/`), absolute external URL, `data:` URI, SVG | Returned unchanged |
|
|
779
|
+
|
|
780
|
+
The loader reads `NEXT_PUBLIC_SHOP_ID`, `NEXT_PUBLIC_DEPLOYMENT_COMMIT`, and
|
|
781
|
+
`NEXT_PUBLIC_IMGPROXY_BASE` — all injected by the platform at build/deploy time. When
|
|
782
|
+
they are absent (e.g. local `doswiftly dev`, where `public/` images are served by the
|
|
783
|
+
dev server) the loader leaves local `public/` images untouched; product images, being
|
|
784
|
+
absolute CDN URLs, are unaffected. `<Image quality>` is a no-op — quality is applied
|
|
785
|
+
server-side (the format is still auto-negotiated AVIF/WebP). Pass explicit overrides only for non-standard setups:
|
|
786
|
+
`createImageLoader({ shopId, version, cdnBase })`.
|
|
787
|
+
|
|
788
|
+
For non-`<Image>` usage (CSS backgrounds, raw `<img>`), build CDN URLs yourself
|
|
789
|
+
with the pure helper `buildImageLoaderUrl` from `@doswiftly/storefront-sdk`.
|
|
790
|
+
|
|
738
791
|
## Middleware pipeline
|
|
739
792
|
|
|
740
793
|
Default order (wired automatically by `StorefrontProvider`):
|
package/dist/core/image.d.ts
CHANGED
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Image types and utilities for DoSwiftly storefronts.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Two ways images reach the browser:
|
|
5
|
+
* - **Product images**: the GraphQL API returns ready-to-use CDN URLs (you may
|
|
6
|
+
* request a size with `url(transform: { maxWidth: 800 })`, but with the image
|
|
7
|
+
* loader below the size is no longer needed — the loader sets it per srcset entry).
|
|
8
|
+
* - **Local images** (your `public/` folder, e.g. a hero or logo used with the
|
|
9
|
+
* Next.js `<Image>` component): routed through the image CDN by the loader so they
|
|
10
|
+
* are resized + format-negotiated instead of shipped at full size.
|
|
6
11
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
12
|
+
* The image CDN auto-negotiates AVIF/WebP from the browser `Accept` header, so do NOT
|
|
13
|
+
* hardcode a format — the best one is served automatically.
|
|
14
|
+
*
|
|
15
|
+
* The URL-building below is pure + framework-agnostic. The zero-config Next.js loader
|
|
16
|
+
* (`createImageLoader()`) lives in `@doswiftly/storefront-sdk/next`.
|
|
9
17
|
*/
|
|
10
18
|
/**
|
|
11
19
|
* Image data from GraphQL API (matches Image type in storefront schema).
|
|
@@ -24,6 +32,37 @@ export interface ImageData {
|
|
|
24
32
|
/** ThumbHash placeholder (base64-encoded ~40 chars). Decode with thumbHashToDataURL() for blur preview. */
|
|
25
33
|
thumbhash?: string | null;
|
|
26
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* Base URL of the DoSwiftly image CDN. Platform-owned constant — storefronts never
|
|
37
|
+
* configure it (the override exists only for tests / non-standard hosting).
|
|
38
|
+
*/
|
|
39
|
+
export declare const IMAGE_CDN_BASE_URL = "https://img.doswiftly.pl";
|
|
40
|
+
/** Per-storefront configuration the image loader needs to build CDN URLs. */
|
|
41
|
+
export interface ImageLoaderConfig {
|
|
42
|
+
/** Shop identifier — namespaces this storefront's local images in CDN storage. */
|
|
43
|
+
shopId: string;
|
|
44
|
+
/** Current deployment version — busts the cache when a `public/` image is replaced under the same name. */
|
|
45
|
+
version: string;
|
|
46
|
+
/** Override the image CDN base URL. Defaults to {@link IMAGE_CDN_BASE_URL}. */
|
|
47
|
+
cdnBase?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Framework build-output path prefixes to leave untouched (hashed/immutable assets the
|
|
50
|
+
* framework already fingerprints). Defaults to {@link FRAMEWORK_BUILD_PREFIXES}.
|
|
51
|
+
*/
|
|
52
|
+
buildAssetPrefixes?: readonly string[];
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Framework build-output path prefixes — hashed/immutable assets the framework already
|
|
56
|
+
* fingerprints; never route them through the image CDN. Covers Next/Nuxt/Astro/SvelteKit
|
|
57
|
+
* (underscore-prefixed → collision-free with merchant `public/` folders). Override via
|
|
58
|
+
* {@link ImageLoaderConfig.buildAssetPrefixes} for other conventions (e.g. a Vite-based
|
|
59
|
+
* framework serving build output under `/assets/`, which can collide with `public/assets/`).
|
|
60
|
+
*/
|
|
61
|
+
export declare const FRAMEWORK_BUILD_PREFIXES: readonly ["/_next/", "/_nuxt/", "/_astro/", "/_app/"];
|
|
62
|
+
export declare function buildImageLoaderUrl(config: ImageLoaderConfig, args: {
|
|
63
|
+
src: string;
|
|
64
|
+
width: number;
|
|
65
|
+
}): string;
|
|
27
66
|
/**
|
|
28
67
|
* Decode a base64-encoded ThumbHash to a data URL for use as a blur placeholder.
|
|
29
68
|
*
|
package/dist/core/image.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../../src/core/image.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../../src/core/image.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,gFAAgF;IAChF,GAAG,EAAE,MAAM,CAAC;IACZ,uCAAuC;IACvC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,qCAAqC;IACrC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,sCAAsC;IACtC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,eAAe;IACf,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,2GAA2G;IAC3G,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAMD;;;GAGG;AACH,eAAO,MAAM,kBAAkB,6BAA6B,CAAC;AAE7D,6EAA6E;AAC7E,MAAM,WAAW,iBAAiB;IAChC,kFAAkF;IAClF,MAAM,EAAE,MAAM,CAAC;IACf,2GAA2G;IAC3G,OAAO,EAAE,MAAM,CAAC;IAChB,+EAA+E;IAC/E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,kBAAkB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACxC;AAED;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,uDAAwD,CAAC;AA2C9F,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,iBAAiB,EACzB,IAAI,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACnC,MAAM,CAwDR;AAOD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAiD5F"}
|
package/dist/core/image.js
CHANGED
|
@@ -1,12 +1,130 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Image types and utilities for DoSwiftly storefronts.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Two ways images reach the browser:
|
|
5
|
+
* - **Product images**: the GraphQL API returns ready-to-use CDN URLs (you may
|
|
6
|
+
* request a size with `url(transform: { maxWidth: 800 })`, but with the image
|
|
7
|
+
* loader below the size is no longer needed — the loader sets it per srcset entry).
|
|
8
|
+
* - **Local images** (your `public/` folder, e.g. a hero or logo used with the
|
|
9
|
+
* Next.js `<Image>` component): routed through the image CDN by the loader so they
|
|
10
|
+
* are resized + format-negotiated instead of shipped at full size.
|
|
6
11
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
12
|
+
* The image CDN auto-negotiates AVIF/WebP from the browser `Accept` header, so do NOT
|
|
13
|
+
* hardcode a format — the best one is served automatically.
|
|
14
|
+
*
|
|
15
|
+
* The URL-building below is pure + framework-agnostic. The zero-config Next.js loader
|
|
16
|
+
* (`createImageLoader()`) lives in `@doswiftly/storefront-sdk/next`.
|
|
17
|
+
*/
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
// Image loader URL builder (client-side, pure) — feeds the Next.js `<Image>` loader
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
/**
|
|
22
|
+
* Base URL of the DoSwiftly image CDN. Platform-owned constant — storefronts never
|
|
23
|
+
* configure it (the override exists only for tests / non-standard hosting).
|
|
24
|
+
*/
|
|
25
|
+
export const IMAGE_CDN_BASE_URL = 'https://img.doswiftly.pl';
|
|
26
|
+
/**
|
|
27
|
+
* Framework build-output path prefixes — hashed/immutable assets the framework already
|
|
28
|
+
* fingerprints; never route them through the image CDN. Covers Next/Nuxt/Astro/SvelteKit
|
|
29
|
+
* (underscore-prefixed → collision-free with merchant `public/` folders). Override via
|
|
30
|
+
* {@link ImageLoaderConfig.buildAssetPrefixes} for other conventions (e.g. a Vite-based
|
|
31
|
+
* framework serving build output under `/assets/`, which can collide with `public/assets/`).
|
|
32
|
+
*/
|
|
33
|
+
export const FRAMEWORK_BUILD_PREFIXES = ['/_next/', '/_nuxt/', '/_astro/', '/_app/'];
|
|
34
|
+
/**
|
|
35
|
+
* Pure URL builder behind the Next.js image loader. Maps one `<Image>` request
|
|
36
|
+
* (`src` + the width Next.js asks for) to an image-CDN URL, in three cases:
|
|
37
|
+
*
|
|
38
|
+
* 1. **Product image** — `src` is already a CDN URL from the GraphQL API. The
|
|
39
|
+
* requested `width` is set per srcset entry, so the same image renders at the
|
|
40
|
+
* right size for each viewport (truly responsive).
|
|
41
|
+
* 2. **Local `public/` image** — `src` is a root-relative path (e.g. `/hero.webp`),
|
|
42
|
+
* routed through the CDN (resize + format negotiation) from the storefront's
|
|
43
|
+
* uploaded local images.
|
|
44
|
+
* 3. **Anything else** — Next.js build assets (`/_next/*`), absolute external URLs,
|
|
45
|
+
* and `data:` URIs are returned unchanged.
|
|
46
|
+
*
|
|
47
|
+
* Framework-agnostic + side-effect free.
|
|
48
|
+
*/
|
|
49
|
+
/**
|
|
50
|
+
* Image formats the loader routes through the resize CDN. MUST stay in sync with the
|
|
51
|
+
* backend deploy mirror — the loader may only point `<Image>` at a `public/` path the
|
|
52
|
+
* deploy actually mirrored. SVG is intentionally excluded: vectors are passed through,
|
|
53
|
+
* not rasterized.
|
|
54
|
+
*
|
|
55
|
+
* @sync-with backend cli.controller.ts `isPublicImageKey`
|
|
56
|
+
*/
|
|
57
|
+
const LOADER_IMAGE_EXTENSIONS = /\.(png|jpe?g|webp|avif|gif)$/i;
|
|
58
|
+
/**
|
|
59
|
+
* Path prefix of Next.js build-output **media** (images imported in code, e.g.
|
|
60
|
+
* `import hero from './hero.webp'` → content-hashed bundle). UNLIKE the rest of `/_next/*`
|
|
61
|
+
* (JS/CSS chunks, left untouched), media images ARE routed through the resize CDN: the deploy
|
|
62
|
+
* mirrors them to `s/{shopId}/_next-media/{suffix}`. The content hash in the filename is the
|
|
63
|
+
* version, so the CDN key is immutable (no `?v=` cache-bust).
|
|
64
|
+
*
|
|
65
|
+
* @sync-with backend deploy mirror (`_next/static/media/` prefix → `_next-media/` R2 key)
|
|
9
66
|
*/
|
|
67
|
+
const NEXT_STATIC_MEDIA_PREFIX = '/_next/static/media/';
|
|
68
|
+
/** Suffix after {@link NEXT_STATIC_MEDIA_PREFIX}, or `null` if `pathname` is not a Next media path. */
|
|
69
|
+
function nextStaticMediaSuffix(pathname) {
|
|
70
|
+
return pathname.startsWith(NEXT_STATIC_MEDIA_PREFIX) ? pathname.slice(NEXT_STATIC_MEDIA_PREFIX.length) : null;
|
|
71
|
+
}
|
|
72
|
+
export function buildImageLoaderUrl(config, args) {
|
|
73
|
+
// `||` (not `??`): an empty injected base must still fall back to the platform default.
|
|
74
|
+
const base = (config.cdnBase || IMAGE_CDN_BASE_URL).replace(/\/+$/, '');
|
|
75
|
+
const { src, width } = args;
|
|
76
|
+
// (1) Product image — already an absolute CDN URL. Set the per-srcset width.
|
|
77
|
+
if (src.startsWith(base)) {
|
|
78
|
+
try {
|
|
79
|
+
const url = new URL(src);
|
|
80
|
+
url.searchParams.set('width', String(width));
|
|
81
|
+
return url.toString();
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return src;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// (2) Local root-relative image — NOT protocol-relative (`//host`). Two sub-cases:
|
|
88
|
+
// (2a) a Next build-output media image (`/_next/static/media/*`), and (2b) a `public/` image.
|
|
89
|
+
if (src.startsWith('/') && !src.startsWith('//')) {
|
|
90
|
+
// Not in a deployed storefront (no shopId) → leave untouched, no resize.
|
|
91
|
+
if (!config.shopId)
|
|
92
|
+
return src;
|
|
93
|
+
// Drop any author query/hash — the loader owns `width` (and, for public/, the `?v=`).
|
|
94
|
+
const pathname = src.replace(/[?#].*$/, '');
|
|
95
|
+
// Only mirrored image formats. SVG + every non-image (JS/CSS/fonts…) pass through — the
|
|
96
|
+
// extension gate is the safety that keeps framework build chunks out of the resize CDN.
|
|
97
|
+
if (!LOADER_IMAGE_EXTENSIONS.test(pathname))
|
|
98
|
+
return src;
|
|
99
|
+
// (2a) Next build-output media image. The content hash in the filename IS the version, so
|
|
100
|
+
// the CDN key is immutable → no `?v=`. The deploy mirrors these to `s/{shopId}/_next-media/`.
|
|
101
|
+
const mediaSuffix = nextStaticMediaSuffix(pathname);
|
|
102
|
+
if (mediaSuffix !== null) {
|
|
103
|
+
// `encodeURIComponent` keeps `..` (unreserved) — harmless while the image CDN is unsigned +
|
|
104
|
+
// public; if HMAC signing is ever added, strip `..` segments here AND in the public/ branch.
|
|
105
|
+
const encoded = mediaSuffix.split('/').map(encodeURIComponent).join('/');
|
|
106
|
+
return `${base}/s/${config.shopId}/_next-media/${encoded}?width=${width}`;
|
|
107
|
+
}
|
|
108
|
+
// Any other framework build output (hashed/immutable, NOT mirrored) → untouched.
|
|
109
|
+
const buildPrefixes = config.buildAssetPrefixes ?? FRAMEWORK_BUILD_PREFIXES;
|
|
110
|
+
if (buildPrefixes.some((p) => pathname.startsWith(p)))
|
|
111
|
+
return src;
|
|
112
|
+
// (2b) Local public/ image. Name-stable key → needs the deploy version for the `?v=` cache-
|
|
113
|
+
// bust (the source key is overwritten in place when a public/ image is replaced).
|
|
114
|
+
if (!config.version)
|
|
115
|
+
return src;
|
|
116
|
+
// Encode per segment so spaces / non-ASCII filenames match the raw R2 key after the CDN
|
|
117
|
+
// percent-decodes the path (keeps `/` as the separator).
|
|
118
|
+
const path = pathname
|
|
119
|
+
.replace(/^\/+/, '')
|
|
120
|
+
.split('/')
|
|
121
|
+
.map(encodeURIComponent)
|
|
122
|
+
.join('/');
|
|
123
|
+
return `${base}/s/${config.shopId}/public/${path}?width=${width}&v=${config.version}`;
|
|
124
|
+
}
|
|
125
|
+
// (3) Build assets, protocol-relative/external URLs, data URIs — untouched.
|
|
126
|
+
return src;
|
|
127
|
+
}
|
|
10
128
|
// ---------------------------------------------------------------------------
|
|
11
129
|
// ThumbHash decoder (client-side) — algorithm by Evan Wallace
|
|
12
130
|
// Pure math, zero dependencies, framework-agnostic.
|
package/dist/core/index.d.ts
CHANGED
|
@@ -74,7 +74,7 @@ export { matchesRoute, type RouteProtectionConfig } from './auth/routes';
|
|
|
74
74
|
export { createSetTokenHandler, createClearTokenHandler, createWhoamiHandler, originAllowlistValidator, trustedForwardedHostValidator, } from './auth/handlers';
|
|
75
75
|
export type { OriginValidator, OriginValidatorContext } from './auth/handlers';
|
|
76
76
|
export { createAuthTokenClient, type AuthTokenClient } from './auth/token-client';
|
|
77
|
-
export { type ImageData, thumbHashToDataURL } from './image';
|
|
77
|
+
export { type ImageData, thumbHashToDataURL, IMAGE_CDN_BASE_URL, FRAMEWORK_BUILD_PREFIXES, buildImageLoaderUrl, type ImageLoaderConfig, } from './image';
|
|
78
78
|
export { getOperationName } from './client/operation-name';
|
|
79
79
|
export { hashQuery } from './client/hash';
|
|
80
80
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/core/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"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"}
|
package/dist/core/index.js
CHANGED
|
@@ -95,8 +95,10 @@ export { matchesRoute } from './auth/routes';
|
|
|
95
95
|
export { createSetTokenHandler, createClearTokenHandler, createWhoamiHandler, originAllowlistValidator, trustedForwardedHostValidator, } from './auth/handlers';
|
|
96
96
|
// Auth token client (client-side fetch helpers)
|
|
97
97
|
export { createAuthTokenClient } from './auth/token-client';
|
|
98
|
-
// Image types
|
|
99
|
-
|
|
98
|
+
// Image types + loader URL builder. Product images come pre-transformed from the
|
|
99
|
+
// GraphQL API; the pure `buildImageLoaderUrl` powers the Next.js `<Image>` loader
|
|
100
|
+
// (zero-config wrapper `createImageLoader()` lives in `@doswiftly/storefront-sdk/next`).
|
|
101
|
+
export { thumbHashToDataURL, IMAGE_CDN_BASE_URL, FRAMEWORK_BUILD_PREFIXES, buildImageLoaderUrl, } from './image';
|
|
100
102
|
// Utilities
|
|
101
103
|
export { getOperationName } from './client/operation-name';
|
|
102
104
|
export { hashQuery } from './client/hash';
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Next.js `<Image>` loader for DoSwiftly storefronts.
|
|
3
|
+
*
|
|
4
|
+
* Wire it once:
|
|
5
|
+
* ```ts
|
|
6
|
+
* // next.config.ts
|
|
7
|
+
* const nextConfig = { images: { loader: 'custom', loaderFile: './lib/image-loader.ts' } };
|
|
8
|
+
* ```
|
|
9
|
+
* ```ts
|
|
10
|
+
* // lib/image-loader.ts
|
|
11
|
+
* import { createImageLoader } from '@doswiftly/storefront-sdk/next';
|
|
12
|
+
* export default createImageLoader();
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* Zero configuration: the shop identifier, deployment version, and image-CDN base URL are
|
|
16
|
+
* read from the public environment variables the DoSwiftly deploy pipeline injects
|
|
17
|
+
* (`NEXT_PUBLIC_SHOP_ID`, `NEXT_PUBLIC_DEPLOYMENT_COMMIT`, `NEXT_PUBLIC_IMGPROXY_BASE`).
|
|
18
|
+
* Pass overrides only for tests or non-standard hosting.
|
|
19
|
+
*
|
|
20
|
+
* This entry is Next-specific (it reads `NEXT_PUBLIC_*`); the framework-agnostic core (`.`)
|
|
21
|
+
* stays free of it.
|
|
22
|
+
*/
|
|
23
|
+
import { type ImageLoaderConfig } from '../core/image';
|
|
24
|
+
/** Arguments Next.js passes to a custom image loader. */
|
|
25
|
+
export interface NextImageLoaderArgs {
|
|
26
|
+
src: string;
|
|
27
|
+
width: number;
|
|
28
|
+
/**
|
|
29
|
+
* Next's per-image quality hint. Not applied — the image CDN negotiates the format
|
|
30
|
+
* (AVIF/WebP) from the browser `Accept` header and applies a fixed server-side quality,
|
|
31
|
+
* so `<Image quality>` / `images.qualities` are intentional no-ops on this loader.
|
|
32
|
+
*/
|
|
33
|
+
quality?: number;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Create a Next.js custom image loader — the function `images.loaderFile` /
|
|
37
|
+
* `<Image loader>` expects.
|
|
38
|
+
*
|
|
39
|
+
* Each field defaults from the DoSwiftly deploy environment, so the common usage is
|
|
40
|
+
* argument-free: `export default createImageLoader();`. Any field passed explicitly
|
|
41
|
+
* overrides the env default (useful in tests / non-standard hosting).
|
|
42
|
+
*
|
|
43
|
+
* The env vars are read as LITERAL `process.env.NEXT_PUBLIC_*` member accesses on purpose:
|
|
44
|
+
* Next/Turbopack only inlines those into the client bundle. A dynamic `process.env[name]`
|
|
45
|
+
* read is left as-is and resolves to `undefined` in the browser — which would silently
|
|
46
|
+
* disable the loader and cause a server/client hydration mismatch.
|
|
47
|
+
*/
|
|
48
|
+
export declare function createImageLoader(config?: Partial<ImageLoaderConfig>): (args: NextImageLoaderArgs) => string;
|
|
49
|
+
//# sourceMappingURL=image-loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-loader.d.ts","sourceRoot":"","sources":["../../src/next/image-loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAuB,KAAK,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAE5E,yDAAyD;AACzD,MAAM,WAAW,mBAAmB;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,GAClC,CAAC,IAAI,EAAE,mBAAmB,KAAK,MAAM,CAevC"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Next.js `<Image>` loader for DoSwiftly storefronts.
|
|
3
|
+
*
|
|
4
|
+
* Wire it once:
|
|
5
|
+
* ```ts
|
|
6
|
+
* // next.config.ts
|
|
7
|
+
* const nextConfig = { images: { loader: 'custom', loaderFile: './lib/image-loader.ts' } };
|
|
8
|
+
* ```
|
|
9
|
+
* ```ts
|
|
10
|
+
* // lib/image-loader.ts
|
|
11
|
+
* import { createImageLoader } from '@doswiftly/storefront-sdk/next';
|
|
12
|
+
* export default createImageLoader();
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* Zero configuration: the shop identifier, deployment version, and image-CDN base URL are
|
|
16
|
+
* read from the public environment variables the DoSwiftly deploy pipeline injects
|
|
17
|
+
* (`NEXT_PUBLIC_SHOP_ID`, `NEXT_PUBLIC_DEPLOYMENT_COMMIT`, `NEXT_PUBLIC_IMGPROXY_BASE`).
|
|
18
|
+
* Pass overrides only for tests or non-standard hosting.
|
|
19
|
+
*
|
|
20
|
+
* This entry is Next-specific (it reads `NEXT_PUBLIC_*`); the framework-agnostic core (`.`)
|
|
21
|
+
* stays free of it.
|
|
22
|
+
*/
|
|
23
|
+
import { buildImageLoaderUrl } from '../core/image';
|
|
24
|
+
/**
|
|
25
|
+
* Create a Next.js custom image loader — the function `images.loaderFile` /
|
|
26
|
+
* `<Image loader>` expects.
|
|
27
|
+
*
|
|
28
|
+
* Each field defaults from the DoSwiftly deploy environment, so the common usage is
|
|
29
|
+
* argument-free: `export default createImageLoader();`. Any field passed explicitly
|
|
30
|
+
* overrides the env default (useful in tests / non-standard hosting).
|
|
31
|
+
*
|
|
32
|
+
* The env vars are read as LITERAL `process.env.NEXT_PUBLIC_*` member accesses on purpose:
|
|
33
|
+
* Next/Turbopack only inlines those into the client bundle. A dynamic `process.env[name]`
|
|
34
|
+
* read is left as-is and resolves to `undefined` in the browser — which would silently
|
|
35
|
+
* disable the loader and cause a server/client hydration mismatch.
|
|
36
|
+
*/
|
|
37
|
+
export function createImageLoader(config) {
|
|
38
|
+
const resolved = {
|
|
39
|
+
shopId: config?.shopId ?? process.env.NEXT_PUBLIC_SHOP_ID ?? '',
|
|
40
|
+
version: config?.version ?? process.env.NEXT_PUBLIC_DEPLOYMENT_COMMIT ?? '',
|
|
41
|
+
cdnBase: config?.cdnBase ?? process.env.NEXT_PUBLIC_IMGPROXY_BASE,
|
|
42
|
+
};
|
|
43
|
+
// Dev-only hint: surface raster images that the loader can't optimize (an external host, or
|
|
44
|
+
// a build asset outside `/_next/static/media/`). Only when a shopId is resolved — without one
|
|
45
|
+
// (local `doswiftly dev`) every image is an expected passthrough, so warning would be noise.
|
|
46
|
+
const warned = resolved.shopId && process.env.NODE_ENV !== 'production' ? new Set() : null;
|
|
47
|
+
return (args) => {
|
|
48
|
+
const url = buildImageLoaderUrl(resolved, args);
|
|
49
|
+
if (warned && url === args.src)
|
|
50
|
+
warnImageBypass(args.src, warned);
|
|
51
|
+
return url;
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Dev-only: warn once per src for an EXTERNAL raster image used in `<Image>` — it bypasses the
|
|
56
|
+
* DoSwiftly image CDN (served as-is, no resize / format negotiation). Local `/…` images (`public/`
|
|
57
|
+
* + code-imported `/_next/static/media/`) are routed automatically; a local image reaching here
|
|
58
|
+
* only means a missing platform env (a config issue, not a footgun), so it is NOT flagged. SVG
|
|
59
|
+
* (vector) and `data:` URIs are not bypasses.
|
|
60
|
+
*/
|
|
61
|
+
function warnImageBypass(src, warned) {
|
|
62
|
+
if (warned.has(src) || !/^(https?:)?\/\//.test(src) || !/\.(png|jpe?g|webp|avif|gif)(\?|#|$)/i.test(src))
|
|
63
|
+
return;
|
|
64
|
+
warned.add(src);
|
|
65
|
+
console.warn(`[DoSwiftly] <Image src="${src}"> is an external URL, so it loads at full size and is NOT ` +
|
|
66
|
+
`optimized (no automatic resizing or format conversion). To optimize it: move the image into ` +
|
|
67
|
+
`your public/ folder and use a root-relative src like "/photo.jpg", or use a product image ` +
|
|
68
|
+
`whose URL comes from the API (those are already optimized CDN URLs).`);
|
|
69
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @doswiftly/storefront-sdk/next — Next.js-specific helpers.
|
|
3
|
+
*
|
|
4
|
+
* Utilities that depend on Next.js conventions (the `<Image>` custom loader and
|
|
5
|
+
* build-time public env vars). Kept out of the framework-agnostic core (`.`) so the
|
|
6
|
+
* core stays portable to Node, Edge, Deno, and Bun.
|
|
7
|
+
*/
|
|
8
|
+
export { createImageLoader, type NextImageLoaderArgs } from './image-loader';
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/next/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,iBAAiB,EAAE,KAAK,mBAAmB,EAAE,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @doswiftly/storefront-sdk/next — Next.js-specific helpers.
|
|
3
|
+
*
|
|
4
|
+
* Utilities that depend on Next.js conventions (the `<Image>` custom loader and
|
|
5
|
+
* build-time public env vars). Kept out of the framework-agnostic core (`.`) so the
|
|
6
|
+
* core stays portable to Node, Edge, Deno, and Bun.
|
|
7
|
+
*/
|
|
8
|
+
export { createImageLoader } from './image-loader';
|
|
9
|
+
// The pure builder + its config/base (`buildImageLoaderUrl`, `IMAGE_CDN_BASE_URL`,
|
|
10
|
+
// `ImageLoaderConfig`) are framework-agnostic and live on the core entry
|
|
11
|
+
// (`@doswiftly/storefront-sdk`) — import them from there, not from `/next`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@doswiftly/storefront-sdk",
|
|
3
|
-
"version": "22.
|
|
3
|
+
"version": "22.7.0",
|
|
4
4
|
"description": "Storefront runtime SDK for DoSwiftly Commerce — layered transport, middleware pipeline, React providers, Zustand stores, cache strategies. 0 runtime dependencies in core.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -23,6 +23,10 @@
|
|
|
23
23
|
"types": "./dist/react/server/index.d.ts",
|
|
24
24
|
"default": "./dist/react/server/index.js"
|
|
25
25
|
},
|
|
26
|
+
"./next": {
|
|
27
|
+
"types": "./dist/next/index.d.ts",
|
|
28
|
+
"default": "./dist/next/index.js"
|
|
29
|
+
},
|
|
26
30
|
"./cache": {
|
|
27
31
|
"types": "./dist/core/cache.d.ts",
|
|
28
32
|
"default": "./dist/core/cache.js"
|