@doswiftly/storefront-sdk 22.8.1 → 22.9.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.
@@ -50,6 +50,15 @@ export interface ImageLoaderConfig {
50
50
  * framework already fingerprints). Defaults to {@link FRAMEWORK_BUILD_PREFIXES}.
51
51
  */
52
52
  buildAssetPrefixes?: readonly string[];
53
+ /**
54
+ * Build clean asset URLs without the `/s/{shopId}` storage namespace. Set for a storefront served
55
+ * from a custom domain (`{cdnBase}/_next/static/media/…`), where the platform restores the namespace
56
+ * toward storage at the edge so the stored object key is unchanged. Defaults to `false` — the
57
+ * platform CDN keeps the namespace in the URL (`{cdnBase}/s/{shopId}/…`). The deploy pipeline sets
58
+ * this from the same provisioning signal that picks the asset host, so the URL form and the storage
59
+ * key can never disagree.
60
+ */
61
+ cleanUrl?: boolean;
53
62
  }
54
63
  /**
55
64
  * Framework build-output path prefixes — hashed/immutable assets the framework already
@@ -1 +1 @@
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;AAmF9F,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,iBAAiB,EACzB,IAAI,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACnC,MAAM,CAiER;AAOD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAiD5F"}
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;IACvC;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,uDAAwD,CAAC;AAyF9F,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,iBAAiB,EACzB,IAAI,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACnC,MAAM,CAqER;AAOD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAiD5F"}
@@ -72,24 +72,27 @@ function nextStaticMediaSuffix(pathname) {
72
72
  /** Scheme + authority of an absolute http(s) URL — everything up to the first path `/`. */
73
73
  const ABSOLUTE_URL_AUTHORITY = /^https?:\/\/[^/]+/i;
74
74
  /**
75
- * Suffix after this shop's `/s/{shopId}/_next/static/media/` when `src` is an ABSOLUTE URL — a
76
- * code-imported image whose `src` carries the storefront's Next.js `assetPrefix`
77
- * (`{assetPrefixBase}/s/{shopId}/_next/static/media/{suffix}`, which Next prepends before the loader
78
- * runs). `null` for relative srcs, other shops' assets, and foreign hosts. Anchored on `/s/{shopId}/`
79
- * so a foreign host that merely embeds the marker mid-path is rejected; matched on path structure, not
80
- * host the asset-prefix CDN host is not part of the loader config, only the resize base is.
75
+ * Suffix after the media segment when `src` is an ABSOLUTE URL — a code-imported image whose `src`
76
+ * carries the storefront's Next.js `assetPrefix` (which Next prepends before the loader runs). The
77
+ * anchor is the storage namespace `ns` (`/s/{shopId}` by default, empty when `cleanUrl`):
78
+ * - default: `{assetPrefixBase}/s/{shopId}/_next/static/media/{suffix}` (`ns` = `/s/{shopId}`);
79
+ * - clean: `{assetPrefixBase}/_next/static/media/{suffix}` (`ns` = ``).
80
+ * Anchored on the EXACT `${ns}/_next/static/media/`, so a foreign host that merely embeds the marker
81
+ * mid-path is rejected. `null` for relative srcs, other namespaces, and foreign hosts. Matched on
82
+ * path structure, not host — the asset-prefix host is not part of the loader config, only the resize
83
+ * base is.
81
84
  *
82
85
  * The path is taken from the RAW `src` (strip scheme/authority + query/hash), NOT via `URL.pathname`:
83
86
  * that percent-encodes the path, which would double-encode in {@link buildNextMediaCdnUrl}. The
84
87
  * root-relative counterpart {@link nextStaticMediaSuffix} likewise feeds a raw path, so both `src`
85
88
  * forms of one import — relative (no `assetPrefix`) and absolute (with it) — encode identically.
86
89
  */
