@vue-storefront/next 8.0.0-next.3 → 8.0.0-next.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.
@@ -0,0 +1,32 @@
1
+ //#region \0@oxc-project+runtime@0.115.0/helpers/asyncToGenerator.js
2
+ function asyncGeneratorStep(n, t, e, r, o, a, c) {
3
+ try {
4
+ var i = n[a](c), u = i.value;
5
+ } catch (n) {
6
+ e(n);
7
+ return;
8
+ }
9
+ i.done ? t(u) : Promise.resolve(u).then(r, o);
10
+ }
11
+ function _asyncToGenerator(n) {
12
+ return function() {
13
+ var t = this, e = arguments;
14
+ return new Promise(function(r, o) {
15
+ var a = n.apply(t, e);
16
+ function _next(n) {
17
+ asyncGeneratorStep(a, r, o, _next, _throw, "next", n);
18
+ }
19
+ function _throw(n) {
20
+ asyncGeneratorStep(a, r, o, _next, _throw, "throw", n);
21
+ }
22
+ _next(void 0);
23
+ });
24
+ };
25
+ }
26
+ //#endregion
27
+ Object.defineProperty(exports, "_asyncToGenerator", {
28
+ enumerable: true,
29
+ get: function() {
30
+ return _asyncToGenerator;
31
+ }
32
+ });
@@ -0,0 +1,27 @@
1
+ //#region \0@oxc-project+runtime@0.115.0/helpers/asyncToGenerator.js
2
+ function asyncGeneratorStep(n, t, e, r, o, a, c) {
3
+ try {
4
+ var i = n[a](c), u = i.value;
5
+ } catch (n) {
6
+ e(n);
7
+ return;
8
+ }
9
+ i.done ? t(u) : Promise.resolve(u).then(r, o);
10
+ }
11
+ function _asyncToGenerator(n) {
12
+ return function() {
13
+ var t = this, e = arguments;
14
+ return new Promise(function(r, o) {
15
+ var a = n.apply(t, e);
16
+ function _next(n) {
17
+ asyncGeneratorStep(a, r, o, _next, _throw, "next", n);
18
+ }
19
+ function _throw(n) {
20
+ asyncGeneratorStep(a, r, o, _next, _throw, "throw", n);
21
+ }
22
+ _next(void 0);
23
+ });
24
+ };
25
+ }
26
+ //#endregion
27
+ export { _asyncToGenerator as t };
package/dist/client.cjs CHANGED
@@ -206,7 +206,79 @@ function createAlokaiContext() {
206
206
  }, rest);
207
207
  }
208
208
  //#endregion
209
+ //#region src/storefront-events.ts
210
+ /**
211
+ * Creates a typed pub/sub for storefront domain events. The app defines its own event map and creates
212
+ * the bound helpers once; modules (analytics, personalization, search) emit and subscribe without the
213
+ * core knowing who listens. Client-only — the subscriber registry is created per call, so emitting from
214
+ * a server component reaches no listeners.
215
+ *
216
+ * @example
217
+ * ```ts
218
+ * 'use client';
219
+ * import { createStorefrontEvents } from '@vue-storefront/next/client';
220
+ *
221
+ * export interface StorefrontEventMap {
222
+ * 'cart:productAdded': { productId: string };
223
+ * }
224
+ *
225
+ * export const { emitStorefrontEvent, subscribeStorefrontEvent, useStorefrontEvent } =
226
+ * createStorefrontEvents<StorefrontEventMap>();
227
+ * ```
228
+ */
229
+ function createStorefrontEvents() {
230
+ const handlers = /* @__PURE__ */ new Map();
231
+ /**
232
+ * Emit a domain event. No-op when nothing is subscribed.
233
+ */
234
+ function emitStorefrontEvent(type, payload) {
235
+ var _handlers$get;
236
+ (_handlers$get = handlers.get(type)) === null || _handlers$get === void 0 || _handlers$get.forEach((handler) => handler(payload));
237
+ }
238
+ /**
239
+ * Subscribe to a domain event. Returns an unsubscribe function.
240
+ */
241
+ function subscribeStorefrontEvent(type, handler) {
242
+ var _handlers$get2;
243
+ const set = (_handlers$get2 = handlers.get(type)) !== null && _handlers$get2 !== void 0 ? _handlers$get2 : /* @__PURE__ */ new Set();
244
+ set.add(handler);
245
+ handlers.set(type, set);
246
+ return () => set.delete(handler);
247
+ }
248
+ /**
249
+ * Subscribe to a domain event for the lifetime of the calling component. The handler is read through
250
+ * a ref so an inline (unmemoized) handler doesn't re-subscribe on every render.
251
+ */
252
+ function useStorefrontEvent(type, handler) {
253
+ const handlerRef = (0, react.useRef)(handler);
254
+ handlerRef.current = handler;
255
+ (0, react.useEffect)(() => subscribeStorefrontEvent(type, (payload) => handlerRef.current(payload)), [type]);
256
+ }
257
+ /**
258
+ * Emits a domain event once on mount. Lets server-rendered pages (which cannot call the client
259
+ * emitter directly) announce page-level events such as a PDP view. Give it a `key` tied to the entity
260
+ * (e.g. the product id + sku) so it remounts — and re-emits — when navigating between same-route
261
+ * pages. A ref guard keeps React Strict Mode's double-invoked effect (dev) to a single emit per mount.
262
+ */
263
+ function StorefrontEventEmitter({ event, payload }) {
264
+ const didEmit = (0, react.useRef)(false);
265
+ (0, react.useEffect)(() => {
266
+ if (didEmit.current) return;
267
+ didEmit.current = true;
268
+ emitStorefrontEvent(event, payload);
269
+ }, []);
270
+ return null;
271
+ }
272
+ return {
273
+ emitStorefrontEvent,
274
+ StorefrontEventEmitter,
275
+ subscribeStorefrontEvent,
276
+ useStorefrontEvent
277
+ };
278
+ }
279
+ //#endregion
209
280
  exports.__toESM = __toESM;
