@vue-storefront/next 7.0.2 → 8.0.0-next.4
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/dist/asyncToGenerator-BI_VpA62.cjs +32 -0
- package/dist/asyncToGenerator-Co5zHbo3.mjs +27 -0
- package/dist/client.cjs +72 -0
- package/dist/client.d.cts +38 -1
- package/dist/client.d.mts +39 -2
- package/dist/client.mjs +73 -2
- package/dist/index.cjs +204 -27
- package/dist/index.d.cts +121 -1
- package/dist/index.d.mts +122 -2
- package/dist/index.mjs +203 -27
- package/dist/server.cjs +23 -0
- package/dist/server.d.cts +9 -0
- package/dist/server.d.mts +9 -0
- package/dist/server.mjs +21 -0
- package/package.json +10 -6
- /package/dist/{index-BPhD221E.d.mts → index-7rf5ab-V.d.mts} +0 -0
|
@@ -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
|
-
|
|
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-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
@@ -20,6 +21,207 @@ const createLogger = (options) => {
|
|
|
20
21
|
return injectMetadata(_alokai_connect_logger.LoggerFactory.create(_alokai_connect_logger.LoggerType.ConsolaGcp, options), { alokai: { context: "storefront" } });
|
|
21
22
|
};
|
|
22
23
|
//#endregion
|
|
24
|
+
//#region src/image-optimizer/constants.ts
|
|
25
|
+
/**
|
|
26
|
+
* URL prefix the loader emits and the route folder must use:
|
|
27
|
+
* `app/img-proxy/[host]/[...path]/route.ts`.
|
|
28
|
+
*/
|
|
29
|
+
const IMG_PROXY_PREFIX = "/img-proxy";
|
|
30
|
+
/**
|
|
31
|
+
* Cache-Control applied to successful (2xx) upstream responses unless
|
|
32
|
+
* overridden per host via `cacheControl`.
|
|
33
|
+
*/
|
|
34
|
+
const DEFAULT_CACHE_CONTROL = "public, max-age=3600, s-maxage=86400";
|
|
35
|
+
/**
|
|
36
|
+
* Cache-Control applied to every non-2xx or failed response so errors are
|
|
37
|
+
* never cached by the browser or the CDN.
|
|
38
|
+
*/
|
|
39
|
+
const ERROR_CACHE_CONTROL = "no-store, no-cache, must-revalidate";
|
|
40
|
+
const variants = {
|
|
41
|
+
default: {
|
|
42
|
+
buildUpstreamUrl: (mediaHost, segments) => `${mediaHost}/${joinEncoded(segments)}`,
|
|
43
|
+
encodePath: (pathname) => pathname === "" ? void 0 : pathname
|
|
44
|
+
},
|
|
45
|
+
sapcc: {
|
|
46
|
+
buildUpstreamUrl: (mediaHost, segments) => {
|
|
47
|
+
const [context = "", ...path] = segments;
|
|
48
|
+
return `${mediaHost}/${joinEncoded(path)}?context=${encodeURIComponent(context)}`;
|
|
49
|
+
},
|
|
50
|
+
encodePath: (pathname, searchParams) => {
|
|
51
|
+
var _pathname$split$pop;
|
|
52
|
+
const context = searchParams.get("context");
|
|
53
|
+
if (!context) return;
|
|
54
|
+
const hasExtension = ((_pathname$split$pop = pathname.split("/").pop()) !== null && _pathname$split$pop !== void 0 ? _pathname$split$pop : "").includes(".");
|
|
55
|
+
const separator = pathname.endsWith("/") ? "" : "/";
|
|
56
|
+
const safePathname = hasExtension ? pathname : `${pathname}${separator}image.png`;
|
|
57
|
+
return `/${encodeURIComponent(context)}${safePathname}`;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
function joinEncoded(segments) {
|
|
62
|
+
return segments.map((segment) => encodeURIComponent(segment)).join("/");
|
|
63
|
+
}
|
|
64
|
+
//#endregion
|
|
65
|
+
//#region src/image-optimizer/resolve-hosts.ts
|
|
66
|
+
/**
|
|
67
|
+
* Normalizes the `hosts` config into an ordered list of fully-resolved host
|
|
68
|
+
* configs. Throws on misconfiguration so mistakes surface at module init
|
|
69
|
+
* instead of as silently unoptimized images.
|
|
70
|
+
*
|
|
71
|
+
* @internal
|
|
72
|
+
*/
|
|
73
|
+
function resolveHosts(hosts) {
|
|
74
|
+
return Object.entries(hosts).map(([key, config]) => resolveHost(key, config));
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Derives the conventional env variable name from a host key, e.g.
|
|
78
|
+
* `ct` -> `NEXT_PUBLIC_CT_MEDIA_HOST`, `my-cms` -> `NEXT_PUBLIC_MY_CMS_MEDIA_HOST`.
|
|
79
|
+
*
|
|
80
|
+
* @internal
|
|
81
|
+
*/
|
|
82
|
+
function deriveMediaHostEnvName(hostKey) {
|
|
83
|
+
return `NEXT_PUBLIC_${hostKey.replaceAll("-", "_").toUpperCase()}_MEDIA_HOST`;
|
|
84
|
+
}
|
|
85
|
+
const HOST_KEY_PATTERN = /^(?=.*[a-z])[a-z0-9-]+$/;
|
|
86
|
+
function resolveHost(key, config) {
|
|
87
|
+
var _config$variant, _config$buildUpstream, _config$cacheControl, _config$encodePath;
|
|
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 variant = variants[(_config$variant = config.variant) !== null && _config$variant !== void 0 ? _config$variant : "default"];
|
|
90
|
+
return {
|
|
91
|
+
buildUpstreamUrl: (_config$buildUpstream = config.buildUpstreamUrl) !== null && _config$buildUpstream !== void 0 ? _config$buildUpstream : variant.buildUpstreamUrl,
|
|
92
|
+
cacheControl: (_config$cacheControl = config.cacheControl) !== null && _config$cacheControl !== void 0 ? _config$cacheControl : DEFAULT_CACHE_CONTROL,
|
|
93
|
+
encodePath: (_config$encodePath = config.encodePath) !== null && _config$encodePath !== void 0 ? _config$encodePath : variant.encodePath,
|
|
94
|
+
key,
|
|
95
|
+
loader: config.loader,
|
|
96
|
+
mediaHostEnvName: deriveMediaHostEnvName(key)
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
//#endregion
|
|
100
|
+
//#region src/image-optimizer/create-image-optimizer.ts
|
|
101
|
+
/**
|
|
102
|
+
* Creates a Next.js custom image `loader` and a proxy route-handler `GET`
|
|
103
|
+
* that together let an image CDN in front of the storefront (the Alokai
|
|
104
|
+
* Image Optimizer)
|
|
105
|
+
* optimize media-host images.
|
|
106
|
+
*
|
|
107
|
+
* The loader rewrites `src` values matching a configured media host to
|
|
108
|
+
* `/img-proxy/{key}/{path}?width=&quality=&format=auto`; the route handler
|
|
109
|
+
* proxies that path back to the media host and streams the body so the CDN
|
|
110
|
+
* can transform it on the way to the browser. Non-matching `src` values are
|
|
111
|
+
* returned unchanged.
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* ```ts
|
|
115
|
+
* // config/image-optimizer.ts - the single place the config exists; the
|
|
116
|
+
* // default export lets `images.loaderFile` point straight at this file
|
|
117
|
+
* import { createImageOptimizer } from "@vue-storefront/next";
|
|
118
|
+
*
|
|
119
|
+
* export const { GET, loader } = createImageOptimizer({ hosts: { ct: {} } });
|
|
120
|
+
*
|
|
121
|
+
* export default loader;
|
|
122
|
+
*
|
|
123
|
+
* // app/img-proxy/[host]/[...path]/route.ts
|
|
124
|
+
* export { GET } from "@/config/image-optimizer";
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
function createImageOptimizer(config) {
|
|
128
|
+
if (Object.keys(config.hosts).length === 0) throw new Error([
|
|
129
|
+
"createImageOptimizer requires at least one host in \"hosts\".",
|
|
130
|
+
"Add the media hosts whose images the loader should optimize, e.g.:",
|
|
131
|
+
"",
|
|
132
|
+
" createImageOptimizer({",
|
|
133
|
+
" hosts: {",
|
|
134
|
+
" ct: {},",
|
|
135
|
+
" sapcc: { variant: \"sapcc\" },",
|
|
136
|
+
" },",
|
|
137
|
+
" });",
|
|
138
|
+
"",
|
|
139
|
+
"Each key becomes a URL segment and derives the env variable holding",
|
|
140
|
+
"the media host URL, e.g. \"ct\" reads NEXT_PUBLIC_CT_MEDIA_HOST."
|
|
141
|
+
].join("\n"));
|
|
142
|
+
const hosts = resolveHosts(config.hosts);
|
|
143
|
+
const warnedMissingHosts = /* @__PURE__ */ new Set();
|
|
144
|
+
function loader({ quality, src, width }) {
|
|
145
|
+
for (const host of hosts) {
|
|
146
|
+
const mediaHost = require_env.env(host.mediaHostEnvName);
|
|
147
|
+
if (!mediaHost) {
|
|
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
|
+
}
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
const normalizedHost = mediaHost.replace(/\/$/, "");
|
|
155
|
+
if (!matchesMediaHost(src, normalizedHost)) continue;
|
|
156
|
+
if (host.loader) return host.loader({
|
|
157
|
+
quality,
|
|
158
|
+
src,
|
|
159
|
+
width
|
|
160
|
+
});
|
|
161
|
+
const localPath = src.slice(normalizedHost.length);
|
|
162
|
+
const queryIndex = localPath.indexOf("?");
|
|
163
|
+
const pathname = queryIndex === -1 ? localPath : localPath.slice(0, queryIndex);
|
|
164
|
+
const queryString = queryIndex === -1 ? "" : localPath.slice(queryIndex + 1);
|
|
165
|
+
const proxyPath = host.encodePath(pathname, new URLSearchParams(queryString));
|
|
166
|
+
if (proxyPath === void 0) return src;
|
|
167
|
+
return `${IMG_PROXY_PREFIX}/${host.key}${proxyPath}?width=${width}&quality=${quality || 85}&format=auto`;
|
|
168
|
+
}
|
|
169
|
+
return src;
|
|
170
|
+
}
|
|
171
|
+
function GET(_x, _x2) {
|
|
172
|
+
return _GET.apply(this, arguments);
|
|
173
|
+
}
|
|
174
|
+
function _GET() {
|
|
175
|
+
_GET = require_asyncToGenerator._asyncToGenerator(function* (_request, { params }) {
|
|
176
|
+
var _env, _upstream$headers$get;
|
|
177
|
+
const { host: hostKey, path } = yield params;
|
|
178
|
+
const host = hosts.find((entry) => entry.key === hostKey);
|
|
179
|
+
if (!host) return errorResponse(`Unknown image proxy host "${hostKey}"`, 404);
|
|
180
|
+
if (path.length === 0 || path.some(isUnsafeSegment)) return errorResponse("Invalid image path", 400);
|
|
181
|
+
const mediaHost = (_env = require_env.env(host.mediaHostEnvName)) === null || _env === void 0 ? void 0 : _env.replace(/\/$/, "");
|
|
182
|
+
if (!mediaHost) return errorResponse(`${host.mediaHostEnvName} is not defined`, 500);
|
|
183
|
+
let upstream;
|
|
184
|
+
try {
|
|
185
|
+
upstream = yield fetch(host.buildUpstreamUrl(mediaHost, path), { redirect: "manual" });
|
|
186
|
+
} catch (error) {
|
|
187
|
+
getLogger().error(`Failed to fetch upstream image for host "${hostKey}": ${String(error)}`);
|
|
188
|
+
return errorResponse("Failed to fetch upstream image", 502);
|
|
189
|
+
}
|
|
190
|
+
if (upstream.status >= 300 && upstream.status < 400) return errorResponse("Upstream redirect is not allowed", 502);
|
|
191
|
+
return new Response(upstream.body, {
|
|
192
|
+
headers: {
|
|
193
|
+
"cache-control": upstream.ok ? host.cacheControl : ERROR_CACHE_CONTROL,
|
|
194
|
+
"content-type": (_upstream$headers$get = upstream.headers.get("content-type")) !== null && _upstream$headers$get !== void 0 ? _upstream$headers$get : "application/octet-stream"
|
|
195
|
+
},
|
|
196
|
+
status: upstream.status
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
return _GET.apply(this, arguments);
|
|
200
|
+
}
|
|
201
|
+
return {
|
|
202
|
+
GET,
|
|
203
|
+
loader
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
let logger;
|
|
207
|
+
function getLogger() {
|
|
208
|
+
var _logger;
|
|
209
|
+
(_logger = logger) !== null && _logger !== void 0 || (logger = createLogger());
|
|
210
|
+
return logger;
|
|
211
|
+
}
|
|
212
|
+
function matchesMediaHost(src, normalizedHost) {
|
|
213
|
+
return src === normalizedHost || src.startsWith(`${normalizedHost}/`) || src.startsWith(`${normalizedHost}?`);
|
|
214
|
+
}
|
|
215
|
+
function isUnsafeSegment(segment) {
|
|
216
|
+
return segment === "" || segment === "." || segment === "..";
|
|
217
|
+
}
|
|
218
|
+
function errorResponse(message, status) {
|
|
219
|
+
return new Response(message, {
|
|
220
|
+
headers: { "cache-control": ERROR_CACHE_CONTROL },
|
|
221
|
+
status
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
//#endregion
|
|
23
225
|
//#region src/middleware.ts
|
|
24
226
|
/**
|
|
25
227
|
* Creates an Alokai middleware wrapper that adds pathname information to request headers.
|
|
@@ -64,32 +266,6 @@ const defaultMethodsRequestConfig = {
|
|
|
64
266
|
} }
|
|
65
267
|
};
|
|
66
268
|
//#endregion
|
|
67
|
-
//#region \0@oxc-project+runtime@0.115.0/helpers/asyncToGenerator.js
|
|
68
|
-
function asyncGeneratorStep(n, t, e, r, o, a, c) {
|
|
69
|
-
try {
|
|
70
|
-
var i = n[a](c), u = i.value;
|
|
71
|
-
} catch (n) {
|
|
72
|
-
e(n);
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
i.done ? t(u) : Promise.resolve(u).then(r, o);
|
|
76
|
-
}
|
|
77
|
-
function _asyncToGenerator(n) {
|
|
78
|
-
return function() {
|
|
79
|
-
var t = this, e = arguments;
|
|
80
|
-
return new Promise(function(r, o) {
|
|
81
|
-
var a = n.apply(t, e);
|
|
82
|
-
function _next(n) {
|
|
83
|
-
asyncGeneratorStep(a, r, o, _next, _throw, "next", n);
|
|
84
|
-
}
|
|
85
|
-
function _throw(n) {
|
|
86
|
-
asyncGeneratorStep(a, r, o, _next, _throw, "throw", n);
|
|
87
|
-
}
|
|
88
|
-
_next(void 0);
|
|
89
|
-
});
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
//#endregion
|
|
93
269
|
//#region src/sdk/helpers/resolveDynamicContext.ts
|
|
94
270
|
const BLACKLISTED_HEADERS = new Set(["host"]);
|
|
95
271
|
function isAppRouterHeaders(headers) {
|
|
@@ -112,7 +288,7 @@ function normalizeRequestHeaders(requestHeaders) {
|
|
|
112
288
|
}
|
|
113
289
|
function resolveDynamicContext(context) {
|
|
114
290
|
return require_env._objectSpread2(require_env._objectSpread2({}, context), {}, { getRequestHeaders() {
|
|
115
|
-
return _asyncToGenerator(function* () {
|
|
291
|
+
return require_asyncToGenerator._asyncToGenerator(function* () {
|
|
116
292
|
var _context$getRequestHe;
|
|
117
293
|
const normalizedHeaders = normalizeRequestHeaders(yield (_context$getRequestHe = context.getRequestHeaders) === null || _context$getRequestHe === void 0 ? void 0 : _context$getRequestHe.call(context));
|
|
118
294
|
const requestHeaders = Object.fromEntries(Object.entries(normalizedHeaders).filter(([key]) => !BLACKLISTED_HEADERS.has(key)));
|
|
@@ -237,6 +413,7 @@ function buildModules(modules) {
|
|
|
237
413
|
}
|
|
238
414
|
//#endregion
|
|
239
415
|
exports.createAlokaiMiddleware = createAlokaiMiddleware;
|
|
416
|
+
exports.createImageOptimizer = createImageOptimizer;
|
|
240
417
|
exports.createLogger = createLogger;
|
|
241
418
|
exports.createSdk = createSdk;
|
|
242
419
|
Object.defineProperty(exports, "defineGetConfigSwitcherHeader", {
|
package/dist/index.d.cts
CHANGED
|
@@ -1,8 +1,128 @@
|
|
|
1
1
|
import { a as CreateSdkReturn, i as CreateSdkOptions, n as Config, o as InjectedContext, t as env } from "./index-w0GzdXwC.cjs";
|
|
2
2
|
import { buildModule, defineGetConfigSwitcherHeader } from "@alokai/connect/sdk";
|
|
3
|
+
import { ImageLoaderProps } from "next/image";
|
|
3
4
|
import * as _alokai_connect_logger0 from "@alokai/connect/logger";
|
|
4
5
|
import { NextRequest, NextResponse } from "next/server";
|
|
5
6
|
|
|
7
|
+
//#region src/image-optimizer/types.d.ts
|
|
8
|
+
/**
|
|
9
|
+
* Next.js custom image loader produced by `createImageOptimizer`. Pass it as
|
|
10
|
+
* the default export of the file referenced by `images.loaderFile`.
|
|
11
|
+
*/
|
|
12
|
+
type ImageOptimizerLoader = (props: ImageLoaderProps) => string;
|
|
13
|
+
/**
|
|
14
|
+
* Route handler produced by `createImageOptimizer`. Re-export it as `GET`
|
|
15
|
+
* from `app/img-proxy/[host]/[...path]/route.ts`.
|
|
16
|
+
*/
|
|
17
|
+
type ImageOptimizerRouteHandler = (request: Request, context: ImageOptimizerRouteContext) => Promise<Response>;
|
|
18
|
+
interface ImageOptimizer {
|
|
19
|
+
GET: ImageOptimizerRouteHandler;
|
|
20
|
+
loader: ImageOptimizerLoader;
|
|
21
|
+
}
|
|
22
|
+
interface CreateImageOptimizerConfig {
|
|
23
|
+
/**
|
|
24
|
+
* Media hosts whose images should be optimized, keyed by host key. An
|
|
25
|
+
* empty object means all defaults. The key becomes the `[host]` URL
|
|
26
|
+
* segment and derives the default env variable name:
|
|
27
|
+
* `NEXT_PUBLIC_{KEY}_MEDIA_HOST`.
|
|
28
|
+
*/
|
|
29
|
+
hosts: ImageOptimizerHosts;
|
|
30
|
+
}
|
|
31
|
+
type ImageOptimizerHosts = Record<string, ImageOptimizerHostConfig>;
|
|
32
|
+
type ImageOptimizerHostConfig = ImageOptimizerDelegatedHostConfig | ImageOptimizerProxiedHostConfig;
|
|
33
|
+
/**
|
|
34
|
+
* Host with its own image CDN (e.g. Contentful): matching `src` values are
|
|
35
|
+
* delegated to the given loader and never proxied, so the proxy-side options
|
|
36
|
+
* do not apply.
|
|
37
|
+
*/
|
|
38
|
+
interface ImageOptimizerDelegatedHostConfig {
|
|
39
|
+
buildUpstreamUrl?: never;
|
|
40
|
+
cacheControl?: never;
|
|
41
|
+
encodePath?: never;
|
|
42
|
+
/**
|
|
43
|
+
* Rewrites a matching `src` directly to the host's own CDN. Same signature
|
|
44
|
+
* as a Next.js image loader, so existing loader implementations plug in.
|
|
45
|
+
*/
|
|
46
|
+
loader: ImageOptimizerLoader;
|
|
47
|
+
variant?: never;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Host proxied through `/img-proxy/{key}/...` so the image CDN in front of
|
|
51
|
+
* the storefront (Alokai Image Optimizer) can transform the response.
|
|
52
|
+
*/
|
|
53
|
+
interface ImageOptimizerProxiedHostConfig {
|
|
54
|
+
/** Server-side hook overriding the variant's upstream URL reconstruction. */
|
|
55
|
+
buildUpstreamUrl?: BuildUpstreamUrlHook;
|
|
56
|
+
/**
|
|
57
|
+
* Cache-Control for successful (2xx) upstream responses.
|
|
58
|
+
*
|
|
59
|
+
* @default "public, max-age=3600, s-maxage=86400"
|
|
60
|
+
*/
|
|
61
|
+
cacheControl?: string;
|
|
62
|
+
/** Client-side hook overriding the variant's path encoding. */
|
|
63
|
+
encodePath?: EncodePathHook;
|
|
64
|
+
loader?: never;
|
|
65
|
+
/**
|
|
66
|
+
* Built-in URL scheme. `sapcc` encodes the `?context=` query parameter as
|
|
67
|
+
* a path segment and appends a synthetic filename when the path has none.
|
|
68
|
+
* Ignored for a side when the corresponding hook is provided.
|
|
69
|
+
*
|
|
70
|
+
* @default "default"
|
|
71
|
+
*/
|
|
72
|
+
variant?: ImageOptimizerVariant;
|
|
73
|
+
}
|
|
74
|
+
type ImageOptimizerVariant = "default" | "sapcc";
|
|
75
|
+
/**
|
|
76
|
+
* Builds the path emitted after `/img-proxy/{key}` on the loader (client)
|
|
77
|
+
* side. Receives the pathname relative to the media host (with leading
|
|
78
|
+
* slash, without query string) and the parsed query string of the original
|
|
79
|
+
* `src`. Return the proxy path (starting with `/`), or `undefined` to skip
|
|
80
|
+
* proxying and return the original `src` unchanged.
|
|
81
|
+
*/
|
|
82
|
+
type EncodePathHook = (pathname: string, searchParams: URLSearchParams) => string | undefined;
|
|
83
|
+
/**
|
|
84
|
+
* Rebuilds the upstream URL on the route-handler (server) side. Receives the
|
|
85
|
+
* media host (without trailing slash) and the decoded `[...path]` segments
|
|
86
|
+
* from the route params. Must return an absolute URL.
|
|
87
|
+
*/
|
|
88
|
+
type BuildUpstreamUrlHook = (mediaHost: string, segments: string[]) => string;
|
|
89
|
+
interface ImageOptimizerRouteContext {
|
|
90
|
+
params: Promise<ImageOptimizerRouteParams>;
|
|
91
|
+
}
|
|
92
|
+
interface ImageOptimizerRouteParams {
|
|
93
|
+
host: string;
|
|
94
|
+
path: string[];
|
|
95
|
+
}
|
|
96
|
+
//#endregion
|
|
97
|
+
//#region src/image-optimizer/create-image-optimizer.d.ts
|
|
98
|
+
/**
|
|
99
|
+
* Creates a Next.js custom image `loader` and a proxy route-handler `GET`
|
|
100
|
+
* that together let an image CDN in front of the storefront (the Alokai
|
|
101
|
+
* Image Optimizer)
|
|
102
|
+
* optimize media-host images.
|
|
103
|
+
*
|
|
104
|
+
* The loader rewrites `src` values matching a configured media host to
|
|
105
|
+
* `/img-proxy/{key}/{path}?width=&quality=&format=auto`; the route handler
|
|
106
|
+
* proxies that path back to the media host and streams the body so the CDN
|
|
107
|
+
* can transform it on the way to the browser. Non-matching `src` values are
|
|
108
|
+
* returned unchanged.
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* ```ts
|
|
112
|
+
* // config/image-optimizer.ts - the single place the config exists; the
|
|
113
|
+
* // default export lets `images.loaderFile` point straight at this file
|
|
114
|
+
* import { createImageOptimizer } from "@vue-storefront/next";
|
|
115
|
+
*
|
|
116
|
+
* export const { GET, loader } = createImageOptimizer({ hosts: { ct: {} } });
|
|
117
|
+
*
|
|
118
|
+
* export default loader;
|
|
119
|
+
*
|
|
120
|
+
* // app/img-proxy/[host]/[...path]/route.ts
|
|
121
|
+
* export { GET } from "@/config/image-optimizer";
|
|
122
|
+
* ```
|
|
123
|
+
*/
|
|
124
|
+
declare function createImageOptimizer(config: CreateImageOptimizerConfig): ImageOptimizer;
|
|
125
|
+
//#endregion
|
|
6
126
|
//#region src/logger/types.d.ts
|
|
7
127
|
type LogVerbosity = "alert" | "critical" | "debug" | "emergency" | "error" | "info" | "notice" | "warning";
|
|
8
128
|
type LoggerOptions = Partial<{
|
|
@@ -176,4 +296,4 @@ type DefineSdkModule = (context: InjectedContext) => ReturnType<typeof buildModu
|
|
|
176
296
|
*/
|
|
177
297
|
declare function defineSdkModule<TModuleDefinition extends DefineSdkModule>(moduleDefinition: TModuleDefinition): TModuleDefinition;
|
|
178
298
|
//#endregion
|
|
179
|
-
export { type CreateSdkOptions, createAlokaiMiddleware, createLogger, createSdk, defineGetConfigSwitcherHeader, defineSdkConfig, defineSdkModule, env, resolveSdkOptions };
|
|
299
|
+
export { BuildUpstreamUrlHook, CreateImageOptimizerConfig, type CreateSdkOptions, EncodePathHook, ImageOptimizer, ImageOptimizerDelegatedHostConfig, ImageOptimizerHostConfig, ImageOptimizerHosts, ImageOptimizerLoader, ImageOptimizerProxiedHostConfig, ImageOptimizerRouteContext, ImageOptimizerRouteHandler, ImageOptimizerRouteParams, ImageOptimizerVariant, createAlokaiMiddleware, createImageOptimizer, createLogger, createSdk, defineGetConfigSwitcherHeader, defineSdkConfig, defineSdkModule, env, resolveSdkOptions };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,8 +1,128 @@
|
|
|
1
|
-
import { a as CreateSdkReturn, i as CreateSdkOptions, n as Config, o as InjectedContext, t as env } from "./index-
|
|
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
|
+
import { ImageLoaderProps } from "next/image";
|
|
4
5
|
import { NextRequest, NextResponse } from "next/server";
|
|
5
6
|
|
|
7
|
+
//#region src/image-optimizer/types.d.ts
|
|
8
|
+
/**
|
|
9
|
+
* Next.js custom image loader produced by `createImageOptimizer`. Pass it as
|
|
10
|
+
* the default export of the file referenced by `images.loaderFile`.
|
|
11
|
+
*/
|
|
12
|
+
type ImageOptimizerLoader = (props: ImageLoaderProps) => string;
|
|
13
|
+
/**
|
|
14
|
+
* Route handler produced by `createImageOptimizer`. Re-export it as `GET`
|
|
15
|
+
* from `app/img-proxy/[host]/[...path]/route.ts`.
|
|
16
|
+
*/
|
|
17
|
+
type ImageOptimizerRouteHandler = (request: Request, context: ImageOptimizerRouteContext) => Promise<Response>;
|
|
18
|
+
interface ImageOptimizer {
|
|
19
|
+
GET: ImageOptimizerRouteHandler;
|
|
20
|
+
loader: ImageOptimizerLoader;
|
|
21
|
+
}
|
|
22
|
+
interface CreateImageOptimizerConfig {
|
|
23
|
+
/**
|
|
24
|
+
* Media hosts whose images should be optimized, keyed by host key. An
|
|
25
|
+
* empty object means all defaults. The key becomes the `[host]` URL
|
|
26
|
+
* segment and derives the default env variable name:
|
|
27
|
+
* `NEXT_PUBLIC_{KEY}_MEDIA_HOST`.
|
|
28
|
+
*/
|
|
29
|
+
hosts: ImageOptimizerHosts;
|
|
30
|
+
}
|
|
31
|
+
type ImageOptimizerHosts = Record<string, ImageOptimizerHostConfig>;
|
|
32
|
+
type ImageOptimizerHostConfig = ImageOptimizerDelegatedHostConfig | ImageOptimizerProxiedHostConfig;
|
|
33
|
+
/**
|
|
34
|
+
* Host with its own image CDN (e.g. Contentful): matching `src` values are
|
|
35
|
+
* delegated to the given loader and never proxied, so the proxy-side options
|
|
36
|
+
* do not apply.
|
|
37
|
+
*/
|
|
38
|
+
interface ImageOptimizerDelegatedHostConfig {
|
|
39
|
+
buildUpstreamUrl?: never;
|
|
40
|
+
cacheControl?: never;
|
|
41
|
+
encodePath?: never;
|
|
42
|
+
/**
|
|
43
|
+
* Rewrites a matching `src` directly to the host's own CDN. Same signature
|
|
44
|
+
* as a Next.js image loader, so existing loader implementations plug in.
|
|
45
|
+
*/
|
|
46
|
+
loader: ImageOptimizerLoader;
|
|
47
|
+
variant?: never;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Host proxied through `/img-proxy/{key}/...` so the image CDN in front of
|
|
51
|
+
* the storefront (Alokai Image Optimizer) can transform the response.
|
|
52
|
+
*/
|
|
53
|
+
interface ImageOptimizerProxiedHostConfig {
|
|
54
|
+
/** Server-side hook overriding the variant's upstream URL reconstruction. */
|
|
55
|
+
buildUpstreamUrl?: BuildUpstreamUrlHook;
|
|
56
|
+
/**
|
|
57
|
+
* Cache-Control for successful (2xx) upstream responses.
|
|
58
|
+
*
|
|
59
|
+
* @default "public, max-age=3600, s-maxage=86400"
|
|
60
|
+
*/
|
|
61
|
+
cacheControl?: string;
|
|
62
|
+
/** Client-side hook overriding the variant's path encoding. */
|
|
63
|
+
encodePath?: EncodePathHook;
|
|
64
|
+
loader?: never;
|
|
65
|
+
/**
|
|
66
|
+
* Built-in URL scheme. `sapcc` encodes the `?context=` query parameter as
|
|
67
|
+
* a path segment and appends a synthetic filename when the path has none.
|
|
68
|
+
* Ignored for a side when the corresponding hook is provided.
|
|
69
|
+
*
|
|
70
|
+
* @default "default"
|
|
71
|
+
*/
|
|
72
|
+
variant?: ImageOptimizerVariant;
|
|
73
|
+
}
|
|
74
|
+
type ImageOptimizerVariant = "default" | "sapcc";
|
|
75
|
+
/**
|
|
76
|
+
* Builds the path emitted after `/img-proxy/{key}` on the loader (client)
|
|
77
|
+
* side. Receives the pathname relative to the media host (with leading
|
|
78
|
+
* slash, without query string) and the parsed query string of the original
|
|
79
|
+
* `src`. Return the proxy path (starting with `/`), or `undefined` to skip
|
|
80
|
+
* proxying and return the original `src` unchanged.
|
|
81
|
+
*/
|
|
82
|
+
type EncodePathHook = (pathname: string, searchParams: URLSearchParams) => string | undefined;
|
|
83
|
+
/**
|
|
84
|
+
* Rebuilds the upstream URL on the route-handler (server) side. Receives the
|
|
85
|
+
* media host (without trailing slash) and the decoded `[...path]` segments
|
|
86
|
+
* from the route params. Must return an absolute URL.
|
|
87
|
+
*/
|
|
88
|
+
type BuildUpstreamUrlHook = (mediaHost: string, segments: string[]) => string;
|
|
89
|
+
interface ImageOptimizerRouteContext {
|
|
90
|
+
params: Promise<ImageOptimizerRouteParams>;
|
|
91
|
+
}
|
|
92
|
+
interface ImageOptimizerRouteParams {
|
|
93
|
+
host: string;
|
|
94
|
+
path: string[];
|
|
95
|
+
}
|
|
96
|
+
//#endregion
|
|
97
|
+
//#region src/image-optimizer/create-image-optimizer.d.ts
|
|
98
|
+
/**
|
|
99
|
+
* Creates a Next.js custom image `loader` and a proxy route-handler `GET`
|
|
100
|
+
* that together let an image CDN in front of the storefront (the Alokai
|
|
101
|
+
* Image Optimizer)
|
|
102
|
+
* optimize media-host images.
|
|
103
|
+
*
|
|
104
|
+
* The loader rewrites `src` values matching a configured media host to
|
|
105
|
+
* `/img-proxy/{key}/{path}?width=&quality=&format=auto`; the route handler
|
|
106
|
+
* proxies that path back to the media host and streams the body so the CDN
|
|
107
|
+
* can transform it on the way to the browser. Non-matching `src` values are
|
|
108
|
+
* returned unchanged.
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* ```ts
|
|
112
|
+
* // config/image-optimizer.ts - the single place the config exists; the
|
|
113
|
+
* // default export lets `images.loaderFile` point straight at this file
|
|
114
|
+
* import { createImageOptimizer } from "@vue-storefront/next";
|
|
115
|
+
*
|
|
116
|
+
* export const { GET, loader } = createImageOptimizer({ hosts: { ct: {} } });
|
|
117
|
+
*
|
|
118
|
+
* export default loader;
|
|
119
|
+
*
|
|
120
|
+
* // app/img-proxy/[host]/[...path]/route.ts
|
|
121
|
+
* export { GET } from "@/config/image-optimizer";
|
|
122
|
+
* ```
|
|
123
|
+
*/
|
|
124
|
+
declare function createImageOptimizer(config: CreateImageOptimizerConfig): ImageOptimizer;
|
|
125
|
+
//#endregion
|
|
6
126
|
//#region src/logger/types.d.ts
|
|
7
127
|
type LogVerbosity = "alert" | "critical" | "debug" | "emergency" | "error" | "info" | "notice" | "warning";
|
|
8
128
|
type LoggerOptions = Partial<{
|
|
@@ -176,4 +296,4 @@ type DefineSdkModule = (context: InjectedContext) => ReturnType<typeof buildModu
|
|
|
176
296
|
*/
|
|
177
297
|
declare function defineSdkModule<TModuleDefinition extends DefineSdkModule>(moduleDefinition: TModuleDefinition): TModuleDefinition;
|
|
178
298
|
//#endregion
|
|
179
|
-
export { type CreateSdkOptions, createAlokaiMiddleware, createLogger, createSdk, defineGetConfigSwitcherHeader, defineSdkConfig, defineSdkModule, env, resolveSdkOptions };
|
|
299
|
+
export { BuildUpstreamUrlHook, CreateImageOptimizerConfig, type CreateSdkOptions, EncodePathHook, ImageOptimizer, ImageOptimizerDelegatedHostConfig, ImageOptimizerHostConfig, ImageOptimizerHosts, ImageOptimizerLoader, ImageOptimizerProxiedHostConfig, ImageOptimizerRouteContext, ImageOptimizerRouteHandler, ImageOptimizerRouteParams, ImageOptimizerVariant, createAlokaiMiddleware, createImageOptimizer, createLogger, createSdk, defineGetConfigSwitcherHeader, defineSdkConfig, defineSdkModule, env, resolveSdkOptions };
|
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
|
|
@@ -18,6 +19,207 @@ const createLogger = (options) => {
|
|
|
18
19
|
return injectMetadata(LoggerFactory.create(LoggerType.ConsolaGcp, options), { alokai: { context: "storefront" } });
|
|
19
20
|
};
|
|
20
21
|
//#endregion
|
|
22
|
+
//#region src/image-optimizer/constants.ts
|
|
23
|
+
/**
|
|
24
|
+
* URL prefix the loader emits and the route folder must use:
|
|
25
|
+
* `app/img-proxy/[host]/[...path]/route.ts`.
|
|
26
|
+
*/
|
|
27
|
+
const IMG_PROXY_PREFIX = "/img-proxy";
|
|
28
|
+
/**
|
|
29
|
+
* Cache-Control applied to successful (2xx) upstream responses unless
|
|
30
|
+
* overridden per host via `cacheControl`.
|
|
31
|
+
*/
|
|
32
|
+
const DEFAULT_CACHE_CONTROL = "public, max-age=3600, s-maxage=86400";
|
|
33
|
+
/**
|
|
34
|
+
* Cache-Control applied to every non-2xx or failed response so errors are
|
|
35
|
+
* never cached by the browser or the CDN.
|
|
36
|
+
*/
|
|
37
|
+
const ERROR_CACHE_CONTROL = "no-store, no-cache, must-revalidate";
|
|
38
|
+
const variants = {
|
|
39
|
+
default: {
|
|
40
|
+
buildUpstreamUrl: (mediaHost, segments) => `${mediaHost}/${joinEncoded(segments)}`,
|
|
41
|
+
encodePath: (pathname) => pathname === "" ? void 0 : pathname
|
|
42
|
+
},
|
|
43
|
+
sapcc: {
|
|
44
|
+
buildUpstreamUrl: (mediaHost, segments) => {
|
|
45
|
+
const [context = "", ...path] = segments;
|
|
46
|
+
return `${mediaHost}/${joinEncoded(path)}?context=${encodeURIComponent(context)}`;
|
|
47
|
+
},
|
|
48
|
+
encodePath: (pathname, searchParams) => {
|
|
49
|
+
var _pathname$split$pop;
|
|
50
|
+
const context = searchParams.get("context");
|
|
51
|
+
if (!context) return;
|
|
52
|
+
const hasExtension = ((_pathname$split$pop = pathname.split("/").pop()) !== null && _pathname$split$pop !== void 0 ? _pathname$split$pop : "").includes(".");
|
|
53
|
+
const separator = pathname.endsWith("/") ? "" : "/";
|
|
54
|
+
const safePathname = hasExtension ? pathname : `${pathname}${separator}image.png`;
|
|
55
|
+
return `/${encodeURIComponent(context)}${safePathname}`;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
function joinEncoded(segments) {
|
|
60
|
+
return segments.map((segment) => encodeURIComponent(segment)).join("/");
|
|
61
|
+
}
|
|
62
|
+
//#endregion
|
|
63
|
+
//#region src/image-optimizer/resolve-hosts.ts
|
|
64
|
+
/**
|
|
65
|
+
* Normalizes the `hosts` config into an ordered list of fully-resolved host
|
|
66
|
+
* configs. Throws on misconfiguration so mistakes surface at module init
|
|
67
|
+
* instead of as silently unoptimized images.
|
|
68
|
+
*
|
|
69
|
+
* @internal
|
|
70
|
+
*/
|
|
71
|
+
function resolveHosts(hosts) {
|
|
72
|
+
return Object.entries(hosts).map(([key, config]) => resolveHost(key, config));
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Derives the conventional env variable name from a host key, e.g.
|
|
76
|
+
* `ct` -> `NEXT_PUBLIC_CT_MEDIA_HOST`, `my-cms` -> `NEXT_PUBLIC_MY_CMS_MEDIA_HOST`.
|
|
77
|
+
*
|
|
78
|
+
* @internal
|
|
79
|
+
*/
|
|
80
|
+
function deriveMediaHostEnvName(hostKey) {
|
|
81
|
+
return `NEXT_PUBLIC_${hostKey.replaceAll("-", "_").toUpperCase()}_MEDIA_HOST`;
|
|
82
|
+
}
|
|
83
|
+
const HOST_KEY_PATTERN = /^(?=.*[a-z])[a-z0-9-]+$/;
|
|
84
|
+
function resolveHost(key, config) {
|
|
85
|
+
var _config$variant, _config$buildUpstream, _config$cacheControl, _config$encodePath;
|
|
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 variant = variants[(_config$variant = config.variant) !== null && _config$variant !== void 0 ? _config$variant : "default"];
|
|
88
|
+
return {
|
|
89
|
+
buildUpstreamUrl: (_config$buildUpstream = config.buildUpstreamUrl) !== null && _config$buildUpstream !== void 0 ? _config$buildUpstream : variant.buildUpstreamUrl,
|
|
90
|
+
cacheControl: (_config$cacheControl = config.cacheControl) !== null && _config$cacheControl !== void 0 ? _config$cacheControl : DEFAULT_CACHE_CONTROL,
|
|
91
|
+
encodePath: (_config$encodePath = config.encodePath) !== null && _config$encodePath !== void 0 ? _config$encodePath : variant.encodePath,
|
|
92
|
+
key,
|
|
93
|
+
loader: config.loader,
|
|
94
|
+
mediaHostEnvName: deriveMediaHostEnvName(key)
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
//#endregion
|
|
98
|
+
//#region src/image-optimizer/create-image-optimizer.ts
|
|
99
|
+
/**
|
|
100
|
+
* Creates a Next.js custom image `loader` and a proxy route-handler `GET`
|
|
101
|
+
* that together let an image CDN in front of the storefront (the Alokai
|
|
102
|
+
* Image Optimizer)
|
|
103
|
+
* optimize media-host images.
|
|
104
|
+
*
|
|
105
|
+
* The loader rewrites `src` values matching a configured media host to
|
|
106
|
+
* `/img-proxy/{key}/{path}?width=&quality=&format=auto`; the route handler
|
|
107
|
+
* proxies that path back to the media host and streams the body so the CDN
|
|
108
|
+
* can transform it on the way to the browser. Non-matching `src` values are
|
|
109
|
+
* returned unchanged.
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```ts
|
|
113
|
+
* // config/image-optimizer.ts - the single place the config exists; the
|
|
114
|
+
* // default export lets `images.loaderFile` point straight at this file
|
|
115
|
+
* import { createImageOptimizer } from "@vue-storefront/next";
|
|
116
|
+
*
|
|
117
|
+
* export const { GET, loader } = createImageOptimizer({ hosts: { ct: {} } });
|
|
118
|
+
*
|
|
119
|
+
* export default loader;
|
|
120
|
+
*
|
|
121
|
+
* // app/img-proxy/[host]/[...path]/route.ts
|
|
122
|
+
* export { GET } from "@/config/image-optimizer";
|
|
123
|
+
* ```
|
|
124
|
+
*/
|
|
125
|
+
function createImageOptimizer(config) {
|
|
126
|
+
if (Object.keys(config.hosts).length === 0) throw new Error([
|
|
127
|
+
"createImageOptimizer requires at least one host in \"hosts\".",
|
|
128
|
+
"Add the media hosts whose images the loader should optimize, e.g.:",
|
|
129
|
+
"",
|
|
130
|
+
" createImageOptimizer({",
|
|
131
|
+
" hosts: {",
|
|
132
|
+
" ct: {},",
|
|
133
|
+
" sapcc: { variant: \"sapcc\" },",
|
|
134
|
+
" },",
|
|
135
|
+
" });",
|
|
136
|
+
"",
|
|
137
|
+
"Each key becomes a URL segment and derives the env variable holding",
|
|
138
|
+
"the media host URL, e.g. \"ct\" reads NEXT_PUBLIC_CT_MEDIA_HOST."
|
|
139
|
+
].join("\n"));
|
|
140
|
+
const hosts = resolveHosts(config.hosts);
|
|
141
|
+
const warnedMissingHosts = /* @__PURE__ */ new Set();
|
|
142
|
+
function loader({ quality, src, width }) {
|
|
143
|
+
for (const host of hosts) {
|
|
144
|
+
const mediaHost = env(host.mediaHostEnvName);
|
|
145
|
+
if (!mediaHost) {
|
|
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
|
+
}
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
const normalizedHost = mediaHost.replace(/\/$/, "");
|
|
153
|
+
if (!matchesMediaHost(src, normalizedHost)) continue;
|
|
154
|
+
if (host.loader) return host.loader({
|
|
155
|
+
quality,
|
|
156
|
+
src,
|
|
157
|
+
width
|
|
158
|
+
});
|
|
159
|
+
const localPath = src.slice(normalizedHost.length);
|
|
160
|
+
const queryIndex = localPath.indexOf("?");
|
|
161
|
+
const pathname = queryIndex === -1 ? localPath : localPath.slice(0, queryIndex);
|
|
162
|
+
const queryString = queryIndex === -1 ? "" : localPath.slice(queryIndex + 1);
|
|
163
|
+
const proxyPath = host.encodePath(pathname, new URLSearchParams(queryString));
|
|
164
|
+
if (proxyPath === void 0) return src;
|
|
165
|
+
return `${IMG_PROXY_PREFIX}/${host.key}${proxyPath}?width=${width}&quality=${quality || 85}&format=auto`;
|
|
166
|
+
}
|
|
167
|
+
return src;
|
|
168
|
+
}
|
|
169
|
+
function GET(_x, _x2) {
|
|
170
|
+
return _GET.apply(this, arguments);
|
|
171
|
+
}
|
|
172
|
+
function _GET() {
|
|
173
|
+
_GET = _asyncToGenerator(function* (_request, { params }) {
|
|
174
|
+
var _env, _upstream$headers$get;
|
|
175
|
+
const { host: hostKey, path } = yield params;
|
|
176
|
+
const host = hosts.find((entry) => entry.key === hostKey);
|
|
177
|
+
if (!host) return errorResponse(`Unknown image proxy host "${hostKey}"`, 404);
|
|
178
|
+
if (path.length === 0 || path.some(isUnsafeSegment)) return errorResponse("Invalid image path", 400);
|
|
179
|
+
const mediaHost = (_env = env(host.mediaHostEnvName)) === null || _env === void 0 ? void 0 : _env.replace(/\/$/, "");
|
|
180
|
+
if (!mediaHost) return errorResponse(`${host.mediaHostEnvName} is not defined`, 500);
|
|
181
|
+
let upstream;
|
|
182
|
+
try {
|
|
183
|
+
upstream = yield fetch(host.buildUpstreamUrl(mediaHost, path), { redirect: "manual" });
|
|
184
|
+
} catch (error) {
|
|
185
|
+
getLogger().error(`Failed to fetch upstream image for host "${hostKey}": ${String(error)}`);
|
|
186
|
+
return errorResponse("Failed to fetch upstream image", 502);
|
|
187
|
+
}
|
|
188
|
+
if (upstream.status >= 300 && upstream.status < 400) return errorResponse("Upstream redirect is not allowed", 502);
|
|
189
|
+
return new Response(upstream.body, {
|
|
190
|
+
headers: {
|
|
191
|
+
"cache-control": upstream.ok ? host.cacheControl : ERROR_CACHE_CONTROL,
|
|
192
|
+
"content-type": (_upstream$headers$get = upstream.headers.get("content-type")) !== null && _upstream$headers$get !== void 0 ? _upstream$headers$get : "application/octet-stream"
|
|
193
|
+
},
|
|
194
|
+
status: upstream.status
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
return _GET.apply(this, arguments);
|
|
198
|
+
}
|
|
199
|
+
return {
|
|
200
|
+
GET,
|
|
201
|
+
loader
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
let logger;
|
|
205
|
+
function getLogger() {
|
|
206
|
+
var _logger;
|
|
207
|
+
(_logger = logger) !== null && _logger !== void 0 || (logger = createLogger());
|
|
208
|
+
return logger;
|
|
209
|
+
}
|
|
210
|
+
function matchesMediaHost(src, normalizedHost) {
|
|
211
|
+
return src === normalizedHost || src.startsWith(`${normalizedHost}/`) || src.startsWith(`${normalizedHost}?`);
|
|
212
|
+
}
|
|
213
|
+
function isUnsafeSegment(segment) {
|
|
214
|
+
return segment === "" || segment === "." || segment === "..";
|
|
215
|
+
}
|
|
216
|
+
function errorResponse(message, status) {
|
|
217
|
+
return new Response(message, {
|
|
218
|
+
headers: { "cache-control": ERROR_CACHE_CONTROL },
|
|
219
|
+
status
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
//#endregion
|
|
21
223
|
//#region src/middleware.ts
|
|
22
224
|
/**
|
|
23
225
|
* Creates an Alokai middleware wrapper that adds pathname information to request headers.
|
|
@@ -62,32 +264,6 @@ const defaultMethodsRequestConfig = {
|
|
|
62
264
|
} }
|
|
63
265
|
};
|
|
64
266
|
//#endregion
|
|
65
|
-
//#region \0@oxc-project+runtime@0.115.0/helpers/asyncToGenerator.js
|
|
66
|
-
function asyncGeneratorStep(n, t, e, r, o, a, c) {
|
|
67
|
-
try {
|
|
68
|
-
var i = n[a](c), u = i.value;
|
|
69
|
-
} catch (n) {
|
|
70
|
-
e(n);
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
i.done ? t(u) : Promise.resolve(u).then(r, o);
|
|
74
|
-
}
|
|
75
|
-
function _asyncToGenerator(n) {
|
|
76
|
-
return function() {
|
|
77
|
-
var t = this, e = arguments;
|
|
78
|
-
return new Promise(function(r, o) {
|
|
79
|
-
var a = n.apply(t, e);
|
|
80
|
-
function _next(n) {
|
|
81
|
-
asyncGeneratorStep(a, r, o, _next, _throw, "next", n);
|
|
82
|
-
}
|
|
83
|
-
function _throw(n) {
|
|
84
|
-
asyncGeneratorStep(a, r, o, _next, _throw, "throw", n);
|
|
85
|
-
}
|
|
86
|
-
_next(void 0);
|
|
87
|
-
});
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
//#endregion
|
|
91
267
|
//#region src/sdk/helpers/resolveDynamicContext.ts
|
|
92
268
|
const BLACKLISTED_HEADERS = new Set(["host"]);
|
|
93
269
|
function isAppRouterHeaders(headers) {
|
|
@@ -234,4 +410,4 @@ function buildModules(modules) {
|
|
|
234
410
|
return (context) => Object.fromEntries(Object.entries(modules).map(([key, module]) => [key, module(context)]));
|
|
235
411
|
}
|
|
236
412
|
//#endregion
|
|
237
|
-
export { createAlokaiMiddleware, createLogger, createSdk, defineGetConfigSwitcherHeader, defineSdkConfig, defineSdkModule, env, resolveSdkOptions };
|
|
413
|
+
export { createAlokaiMiddleware, createImageOptimizer, createLogger, createSdk, defineGetConfigSwitcherHeader, defineSdkConfig, defineSdkModule, env, resolveSdkOptions };
|
package/dist/server.cjs
ADDED
|
@@ -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 };
|
package/dist/server.mjs
ADDED
|
@@ -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": "
|
|
9
|
+
"version": "8.0.0-next.4",
|
|
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": [
|
|
@@ -31,11 +36,11 @@
|
|
|
31
36
|
"version": "cp CHANGELOG.md ../../docs/enterprise/content/storefront/6.change-log/next.md"
|
|
32
37
|
},
|
|
33
38
|
"dependencies": {
|
|
34
|
-
"@alokai/instrumentation-next-component": "1.0.
|
|
39
|
+
"@alokai/instrumentation-next-component": "1.0.4-next.0",
|
|
35
40
|
"zustand": "^4.5.4"
|
|
36
41
|
},
|
|
37
42
|
"devDependencies": {
|
|
38
|
-
"@alokai/connect": "
|
|
43
|
+
"@alokai/connect": "2.3.0-next.7",
|
|
39
44
|
"@shared/typescript-config": "1.0.0",
|
|
40
45
|
"@types/react": "^19",
|
|
41
46
|
"@types/react-dom": "^19",
|
|
@@ -50,13 +55,12 @@
|
|
|
50
55
|
"vitest": "4.0.18"
|
|
51
56
|
},
|
|
52
57
|
"peerDependencies": {
|
|
53
|
-
"@alokai/connect": "
|
|
58
|
+
"@alokai/connect": "2.3.0-next.7",
|
|
54
59
|
"next": "^16.0.0",
|
|
55
60
|
"react": "^19.0.0"
|
|
56
61
|
},
|
|
57
62
|
"engines": {
|
|
58
|
-
"
|
|
59
|
-
"node": ">=18.0.0"
|
|
63
|
+
"node": "^20.10.0 || >=22.14.0"
|
|
60
64
|
},
|
|
61
65
|
"publishConfig": {
|
|
62
66
|
"access": "public"
|
|
File without changes
|