87
- function absoluteNextMediaSuffix(src, shopId) {
90
+ function absoluteNextMediaSuffix(src, ns) {
88
91
  // Fast reject before any allocation: must be an absolute http(s) URL that mentions the media segment.
89
92
  if (!ABSOLUTE_URL_AUTHORITY.test(src) || !src.includes(NEXT_STATIC_MEDIA_PREFIX))
90
93
  return null;
91
94
  const path = src.replace(ABSOLUTE_URL_AUTHORITY, '').replace(/[?#].*$/, '');
92
- const prefix = `/s/${shopId}${NEXT_STATIC_MEDIA_PREFIX}`;
95
+ const prefix = `${ns}${NEXT_STATIC_MEDIA_PREFIX}`;
93
96
  return path.startsWith(prefix) ? path.slice(prefix.length) : null;
94
97
  }
95
98
  /**
@@ -101,16 +104,23 @@ function absoluteNextMediaSuffix(src, shopId) {
101
104
  * `encodeURIComponent` keeps `..` (unreserved) — harmless while the image CDN is unsigned + public;
102
105
  * if HMAC signing is ever added, strip `..` segments here AND in the public/ branch.
103
106
  *
107
+ * `ns` is the storage namespace (empty for a custom domain, `/s/{shopId}` for the platform CDN); the
108
+ * platform restores it toward storage for a custom domain, so the stored object key is the same.
109
+ *
104
110
  * @sync-with backend deploy mirror (`_next/static/media/` mirrored 1:1 into the image-CDN key)
105
111
  */
106
- function buildNextMediaCdnUrl(base, shopId, suffix, width) {
112
+ function buildNextMediaCdnUrl(base, ns, suffix, width) {
107
113
  const encoded = suffix.split('/').map(encodeURIComponent).join('/');
108
- return `${base}/s/${shopId}/_next/static/media/${encoded}?width=${width}`;
114
+ return `${base}${ns}/_next/static/media/${encoded}?width=${width}`;
109
115
  }
110
116
  export function buildImageLoaderUrl(config, args) {
111
117
  // `||` (not `??`): an empty injected base must still fall back to the platform default.
112
118
  const base = (config.cdnBase || IMAGE_CDN_BASE_URL).replace(/\/+$/, '');
113
119
  const { src, width } = args;
120
+ // Storage namespace prepended to local-asset URLs: empty when clean (a custom domain restores it
121
+ // toward storage), `/s/{shopId}` otherwise. Computed once, threaded through both URL builders and
122
+ // the absolute-media anchor so the form is decided in one place.
123
+ const ns = (config.cleanUrl ?? false) ? '' : `/s/${config.shopId}`;
114
124
  // (1) Product image — already an absolute CDN URL. Set the per-srcset width.
115
125
  if (src.startsWith(base)) {
116
126
  try {
@@ -129,9 +139,9 @@ export function buildImageLoaderUrl(config, args) {
129
139
  // `_next/static/media/` but are not images, so the extension gate leaves them to load raw from
130
140
  // the asset CDN (they are not mirrored to the resize CDN).
131
141
  if (config.shopId) {
132
- const mediaSuffix = absoluteNextMediaSuffix(src, config.shopId);
142
+ const mediaSuffix = absoluteNextMediaSuffix(src, ns);
133
143
  if (mediaSuffix !== null && LOADER_IMAGE_EXTENSIONS.test(mediaSuffix)) {
134
- return buildNextMediaCdnUrl(base, config.shopId, mediaSuffix, width);
144
+ return buildNextMediaCdnUrl(base, ns, mediaSuffix, width);
135
145
  }
136
146
  }
137
147
  // (2) Local root-relative image — NOT protocol-relative (`//host`). Two sub-cases:
@@ -149,7 +159,7 @@ export function buildImageLoaderUrl(config, args) {
149
159
  // (2a) Next build-output media image — same image-CDN key as the absolute (assetPrefix) form (1b).
150
160
  const mediaSuffix = nextStaticMediaSuffix(pathname);
151
161
  if (mediaSuffix !== null) {
152
- return buildNextMediaCdnUrl(base, config.shopId, mediaSuffix, width);
162
+ return buildNextMediaCdnUrl(base, ns, mediaSuffix, width);
153
163
  }
154
164
  // Any other framework build output (hashed/immutable, NOT mirrored) → untouched.
155
165
  const buildPrefixes = config.buildAssetPrefixes ?? FRAMEWORK_BUILD_PREFIXES;
@@ -166,7 +176,7 @@ export function buildImageLoaderUrl(config, args) {
166
176
  .split('/')
167
177
  .map(encodeURIComponent)
168
178
  .join('/');
169
- return `${base}/s/${config.shopId}/public/${path}?width=${width}&v=${config.version}`;
179
+ return `${base}${ns}/public/${path}?width=${width}&v=${config.version}`;
170
180
  }
171
181
  // (3) Build assets, protocol-relative/external URLs, data URIs — untouched.
172
182
  return src;
@@ -1 +1 @@
1
- {"version":3,"file":"cart.d.ts","sourceRoot":"","sources":["../../../src/core/operations/cart.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAkcH,eAAO,MAAM,UAAU,QAOrB,CAAC;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,qCAAqC,QAehD,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,+BAA+B,QAO1C,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,QAO/B,CAAC;AAMH,eAAO,MAAM,WAAW,QAYtB,CAAC;AAEH,eAAO,MAAM,cAAc,QAWzB,CAAC;AAEH,eAAO,MAAM,iBAAiB,QAW5B,CAAC;AAEH,eAAO,MAAM,iBAAiB,QAW5B,CAAC;AAEH,eAAO,MAAM,0BAA0B,QAWrC,CAAC;AAEH,eAAO,MAAM,gBAAgB,QAW3B,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,QAWjC,CAAC;AAEH,eAAO,MAAM,0BAA0B,QAWrC,CAAC;AAMH;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,QAWrB,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,QAWnC,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,QAY/B,CAAC;AAMH,eAAO,MAAM,yBAAyB,QAWpC,CAAC;AAEH,eAAO,MAAM,wBAAwB,QAWnC,CAAC;AAEH,eAAO,MAAM,2BAA2B,QAWtC,CAAC;AAEH,eAAO,MAAM,0BAA0B,QAWrC,CAAC;AAEH,eAAO,MAAM,oBAAoB,QAW/B,CAAC;AAEH,eAAO,MAAM,qBAAqB,QAWhC,CAAC;AAEH,eAAO,MAAM,+BAA+B,QAW1C,CAAC;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,aAAa,QAWxB,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,cAAc,QAWzB,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,4BAA4B,QAWvC,CAAC;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,2BAA2B,QAoBtC,CAAC"}
1
+ {"version":3,"file":"cart.d.ts","sourceRoot":"","sources":["../../../src/core/operations/cart.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAmcH,eAAO,MAAM,UAAU,QAOrB,CAAC;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,qCAAqC,QAehD,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,+BAA+B,QAO1C,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,QAO/B,CAAC;AAMH,eAAO,MAAM,WAAW,QAYtB,CAAC;AAEH,eAAO,MAAM,cAAc,QAWzB,CAAC;AAEH,eAAO,MAAM,iBAAiB,QAW5B,CAAC;AAEH,eAAO,MAAM,iBAAiB,QAW5B,CAAC;AAEH,eAAO,MAAM,0BAA0B,QAWrC,CAAC;AAEH,eAAO,MAAM,gBAAgB,QAW3B,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,QAWjC,CAAC;AAEH,eAAO,MAAM,0BAA0B,QAWrC,CAAC;AAMH;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,QAWrB,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,QAWnC,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,QAY/B,CAAC;AAMH,eAAO,MAAM,yBAAyB,QAWpC,CAAC;AAEH,eAAO,MAAM,wBAAwB,QAWnC,CAAC;AAEH,eAAO,MAAM,2BAA2B,QAWtC,CAAC;AAEH,eAAO,MAAM,0BAA0B,QAWrC,CAAC;AAEH,eAAO,MAAM,oBAAoB,QAW/B,CAAC;AAEH,eAAO,MAAM,qBAAqB,QAWhC,CAAC;AAEH,eAAO,MAAM,+BAA+B,QAW1C,CAAC;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,aAAa,QAWxB,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,cAAc,QAWzB,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,4BAA4B,QAWvC,CAAC;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,2BAA2B,QAoBtC,CAAC"}
@@ -277,6 +277,7 @@ const ORDER_FRAGMENT = `
277
277
  expiredAt
278
278
  shippingAddress { ...MailingAddress }
279
279
  itemCount
280
+ customerNote
280
281
  discountAllocations {
281
282
  discountCode
282
283
  amount { ...Money }
@@ -12,10 +12,10 @@
12
12
  * export default createImageLoader();
13
13
  * ```
14
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.
15
+ * Zero configuration: the shop identifier, deployment version, image-CDN base URL, and clean-URL
16
+ * flag are 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
+ * `NEXT_PUBLIC_ASSET_CLEAN_URL`). Pass overrides only for tests or non-standard hosting.
19
19
  *
20
20
  * This entry is Next-specific (it reads `NEXT_PUBLIC_*`); the framework-agnostic core (`.`)
21
21
  * stays free of it.
@@ -1 +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"}
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,CAgBvC"}
@@ -12,10 +12,10 @@
12
12
  * export default createImageLoader();
13
13
  * ```
14
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.
15
+ * Zero configuration: the shop identifier, deployment version, image-CDN base URL, and clean-URL
16
+ * flag are 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
+ * `NEXT_PUBLIC_ASSET_CLEAN_URL`). Pass overrides only for tests or non-standard hosting.
19
19
  *
20
20
  * This entry is Next-specific (it reads `NEXT_PUBLIC_*`); the framework-agnostic core (`.`)
21
21
  * stays free of it.
@@ -39,6 +39,7 @@ export function createImageLoader(config) {
39
39
  shopId: config?.shopId ?? process.env.NEXT_PUBLIC_SHOP_ID ?? '',
40
40
  version: config?.version ?? process.env.NEXT_PUBLIC_DEPLOYMENT_COMMIT ?? '',
41
41
  cdnBase: config?.cdnBase ?? process.env.NEXT_PUBLIC_IMGPROXY_BASE,
42
+ cleanUrl: config?.cleanUrl ?? (process.env.NEXT_PUBLIC_ASSET_CLEAN_URL === 'true'),
42
43
  };
43
44
  // Dev-only hint: surface raster images that the loader can't optimize (an external host, or
44
45
  // a build asset outside `/_next/static/media/`). Only when a shopId is resolved — without one
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@doswiftly/storefront-sdk",
3
- "version": "22.8.1",
3
+ "version": "22.9.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,