210
281
  exports.createAlokaiContext = createAlokaiContext;
211
282
  exports.createSfStateProvider = createSfStateProvider;
283
+ exports.createStorefrontEvents = createStorefrontEvents;
212
284
  exports.env = require_env.env;
package/dist/client.d.cts CHANGED
@@ -40,4 +40,41 @@ import { SDKApi } from "@alokai/connect/sdk";
40
40
  */
41
41
  declare function createAlokaiContext<TSdk extends SDKApi<any>, TSfContract extends SfContract>(): CreateSdkContextReturn<TSdk, TSfContract>;
42
42
  //#endregion
43
- export { type CreateSdkContextReturn, Maybe, SfContract, SfState, SfStateProps, createAlokaiContext, createSfStateProvider, env };
43
+ //#region src/storefront-events.d.ts
44
+ /**
45
+ * Handler for a storefront event of type `TType` in the event map `TMap`.
46
+ */
47
+ type StorefrontEventHandler<TMap, TType extends keyof TMap> = (payload: TMap[TType]) => void;
48
+ /**
49
+ * Creates a typed pub/sub for storefront domain events. The app defines its own event map and creates
50
+ * the bound helpers once; modules (analytics, personalization, search) emit and subscribe without the
51
+ * core knowing who listens. Client-only — the subscriber registry is created per call, so emitting from
52
+ * a server component reaches no listeners.
53
+ *
54
+ * @example
55
+ * ```ts
56
+ * 'use client';
57
+ * import { createStorefrontEvents } from '@vue-storefront/next/client';
58
+ *
59
+ * export interface StorefrontEventMap {
60
+ * 'cart:productAdded': { productId: string };
61
+ * }
62
+ *
63
+ * export const { emitStorefrontEvent, subscribeStorefrontEvent, useStorefrontEvent } =
64
+ * createStorefrontEvents<StorefrontEventMap>();
65
+ * ```
66
+ */
67
+ declare function createStorefrontEvents<TMap>(): {
68
+ emitStorefrontEvent: <TType extends keyof TMap>(type: TType, payload: TMap[TType]) => void;
69
+ StorefrontEventEmitter: <TType extends keyof TMap>({
70
+ event,
71
+ payload
72
+ }: {
73
+ event: TType;
74
+ payload: TMap[TType];
75
+ }) => null;
76
+ subscribeStorefrontEvent: <TType extends keyof TMap>(type: TType, handler: StorefrontEventHandler<TMap, TType>) => () => void;
77
+ useStorefrontEvent: <TType extends keyof TMap>(type: TType, handler: StorefrontEventHandler<TMap, TType>) => void;
78
+ };
79
+ //#endregion
80
+ export { type CreateSdkContextReturn, Maybe, SfContract, SfState, SfStateProps, StorefrontEventHandler, createAlokaiContext, createSfStateProvider, createStorefrontEvents, env };
package/dist/client.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { c as SfContract, d as createSfStateProvider, l as SfState, r as CreateSdkContextReturn, s as Maybe, t as env, u as SfStateProps } from "./index-BPhD221E.mjs";
1
+ import { c as SfContract, d as createSfStateProvider, l as SfState, r as CreateSdkContextReturn, s as Maybe, t as env, u as SfStateProps } from "./index-7rf5ab-V.mjs";
2
2
  import { SDKApi } from "@alokai/connect/sdk";
3
3
 
4
4
  //#region src/alokai-provider.d.ts
@@ -40,4 +40,41 @@ import { SDKApi } from "@alokai/connect/sdk";
40
40
  */
41
41
  declare function createAlokaiContext<TSdk extends SDKApi<any>, TSfContract extends SfContract>(): CreateSdkContextReturn<TSdk, TSfContract>;
42
42
  //#endregion
43
- export { type CreateSdkContextReturn, Maybe, SfContract, SfState, SfStateProps, createAlokaiContext, createSfStateProvider, env };
43
+ //#region src/storefront-events.d.ts
44
+ /**
45
+ * Handler for a storefront event of type `TType` in the event map `TMap`.
46
+ */
47
+ type StorefrontEventHandler<TMap, TType extends keyof TMap> = (payload: TMap[TType]) => void;
48
+ /**
49
+ * Creates a typed pub/sub for storefront domain events. The app defines its own event map and creates
50
+ * the bound helpers once; modules (analytics, personalization, search) emit and subscribe without the
51
+ * core knowing who listens. Client-only — the subscriber registry is created per call, so emitting from
52
+ * a server component reaches no listeners.
53
+ *
54
+ * @example
55
+ * ```ts
56
+ * 'use client';
57
+ * import { createStorefrontEvents } from '@vue-storefront/next/client';
58
+ *
59
+ * export interface StorefrontEventMap {
60
+ * 'cart:productAdded': { productId: string };
61
+ * }
62
+ *
63
+ * export const { emitStorefrontEvent, subscribeStorefrontEvent, useStorefrontEvent } =
64
+ * createStorefrontEvents<StorefrontEventMap>();
65
+ * ```
66
+ */
67
+ declare function createStorefrontEvents<TMap>(): {
68
+ emitStorefrontEvent: <TType extends keyof TMap>(type: TType, payload: TMap[TType]) => void;
69
+ StorefrontEventEmitter: <TType extends keyof TMap>({
70
+ event,
71
+ payload
72
+ }: {
73
+ event: TType;
74
+ payload: TMap[TType];
75
+ }) => null;
76
+ subscribeStorefrontEvent: <TType extends keyof TMap>(type: TType, handler: StorefrontEventHandler<TMap, TType>) => () => void;
77
+ useStorefrontEvent: <TType extends keyof TMap>(type: TType, handler: StorefrontEventHandler<TMap, TType>) => void;
78
+ };
79
+ //#endregion
80
+ export { type CreateSdkContextReturn, Maybe, SfContract, SfState, SfStateProps, StorefrontEventHandler, createAlokaiContext, createSfStateProvider, createStorefrontEvents, env };
package/dist/client.mjs CHANGED
@@ -2,7 +2,7 @@
2
2
  import { n as _objectSpread2, r as PUBLIC_ENV_KEY, t as env } from "./env-9UiGUnbx.mjs";
3
3
  import { AlokaiInstrumentation } from "@alokai/instrumentation-next-component";
4
4
  import Script from "next/script";
5
- import React, { createContext, useContext, useRef } from "react";
5
+ import React, { createContext, useContext, useEffect, useRef } from "react";
6
6
  import { createStore, useStore } from "zustand";
7
7
  //#region src/env/get-public-env.ts
8
8
  /**
@@ -181,4 +181,75 @@ function createAlokaiContext() {
181
181
  }, rest);
182
182
  }
183
183
  //#endregion
184
- export { createAlokaiContext, createSfStateProvider, env };
184
+ //#region src/storefront-events.ts
185
+ /**
186
+ * Creates a typed pub/sub for storefront domain events. The app defines its own event map and creates
187
+ * the bound helpers once; modules (analytics, personalization, search) emit and subscribe without the
188
+ * core knowing who listens. Client-only — the subscriber registry is created per call, so emitting from
189
+ * a server component reaches no listeners.
190
+ *
191
+ * @example
192
+ * ```ts
193
+ * 'use client';
194
+ * import { createStorefrontEvents } from '@vue-storefront/next/client';
195
+ *
196
+ * export interface StorefrontEventMap {
197
+ * 'cart:productAdded': { productId: string };
198
+ * }
199
+ *
200
+ * export const { emitStorefrontEvent, subscribeStorefrontEvent, useStorefrontEvent } =
201
+ * createStorefrontEvents<StorefrontEventMap>();
202
+ * ```
203
+ */
204
+ function createStorefrontEvents() {
205
+ const handlers = /* @__PURE__ */ new Map();
206
+ /**
207
+ * Emit a domain event. No-op when nothing is subscribed.
208
+ */
209
+ function emitStorefrontEvent(type, payload) {
210
+ var _handlers$get;
211
+ (_handlers$get = handlers.get(type)) === null || _handlers$get === void 0 || _handlers$get.forEach((handler) => handler(payload));
212
+ }
213
+ /**
214
+ * Subscribe to a domain event. Returns an unsubscribe function.
215
+ */
216
+ function subscribeStorefrontEvent(type, handler) {
217
+ var _handlers$get2;
218
+ const set = (_handlers$get2 = handlers.get(type)) !== null && _handlers$get2 !== void 0 ? _handlers$get2 : /* @__PURE__ */ new Set();
219
+ set.add(handler);
220
+ handlers.set(type, set);
221
+ return () => set.delete(handler);
222
+ }
223
+ /**
224
+ * Subscribe to a domain event for the lifetime of the calling component. The handler is read through
225
+ * a ref so an inline (unmemoized) handler doesn't re-subscribe on every render.
226
+ */
227
+ function useStorefrontEvent(type, handler) {
228
+ const handlerRef = useRef(handler);
229
+ handlerRef.current = handler;
230
+ useEffect(() => subscribeStorefrontEvent(type, (payload) => handlerRef.current(payload)), [type]);
231
+ }
232
+ /**
233
+ * Emits a domain event once on mount. Lets server-rendered pages (which cannot call the client
234
+ * emitter directly) announce page-level events such as a PDP view. Give it a `key` tied to the entity
235
+ * (e.g. the product id + sku) so it remounts — and re-emits — when navigating between same-route
236
+ * pages. A ref guard keeps React Strict Mode's double-invoked effect (dev) to a single emit per mount.
237
+ */
238
+ function StorefrontEventEmitter({ event, payload }) {
239
+ const didEmit = useRef(false);
240
+ useEffect(() => {
241
+ if (didEmit.current) return;
242
+ didEmit.current = true;
243
+ emitStorefrontEvent(event, payload);
244
+ }, []);
245
+ return null;
246
+ }
247
+ return {
248
+ emitStorefrontEvent,
249
+ StorefrontEventEmitter,
250
+ subscribeStorefrontEvent,
251
+ useStorefrontEvent
252
+ };
253
+ }
254
+ //#endregion
255
+ export { createAlokaiContext, createSfStateProvider, createStorefrontEvents, env };
package/dist/index.cjs CHANGED
@@ -1,6 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_env = require("./env-BK6gqfbp.cjs");
3
3
  require("./client.cjs");
4
+ const require_asyncToGenerator = require("./asyncToGenerator-BI_VpA62.cjs");
4
5
  let _alokai_connect_logger = require("@alokai/connect/logger");
5
6
  let _alokai_connect_sdk = require("@alokai/connect/sdk");
6
7
  //#region src/logger/injectMetadata.ts
@@ -82,12 +83,9 @@ function deriveMediaHostEnvName(hostKey) {
82
83
  return `NEXT_PUBLIC_${hostKey.replaceAll("-", "_").toUpperCase()}_MEDIA_HOST`;
83
84
  }
84
85
  const HOST_KEY_PATTERN = /^(?=.*[a-z])[a-z0-9-]+$/;
85
- const ENV_NAME_PATTERN = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
86
86
  function resolveHost(key, config) {
87
- var _config$mediaHostEnvN, _config$variant, _config$buildUpstream, _config$cacheControl, _config$encodePath;
87
+ var _config$variant, _config$buildUpstream, _config$cacheControl, _config$encodePath;
88
88
  if (!HOST_KEY_PATTERN.test(key)) throw new Error(`Invalid image optimizer host key "${key}". Use lowercase letters, digits and hyphens only - the key becomes a URL segment and an env variable name fragment.`);
89
- const mediaHostEnvName = (_config$mediaHostEnvN = config.mediaHostEnvName) !== null && _config$mediaHostEnvN !== void 0 ? _config$mediaHostEnvN : deriveMediaHostEnvName(key);
90
- if (!ENV_NAME_PATTERN.test(mediaHostEnvName)) throw new Error(`Invalid mediaHostEnvName "${mediaHostEnvName}" for host "${key}". Pass the NAME of the env variable (e.g. "NEXT_PUBLIC_CT_MEDIA_HOST"), not its value.`);
91
89
  const variant = variants[(_config$variant = config.variant) !== null && _config$variant !== void 0 ? _config$variant : "default"];
92
90
  return {
93
91
  buildUpstreamUrl: (_config$buildUpstream = config.buildUpstreamUrl) !== null && _config$buildUpstream !== void 0 ? _config$buildUpstream : variant.buildUpstreamUrl,
@@ -95,33 +93,7 @@ function resolveHost(key, config) {
95
93
  encodePath: (_config$encodePath = config.encodePath) !== null && _config$encodePath !== void 0 ? _config$encodePath : variant.encodePath,
96
94
  key,
97
95
  loader: config.loader,
98
- mediaHostEnvName
99
- };
100
- }
101
- //#endregion
102
- //#region \0@oxc-project+runtime@0.115.0/helpers/asyncToGenerator.js
103
- function asyncGeneratorStep(n, t, e, r, o, a, c) {
104
- try {
105
- var i = n[a](c), u = i.value;
106
- } catch (n) {
107
- e(n);
108
- return;
109
- }
110
- i.done ? t(u) : Promise.resolve(u).then(r, o);
111
- }
112
- function _asyncToGenerator(n) {
113
- return function() {
114
- var t = this, e = arguments;
115
- return new Promise(function(r, o) {
116
- var a = n.apply(t, e);
117
- function _next(n) {
118
- asyncGeneratorStep(a, r, o, _next, _throw, "next", n);
119
- }
120
- function _throw(n) {
121
- asyncGeneratorStep(a, r, o, _next, _throw, "throw", n);
122
- }
123
- _next(void 0);
124
- });
96
+ mediaHostEnvName: deriveMediaHostEnvName(key)
125
97
  };
126
98
  }
127
99
  //#endregion
@@ -168,11 +140,15 @@ function createImageOptimizer(config) {
168
140
  "the media host URL, e.g. \"ct\" reads NEXT_PUBLIC_CT_MEDIA_HOST."
169
141
  ].join("\n"));
170
142
  const hosts = resolveHosts(config.hosts);
143
+ const warnedMissingHosts = /* @__PURE__ */ new Set();
171
144
  function loader({ quality, src, width }) {
172
145
  for (const host of hosts) {
173
146
  const mediaHost = require_env.env(host.mediaHostEnvName);
174
147
  if (!mediaHost) {
175
- getLogger().warning(`${host.mediaHostEnvName} is not defined, skipping image optimization for host "${host.key}".`);
148
+ if (!warnedMissingHosts.has(host.key)) {
149
+ warnedMissingHosts.add(host.key);
150
+ getLogger().warning(`${host.mediaHostEnvName} is not defined, skipping image optimization for host "${host.key}".`);
151
+ }
176
152
  continue;
177
153
  }
178
154
  const normalizedHost = mediaHost.replace(/\/$/, "");
@@ -196,7 +172,7 @@ function createImageOptimizer(config) {
196
172
  return _GET.apply(this, arguments);
197
173
  }
198
174
  function _GET() {
199
- _GET = _asyncToGenerator(function* (_request, { params }) {
175
+ _GET = require_asyncToGenerator._asyncToGenerator(function* (_request, { params }) {
200
176
  var _env, _upstream$headers$get;
201
177
  const { host: hostKey, path } = yield params;
202
178
  const host = hosts.find((entry) => entry.key === hostKey);
@@ -206,11 +182,12 @@ function createImageOptimizer(config) {
206
182
  if (!mediaHost) return errorResponse(`${host.mediaHostEnvName} is not defined`, 500);
207
183
  let upstream;
208
184
  try {
209
- upstream = yield fetch(host.buildUpstreamUrl(mediaHost, path));
185
+ upstream = yield fetch(host.buildUpstreamUrl(mediaHost, path), { redirect: "manual" });
210
186
  } catch (error) {
211
187
  getLogger().error(`Failed to fetch upstream image for host "${hostKey}": ${String(error)}`);
212
188
  return errorResponse("Failed to fetch upstream image", 502);
213
189
  }
190
+ if (upstream.status >= 300 && upstream.status < 400) return errorResponse("Upstream redirect is not allowed", 502);
214
191
  return new Response(upstream.body, {
215
192
  headers: {
216
193
  "cache-control": upstream.ok ? host.cacheControl : ERROR_CACHE_CONTROL,
@@ -311,7 +288,7 @@ function normalizeRequestHeaders(requestHeaders) {
311
288
  }
312
289
  function resolveDynamicContext(context) {
313
290
  return require_env._objectSpread2(require_env._objectSpread2({}, context), {}, { getRequestHeaders() {
314
- return _asyncToGenerator(function* () {
291
+ return require_asyncToGenerator._asyncToGenerator(function* () {
315
292
  var _context$getRequestHe;
316
293
  const normalizedHeaders = normalizeRequestHeaders(yield (_context$getRequestHe = context.getRequestHeaders) === null || _context$getRequestHe === void 0 ? void 0 : _context$getRequestHe.call(context));
317
294
  const requestHeaders = Object.fromEntries(Object.entries(normalizedHeaders).filter(([key]) => !BLACKLISTED_HEADERS.has(key)));
package/dist/index.d.cts CHANGED
@@ -35,7 +35,7 @@ type ImageOptimizerHostConfig = ImageOptimizerDelegatedHostConfig | ImageOptimiz
35
35
  * delegated to the given loader and never proxied, so the proxy-side options
36
36
  * do not apply.
37
37
  */
38
- interface ImageOptimizerDelegatedHostConfig extends ImageOptimizerHostBaseConfig {
38
+ interface ImageOptimizerDelegatedHostConfig {
39
39
  buildUpstreamUrl?: never;
40
40
  cacheControl?: never;
41
41
  encodePath?: never;
@@ -50,7 +50,7 @@ interface ImageOptimizerDelegatedHostConfig extends ImageOptimizerHostBaseConfig
50
50
  * Host proxied through `/img-proxy/{key}/...` so the image CDN in front of
51
51
  * the storefront (Alokai Image Optimizer) can transform the response.
52
52
  */
53
- interface ImageOptimizerProxiedHostConfig extends ImageOptimizerHostBaseConfig {
53
+ interface ImageOptimizerProxiedHostConfig {
54
54
  /** Server-side hook overriding the variant's upstream URL reconstruction. */
55
55
  buildUpstreamUrl?: BuildUpstreamUrlHook;
56
56
  /**
@@ -71,15 +71,6 @@ interface ImageOptimizerProxiedHostConfig extends ImageOptimizerHostBaseConfig {
71
71
  */
72
72
  variant?: ImageOptimizerVariant;
73
73
  }
74
- interface ImageOptimizerHostBaseConfig {
75
- /**
76
- * NAME of the env variable holding the media host URL (not the URL
77
- * itself).
78
- *
79
- * @default `NEXT_PUBLIC_{KEY}_MEDIA_HOST` derived from the host key
80
- */
81
- mediaHostEnvName?: string;
82
- }
83
74
  type ImageOptimizerVariant = "default" | "sapcc";
84
75
  /**
85
76
  * Builds the path emitted after `/img-proxy/{key}` on the loader (client)
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as CreateSdkReturn, i as CreateSdkOptions, n as Config, o as InjectedContext, t as env } from "./index-BPhD221E.mjs";
1
+ import { a as CreateSdkReturn, i as CreateSdkOptions, n as Config, o as InjectedContext, t as env } from "./index-7rf5ab-V.mjs";
2
2
  import * as _alokai_connect_logger0 from "@alokai/connect/logger";
3
3
  import { buildModule, defineGetConfigSwitcherHeader } from "@alokai/connect/sdk";
4
4
  import { ImageLoaderProps } from "next/image";
@@ -35,7 +35,7 @@ type ImageOptimizerHostConfig = ImageOptimizerDelegatedHostConfig | ImageOptimiz
35
35
  * delegated to the given loader and never proxied, so the proxy-side options
36
36
  * do not apply.
37
37
  */
38
- interface ImageOptimizerDelegatedHostConfig extends ImageOptimizerHostBaseConfig {
38
+ interface ImageOptimizerDelegatedHostConfig {
39
39
  buildUpstreamUrl?: never;
40
40
  cacheControl?: never;
41
41
  encodePath?: never;
@@ -50,7 +50,7 @@ interface ImageOptimizerDelegatedHostConfig extends ImageOptimizerHostBaseConfig
50
50
  * Host proxied through `/img-proxy/{key}/...` so the image CDN in front of
51
51
  * the storefront (Alokai Image Optimizer) can transform the response.
52
52
  */
53
- interface ImageOptimizerProxiedHostConfig extends ImageOptimizerHostBaseConfig {
53
+ interface ImageOptimizerProxiedHostConfig {
54
54
  /** Server-side hook overriding the variant's upstream URL reconstruction. */
55
55
  buildUpstreamUrl?: BuildUpstreamUrlHook;
56
56
  /**
@@ -71,15 +71,6 @@ interface ImageOptimizerProxiedHostConfig extends ImageOptimizerHostBaseConfig {
71
71
  */
72
72
  variant?: ImageOptimizerVariant;
73
73
  }
74
- interface ImageOptimizerHostBaseConfig {
75
- /**
76
- * NAME of the env variable holding the media host URL (not the URL
77
- * itself).
78
- *
79
- * @default `NEXT_PUBLIC_{KEY}_MEDIA_HOST` derived from the host key
80
- */
81
- mediaHostEnvName?: string;
82
- }
83
74
  type ImageOptimizerVariant = "default" | "sapcc";
84
75
  /**
85
76
  * Builds the path emitted after `/img-proxy/{key}` on the loader (client)
package/dist/index.mjs CHANGED
@@ -1,4 +1,5 @@
1
1
  import { n as _objectSpread2, t as env } from "./env-9UiGUnbx.mjs";
2
+ import { t as _asyncToGenerator } from "./asyncToGenerator-Co5zHbo3.mjs";
2
3
  import { LoggerFactory, LoggerType } from "@alokai/connect/logger";
3
4
  import { buildModule, defineGetConfigSwitcherHeader, initSDK, middlewareModule } from "@alokai/connect/sdk";
4
5
  //#region src/logger/injectMetadata.ts
@@ -80,12 +81,9 @@ function deriveMediaHostEnvName(hostKey) {
80
81
  return `NEXT_PUBLIC_${hostKey.replaceAll("-", "_").toUpperCase()}_MEDIA_HOST`;
81
82
  }
82
83
  const HOST_KEY_PATTERN = /^(?=.*[a-z])[a-z0-9-]+$/;
83
- const ENV_NAME_PATTERN = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
84
84
  function resolveHost(key, config) {
85
- var _config$mediaHostEnvN, _config$variant, _config$buildUpstream, _config$cacheControl, _config$encodePath;
85
+ var _config$variant, _config$buildUpstream, _config$cacheControl, _config$encodePath;
86
86
  if (!HOST_KEY_PATTERN.test(key)) throw new Error(`Invalid image optimizer host key "${key}". Use lowercase letters, digits and hyphens only - the key becomes a URL segment and an env variable name fragment.`);
87
- const mediaHostEnvName = (_config$mediaHostEnvN = config.mediaHostEnvName) !== null && _config$mediaHostEnvN !== void 0 ? _config$mediaHostEnvN : deriveMediaHostEnvName(key);
88
- if (!ENV_NAME_PATTERN.test(mediaHostEnvName)) throw new Error(`Invalid mediaHostEnvName "${mediaHostEnvName}" for host "${key}". Pass the NAME of the env variable (e.g. "NEXT_PUBLIC_CT_MEDIA_HOST"), not its value.`);
89
87
  const variant = variants[(_config$variant = config.variant) !== null && _config$variant !== void 0 ? _config$variant : "default"];
90
88
  return {
91
89
  buildUpstreamUrl: (_config$buildUpstream = config.buildUpstreamUrl) !== null && _config$buildUpstream !== void 0 ? _config$buildUpstream : variant.buildUpstreamUrl,
@@ -93,33 +91,7 @@ function resolveHost(key, config) {
93
91
  encodePath: (_config$encodePath = config.encodePath) !== null && _config$encodePath !== void 0 ? _config$encodePath : variant.encodePath,
94
92
  key,
95
93
  loader: config.loader,
96
- mediaHostEnvName
97
- };
98
- }
99
- //#endregion
100
- //#region \0@oxc-project+runtime@0.115.0/helpers/asyncToGenerator.js
101
- function asyncGeneratorStep(n, t, e, r, o, a, c) {
102
- try {
103
- var i = n[a](c), u = i.value;
104
- } catch (n) {
105
- e(n);
106
- return;
107
- }
108
- i.done ? t(u) : Promise.resolve(u).then(r, o);
109
- }
110
- function _asyncToGenerator(n) {
111
- return function() {
112
- var t = this, e = arguments;
113
- return new Promise(function(r, o) {
114
- var a = n.apply(t, e);
115
- function _next(n) {
116
- asyncGeneratorStep(a, r, o, _next, _throw, "next", n);
117
- }
118
- function _throw(n) {
119
- asyncGeneratorStep(a, r, o, _next, _throw, "throw", n);
120
- }
121
- _next(void 0);
122
- });
94
+ mediaHostEnvName: deriveMediaHostEnvName(key)
123
95
  };
124
96
  }
125
97
  //#endregion
@@ -166,11 +138,15 @@ function createImageOptimizer(config) {
166
138
  "the media host URL, e.g. \"ct\" reads NEXT_PUBLIC_CT_MEDIA_HOST."
167
139
  ].join("\n"));
168
140
  const hosts = resolveHosts(config.hosts);
141
+ const warnedMissingHosts = /* @__PURE__ */ new Set();
169
142
  function loader({ quality, src, width }) {
170
143
  for (const host of hosts) {
171
144
  const mediaHost = env(host.mediaHostEnvName);
172
145
  if (!mediaHost) {
173
- getLogger().warning(`${host.mediaHostEnvName} is not defined, skipping image optimization for host "${host.key}".`);
146
+ if (!warnedMissingHosts.has(host.key)) {
147
+ warnedMissingHosts.add(host.key);
148
+ getLogger().warning(`${host.mediaHostEnvName} is not defined, skipping image optimization for host "${host.key}".`);
149
+ }
174
150
  continue;
175
151
  }
176
152
  const normalizedHost = mediaHost.replace(/\/$/, "");
@@ -204,11 +180,12 @@ function createImageOptimizer(config) {
204
180
  if (!mediaHost) return errorResponse(`${host.mediaHostEnvName} is not defined`, 500);
205
181
  let upstream;
206
182
  try {
207
- upstream = yield fetch(host.buildUpstreamUrl(mediaHost, path));
183
+ upstream = yield fetch(host.buildUpstreamUrl(mediaHost, path), { redirect: "manual" });
208
184
  } catch (error) {
209
185
  getLogger().error(`Failed to fetch upstream image for host "${hostKey}": ${String(error)}`);
210
186
  return errorResponse("Failed to fetch upstream image", 502);
211
187
  }
188
+ if (upstream.status >= 300 && upstream.status < 400) return errorResponse("Upstream redirect is not allowed", 502);
212
189
  return new Response(upstream.body, {
213
190
  headers: {
214
191
  "cache-control": upstream.ok ? host.cacheControl : ERROR_CACHE_CONTROL,
@@ -0,0 +1,23 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ require("./client.cjs");
3
+ const require_asyncToGenerator = require("./asyncToGenerator-BI_VpA62.cjs");
4
+ let next_headers = require("next/headers");
5
+ //#region src/current-path.ts
6
+ /**
7
+ * Returns the current request path (`pathname` + `search`) inside a Server Component, as exposed by
8
+ * {@link createAlokaiMiddleware} on the request headers. Reading it through this helper keeps the
9
+ * underlying header names (`x-pathname` / `x-search`) an implementation detail of the framework.
10
+ */
11
+ function getCurrentPath() {
12
+ return _getCurrentPath.apply(this, arguments);
13
+ }
14
+ function _getCurrentPath() {
15
+ _getCurrentPath = require_asyncToGenerator._asyncToGenerator(function* () {
16
+ var _requestHeaders$get, _requestHeaders$get2;
17
+ const requestHeaders = yield (0, next_headers.headers)();
18
+ return `${(_requestHeaders$get = requestHeaders.get("x-pathname")) !== null && _requestHeaders$get !== void 0 ? _requestHeaders$get : ""}${(_requestHeaders$get2 = requestHeaders.get("x-search")) !== null && _requestHeaders$get2 !== void 0 ? _requestHeaders$get2 : ""}`;
19
+ });
20
+ return _getCurrentPath.apply(this, arguments);
21
+ }
22
+ //#endregion
23
+ exports.getCurrentPath = getCurrentPath;
@@ -0,0 +1,9 @@
1
+ //#region src/current-path.d.ts
2
+ /**
3
+ * Returns the current request path (`pathname` + `search`) inside a Server Component, as exposed by
4
+ * {@link createAlokaiMiddleware} on the request headers. Reading it through this helper keeps the
5
+ * underlying header names (`x-pathname` / `x-search`) an implementation detail of the framework.
6
+ */
7
+ declare function getCurrentPath(): Promise<string>;
8
+ //#endregion
9
+ export { getCurrentPath };
@@ -0,0 +1,9 @@
1
+ //#region src/current-path.d.ts
2
+ /**
3
+ * Returns the current request path (`pathname` + `search`) inside a Server Component, as exposed by
4
+ * {@link createAlokaiMiddleware} on the request headers. Reading it through this helper keeps the
5
+ * underlying header names (`x-pathname` / `x-search`) an implementation detail of the framework.
6
+ */
7
+ declare function getCurrentPath(): Promise<string>;
8
+ //#endregion
9
+ export { getCurrentPath };
@@ -0,0 +1,21 @@
1
+ import { t as _asyncToGenerator } from "./asyncToGenerator-Co5zHbo3.mjs";
2
+ import { headers } from "next/headers";
3
+ //#region src/current-path.ts
4
+ /**
5
+ * Returns the current request path (`pathname` + `search`) inside a Server Component, as exposed by
6
+ * {@link createAlokaiMiddleware} on the request headers. Reading it through this helper keeps the
7
+ * underlying header names (`x-pathname` / `x-search`) an implementation detail of the framework.
8
+ */
9
+ function getCurrentPath() {
10
+ return _getCurrentPath.apply(this, arguments);
11
+ }
12
+ function _getCurrentPath() {
13
+ _getCurrentPath = _asyncToGenerator(function* () {
14
+ var _requestHeaders$get, _requestHeaders$get2;
15
+ const requestHeaders = yield headers();
16
+ return `${(_requestHeaders$get = requestHeaders.get("x-pathname")) !== null && _requestHeaders$get !== void 0 ? _requestHeaders$get : ""}${(_requestHeaders$get2 = requestHeaders.get("x-search")) !== null && _requestHeaders$get2 !== void 0 ? _requestHeaders$get2 : ""}`;
17
+ });
18
+ return _getCurrentPath.apply(this, arguments);
19
+ }
20
+ //#endregion
21
+ export { getCurrentPath };
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/vuestorefront/enterprise.git"
8
8
  },
9
- "version": "8.0.0-next.3",
9
+ "version": "8.0.0-next.6",
10
10
  "exports": {
11
11
  ".": {
12
12
  "types": "./dist/index.d.mts",
@@ -17,6 +17,11 @@
17
17
  "types": "./dist/client.d.mts",
18
18
  "import": "./dist/client.mjs",
19
19
  "require": "./dist/client.cjs"
20
+ },
21
+ "./server": {
22
+ "types": "./dist/server.d.mts",
23
+ "import": "./dist/server.mjs",
24
+ "require": "./dist/server.cjs"
20
25
  }
21
26
  },
22
27
  "files": [
@@ -35,7 +40,7 @@
35
40
  "zustand": "^4.5.4"
36
41
  },
37
42
  "devDependencies": {
38
- "@alokai/connect": "2.3.0-next.7",
43
+ "@alokai/connect": "2.3.0-next.10",
39
44
  "@shared/typescript-config": "1.0.0",
40
45
  "@types/react": "^19",
41
46
  "@types/react-dom": "^19",
@@ -50,7 +55,7 @@
50
55
  "vitest": "4.0.18"
51
56
  },
52
57
  "peerDependencies": {
53
- "@alokai/connect": "2.3.0-next.7",
58
+ "@alokai/connect": "2.3.0-next.10",
54
59
  "next": "^16.0.0",
55
60
  "react": "^19.0.0"
56
61
  },