@scayle/storefront-nuxt 8.6.0 → 8.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,60 @@
1
1
  # @scayle/storefront-nuxt
2
2
 
3
+ ## 8.7.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Introduce a flexible interface for registering custom RPC methods from other modules
8
+
9
+ Currently, applications can configure `rpcDir` and `rpcMethodNames` in the module configuration to register custom RPC methods, however this does not extend to modules.
10
+ This release adds a new module hook `storefront:custom-rpc:extend` which can be used to register custom RPC methods.
11
+ By registering a callback to this hook, other modules can add custom RPC methods to the application.
12
+
13
+ The hook functions similarly to `rpcDir` and `rpcMethodNames`. Hook registrants receive an array of custom RPC imports and can add their own. The import definition should include a `source` which defines the file to import the RPC methods from and a `names` array which controls the exports of this file which should be registered as RPC methods.
14
+
15
+ **rpc-methods.ts**
16
+
17
+ ```typescript
18
+ import type { RpcContext, RpcHandler } from '@scayle/storefront-nuxt'
19
+
20
+ export const foo: RpcHandler<string, number> = function testing(
21
+ param: string,
22
+ _: RpcContext,
23
+ ) {
24
+ return param.length
25
+ }
26
+ ```
27
+
28
+ **module.ts**
29
+
30
+ ```typescript
31
+ function setup() {
32
+ const resolver = createResolver(import.meta.url)
33
+
34
+ nuxt.hook('storefront:custom-rpc:extend', (customRpcImports) => {
35
+ customRpcImports.push({
36
+ source: resolver.resolve('./rpc-methods.ts'),
37
+ names: ['foo'],
38
+ })
39
+ })
40
+ }
41
+ ```
42
+
43
+ As part of this change, the `RPCMethod` types are automatically extended with all custom RPC methods.
44
+ It is no longer necessary to extend `RpcMethodsStorefront`.
45
+ This applies both for the `rpcDir` + `rpcMethodNames` strategy as well as the new hook-based implementation.
46
+
47
+ ### Patch Changes
48
+
49
+ - [RPC] Improve warning about missing RPC method overrides
50
+ - Improve error handling when bootstrapping fails due to a missing shop.
51
+
52
+ ## 8.6.1
53
+
54
+ ### Patch Changes
55
+
56
+ - Consider `baseURL` configuration when bootstrapping error pages. This ensures error pages have the correct `currentShop`.
57
+
3
58
  ## 8.6.0
4
59
 
5
60
  ### Minor Changes
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2024 SCAYLE GmbH
3
+ Copyright (c) 2025 SCAYLE GmbH
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/dist/module.d.mts CHANGED
@@ -1,4 +1,5 @@
1
1
  import * as _nuxt_schema from '@nuxt/schema';
2
+ import { HookResult } from '@nuxt/schema';
2
3
  import { M as ModuleBaseOptions } from './shared/storefront-nuxt.a816664e.mjs';
3
4
  export { d as AdditionalShopConfig, A as AppKeys, h as CheckoutEvent, C as CheckoutShopConfig, i as ModulePublicRuntimeConfig, P as PublicShopConfig, c as SapiConfig, S as SessionConfig, e as ShopConfig, f as ShopConfigIndexed, b as StorageConfig, a as StorageEntity, g as StorefrontConfig } from './shared/storefront-nuxt.a816664e.mjs';
4
5
  export { LogLevel } from '@scayle/storefront-core';
@@ -6,6 +7,25 @@ import '@scayle/unstorage-compression-driver';
6
7
  import 'unstorage';
7
8
  import '../dist/runtime/utils/zodSchema.js';
8
9
 
10
+ /**
11
+ * Definition of a custom RPC import
12
+ */
13
+ interface CustomRpcImport {
14
+ /** The location from which to import the RPC method(s) */
15
+ source: string;
16
+ /** The specifiers to import as RPC methods */
17
+ names: string[];
18
+ }
19
+ /**
20
+ * Build-time hooks used by the module
21
+ */
22
+ interface ModuleHooks {
23
+ 'storefront:custom-rpc:extend': (customRpcImports: CustomRpcImport[]) => HookResult;
24
+ }
25
+ declare module '@nuxt/schema' {
26
+ interface NuxtHooks extends ModuleHooks {
27
+ }
28
+ }
9
29
  declare const _default: _nuxt_schema.NuxtModule<ModuleBaseOptions, ModuleBaseOptions, false>;
10
30
 
11
- export { ModuleBaseOptions, _default as default };
31
+ export { ModuleBaseOptions, type ModuleHooks, _default as default };
package/dist/module.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import * as _nuxt_schema from '@nuxt/schema';
2
+ import { HookResult } from '@nuxt/schema';
2
3
  import { M as ModuleBaseOptions } from './shared/storefront-nuxt.a816664e.js';
3
4
  export { d as AdditionalShopConfig, A as AppKeys, h as CheckoutEvent, C as CheckoutShopConfig, i as ModulePublicRuntimeConfig, P as PublicShopConfig, c as SapiConfig, S as SessionConfig, e as ShopConfig, f as ShopConfigIndexed, b as StorageConfig, a as StorageEntity, g as StorefrontConfig } from './shared/storefront-nuxt.a816664e.js';
4
5
  export { LogLevel } from '@scayle/storefront-core';
@@ -6,6 +7,25 @@ import '@scayle/unstorage-compression-driver';
6
7
  import 'unstorage';
7
8
  import '../dist/runtime/utils/zodSchema.js';
8
9
 
10
+ /**
11
+ * Definition of a custom RPC import
12
+ */
13
+ interface CustomRpcImport {
14
+ /** The location from which to import the RPC method(s) */
15
+ source: string;
16
+ /** The specifiers to import as RPC methods */
17
+ names: string[];
18
+ }
19
+ /**
20
+ * Build-time hooks used by the module
21
+ */
22
+ interface ModuleHooks {
23
+ 'storefront:custom-rpc:extend': (customRpcImports: CustomRpcImport[]) => HookResult;
24
+ }
25
+ declare module '@nuxt/schema' {
26
+ interface NuxtHooks extends ModuleHooks {
27
+ }
28
+ }
9
29
  declare const _default: _nuxt_schema.NuxtModule<ModuleBaseOptions, ModuleBaseOptions, false>;
10
30
 
11
- export { ModuleBaseOptions, _default as default };
31
+ export { ModuleBaseOptions, type ModuleHooks, _default as default };
package/dist/module.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scayle/storefront-nuxt",
3
- "version": "8.6.0",
3
+ "version": "8.7.0",
4
4
  "configKey": "storefront",
5
5
  "compatibility": {
6
6
  "nuxt": "^3.9.0"
package/dist/module.mjs CHANGED
@@ -1,7 +1,7 @@
1
- import { defineNuxtModule, createResolver, extendViteConfig, addServerHandler, addTypeTemplate, addServerPlugin, addPlugin, addImportsDir } from '@nuxt/kit';
1
+ import { defineNuxtModule, createResolver, extendViteConfig, addTypeTemplate, addPlugin, addImportsDir } from '@nuxt/kit';
2
2
  import { rpcMethods } from '@scayle/storefront-core';
3
3
  import { defu } from 'defu';
4
- import { genImport, genSafeVariableName } from 'knitwork';
4
+ import { genImport, genSafeVariableName, genExport } from 'knitwork';
5
5
  import { builtinDrivers } from 'unstorage';
6
6
  import { createConsola } from 'consola';
7
7
  import { getApiBasePath } from '../dist/runtime/server/middleware/bootstrap-utils.js';
@@ -27,8 +27,7 @@ function getUsedDrivers(options) {
27
27
  });
28
28
  return Array.from(drivers);
29
29
  }
30
- function createVirtualDriverImport(options) {
31
- const usedDrivers = getUsedDrivers(options);
30
+ function createVirtualDriverImport(usedDrivers) {
32
31
  const usesCompression = !stringToBoolean(
33
32
  process.env.SFC_OMIT_COMPRESSION,
34
33
  false
@@ -45,7 +44,7 @@ export default {
45
44
  }`;
46
45
  }
47
46
  const PACKAGE_NAME = "@scayle/storefront-nuxt";
48
- const PACKAGE_VERSION = "8.6.0";
47
+ const PACKAGE_VERSION = "8.7.0";
49
48
  const logger = createConsola({
50
49
  fancy: true,
51
50
  formatOptions: {
@@ -59,6 +58,150 @@ const logger = createConsola({
59
58
  // However when typechecking it will resolve to consola/browser which does have the fancy option
60
59
  // This assertion avoids the typescript error
61
60
  });
61
+ function createRpcMethodIndex(customRpcImports) {
62
+ return customRpcImports.map(({ source, names }) => genExport(source, names)).join(
63
+ "\n"
64
+ ) + `
65
+ export {}`;
66
+ }
67
+ function createRpcMethodTypeDeclaration(customRpcImports) {
68
+ return `// Auto-generated
69
+ type RpcMethodsCustomType = {
70
+ ${customRpcImports.reduce(
71
+ (lines, { source, names }) => lines.concat(
72
+ names.map((name) => `${name}: typeof import('${source}')['${name}'],`).join("\n")
73
+ ),
74
+ []
75
+ ).join("\n")}
76
+ }
77
+ declare module '@scayle/storefront-nuxt' {
78
+ export interface RpcMethodsStorefront extends RpcMethodsCustomType {}
79
+ }
80
+ export {}`;
81
+ }
82
+ function nitroSetup(nitroConfig, config) {
83
+ const { resolve } = createResolver(import.meta.url);
84
+ const { apiBasePath, customRpcImports, coreMethodNames, usedDrivers } = config;
85
+ nitroConfig.externals = nitroConfig.externals || {};
86
+ nitroConfig.externals.inline = nitroConfig.externals.inline || [];
87
+ const runtimePaths = [
88
+ "handler",
89
+ "error/handler",
90
+ "campaignKey",
91
+ "cached",
92
+ "context",
93
+ "createLog",
94
+ "log",
95
+ "api/cacheAuth",
96
+ "rpc/rpcCall",
97
+ "rpc/rpcRequest",
98
+ "server/middleware/oauth",
99
+ "server/middleware/redirects",
100
+ "server/middleware/cache",
101
+ "server/utils/cacheStorage",
102
+ "utils/zodSchema",
103
+ "nitro/plugins/cacheRuntimeConfig",
104
+ "nitro/plugins/nitroLogger",
105
+ "nitro/plugins/nitroRuntimeStorageConfig",
106
+ "nitro/plugins/nitroSetXPoweredByHeader",
107
+ "nitro/plugins/configValidation"
108
+ ];
109
+ runtimePaths.forEach((path) => {
110
+ const file = resolve(`./runtime/${path}`);
111
+ nitroConfig.externals?.inline?.push(file);
112
+ });
113
+ nitroConfig.replace = nitroConfig.replace || {};
114
+ nitroConfig.replace["process.env.SFC_OMIT_MD5"] = stringToBoolean(
115
+ process.env.SFC_OMIT_MD5
116
+ );
117
+ nitroConfig.replace.__sfc_version = PACKAGE_VERSION;
118
+ nitroConfig.virtual = {
119
+ ...nitroConfig.virtual,
120
+ "#virtual/storage-drivers": createVirtualDriverImport(
121
+ usedDrivers
122
+ ),
123
+ "#virtual/customRpcMethods": createRpcMethodIndex(
124
+ customRpcImports
125
+ )
126
+ };
127
+ nitroConfig.handlers = nitroConfig.handlers ?? [];
128
+ nitroConfig.handlers.unshift({
129
+ middleware: true,
130
+ handler: resolve("./runtime/server/middleware/bootstrap")
131
+ });
132
+ const allRpcNames = customRpcImports.reduce(
133
+ (acc, { names }) => acc.concat(names),
134
+ []
135
+ ).concat(coreMethodNames);
136
+ for (const methodName of allRpcNames) {
137
+ nitroConfig.handlers.push({
138
+ route: `${apiBasePath}/rpc/${methodName}`,
139
+ handler: resolve("./runtime/api/rpcHandler"),
140
+ method: "post"
141
+ });
142
+ }
143
+ nitroConfig.handlers.push({
144
+ route: `${apiBasePath}/purge/all`,
145
+ handler: resolve("./runtime/api/purgeAll"),
146
+ method: "post"
147
+ });
148
+ nitroConfig.handlers.push({
149
+ route: `${apiBasePath}/purge/tags`,
150
+ handler: resolve("./runtime/api/purgeTags"),
151
+ method: "post"
152
+ });
153
+ nitroConfig.handlers.push({
154
+ route: `${apiBasePath}/up`,
155
+ handler: resolve("./runtime/api/up")
156
+ });
157
+ nitroConfig.plugins = nitroConfig.plugins ?? [];
158
+ nitroConfig.plugins.push(resolve("./runtime/nitro/plugins/nitroLogger"));
159
+ nitroConfig.plugins.push(
160
+ resolve("./runtime/nitro/plugins/nitroRuntimeStorageConfig")
161
+ );
162
+ if (stringToBoolean(process.env.SFC_PLUGIN_CONFIG_VALIDATION_ENABLED, true)) {
163
+ logger.debug("Installing config validation plugin");
164
+ nitroConfig.plugins.push(
165
+ resolve("./runtime/nitro/plugins/configValidation")
166
+ );
167
+ }
168
+ if (stringToBoolean(process.env.SFC_PLUGIN_POWERED_BY_ENABLED, true)) {
169
+ logger.debug("Installing X-Powered-By plugin");
170
+ nitroConfig.plugins.push(
171
+ resolve("./runtime/nitro/plugins/nitroSetXPoweredByHeader")
172
+ );
173
+ }
174
+ if (stringToBoolean(process.env.SFC_PLUGIN_RUNTIME_PERFORMANCE_ENABLED, true)) {
175
+ logger.debug("Installing runtime performance plugin");
176
+ nitroConfig.plugins.push(
177
+ resolve("./runtime/nitro/plugins/cacheRuntimeConfig")
178
+ );
179
+ }
180
+ }
181
+ function validateRpcMethodOverrides(rpcMethodOverrides, customRpcNames, coreRpcNames) {
182
+ const overridenRPCMethodNames = new Set(rpcMethodOverrides);
183
+ for (const rpcMethodName of customRpcNames) {
184
+ if (coreRpcNames.has(rpcMethodName) && !overridenRPCMethodNames.has(rpcMethodName)) {
185
+ logger.warn(`
186
+ Overriding RPC method '${rpcMethodName}' from '@scayle/storefront-nuxt' with a local custom implementation.
187
+
188
+ Should this be done on purpose, please be aware that this can lead to unexpected issues since the '@scayle/storefront-nuxt' package also uses this RPC method internally.
189
+ To silence this warning, you can add '${rpcMethodName}' to 'rpcMethodOverrides' in the '@scayle/storefront-nuxt' config.
190
+
191
+ In the future this will become an error where overrides need to be explicitly defined as an override.
192
+ `);
193
+ } else {
194
+ overridenRPCMethodNames.delete(rpcMethodName);
195
+ }
196
+ }
197
+ if (overridenRPCMethodNames.size > 0) {
198
+ logger.warn(`
199
+ Detected RPC methods which were supposed to be overridden but are not. This indicates an incorrect configuration and should be adjusted. Check the 'rpcMethodOverrides' property.
200
+
201
+ Missing RPC Method Overrides: ${Array.from(overridenRPCMethodNames).map((name) => `'${name}'`).join(", ")}
202
+ `);
203
+ }
204
+ }
62
205
  const module = defineNuxtModule({
63
206
  meta: {
64
207
  name: PACKAGE_NAME,
@@ -102,7 +245,7 @@ const module = defineNuxtModule({
102
245
  },
103
246
  internalAccessHeader: ""
104
247
  },
105
- setup(options, nuxt) {
248
+ async setup(options, nuxt) {
106
249
  const { resolve } = createResolver(import.meta.url);
107
250
  const { resolve: appResolve } = createResolver(nuxt.options.srcDir);
108
251
  nuxt.options.runtimeConfig.storefront = defu(
@@ -125,55 +268,40 @@ const module = defineNuxtModule({
125
268
  "slugify"
126
269
  );
127
270
  });
128
- nuxt.options.serverHandlers.unshift({
129
- middleware: true,
130
- handler: resolve("./runtime/server/middleware/bootstrap")
131
- });
132
271
  const apiBasePath = getApiBasePath(options, "/");
133
272
  const rpcMethodNames = new Set(Object.keys(rpcMethods));
134
- const overridenRPCMethodNames = new Set(options.rpcMethodOverrides);
135
- for (const rpcMethodName of options.rpcMethodNames ?? []) {
136
- if (rpcMethodNames.has(rpcMethodName) && !overridenRPCMethodNames.has(rpcMethodName)) {
137
- logger.warn(`
138
- Overriding RPC method '${rpcMethodName}' from '@scayle/storefront-nuxt' with a local custom implementation.
139
-
140
- Should this be done on purpose, please be aware that this can lead to unexpected issues since the '@scayle/storefront-nuxt' package also uses this RPC method internally.
141
- To silence this warning, you can add '${rpcMethodName}' to 'rpcMethodOverrides' in the '@scayle/storefront-nuxt' config.
142
-
143
- In the future this will become an error where overrides need to be explicitly defined as an override.
144
- `);
145
- } else {
146
- overridenRPCMethodNames.delete(rpcMethodName);
147
- }
148
- rpcMethodNames.add(rpcMethodName);
149
- }
150
- if (overridenRPCMethodNames.size > 0) {
151
- logger.warn(`
152
- Specified RPC which were supposed to be overriden but are not. This indicates and outdated config and should be adjusted.
153
-
154
- Missing RPC Overrides: ${Array.from(overridenRPCMethodNames).map((name) => `'${name}'`).join(", ")}
155
- `);
156
- }
157
- rpcMethodNames.forEach((methodName) => {
158
- addServerHandler({
159
- route: `${apiBasePath}/rpc/${methodName}`,
160
- handler: resolve("./runtime/api/rpcHandler"),
161
- method: "post"
273
+ const storefrontRpcMethodNames = options.rpcMethodNames ?? [];
274
+ const customRpcImports = [];
275
+ nuxt.hook("modules:done", async () => {
276
+ await nuxt.callHook("storefront:custom-rpc:extend", customRpcImports);
277
+ customRpcImports.forEach((customImport) => {
278
+ customImport.names.forEach((name) => {
279
+ if (rpcMethodNames.has(name)) {
280
+ throw new Error(
281
+ `Core RPC method '${name}' cannot be overriden via the 'storefront:custom-rpc:extend' hook`
282
+ );
283
+ }
284
+ });
285
+ customImport.names = customImport.names.filter(
286
+ (name) => !storefrontRpcMethodNames.includes(name)
287
+ );
288
+ });
289
+ customRpcImports.push({
290
+ source: appResolve(options.rpcDir ?? "./rpcMethods"),
291
+ names: storefrontRpcMethodNames
292
+ });
293
+ validateRpcMethodOverrides(
294
+ options.rpcMethodOverrides ?? [],
295
+ customRpcImports.reduce(
296
+ (acc, { names }) => acc.concat(names),
297
+ []
298
+ ),
299
+ rpcMethodNames
300
+ );
301
+ addTypeTemplate({
302
+ filename: "types/storefront-custom-rpc.d.ts",
303
+ getContents: () => createRpcMethodTypeDeclaration(customRpcImports)
162
304
  });
163
- });
164
- addServerHandler({
165
- route: `${apiBasePath}/purge/all`,
166
- handler: resolve("./runtime/api/purgeAll"),
167
- method: "post"
168
- });
169
- addServerHandler({
170
- route: `${apiBasePath}/purge/tags`,
171
- handler: resolve("./runtime/api/purgeTags"),
172
- method: "post"
173
- });
174
- addServerHandler({
175
- route: `${apiBasePath}/up`,
176
- handler: resolve("./runtime/api/up")
177
305
  });
178
306
  addTypeTemplate({
179
307
  filename: "types/storefront-bootstrap.d.ts",
@@ -183,10 +311,10 @@ Missing RPC Overrides: ${Array.from(overridenRPCMethodNames).map((name) => `'${n
183
311
  import type { PublicShopConfig } from '@scayle/storefront-nuxt';
184
312
  declare module 'h3' {
185
313
  interface H3EventContext {
186
- $rpcContext: RpcContext,
187
- $cache: Cache
188
- $currentShop: PublicShopConfig,
189
- $availableShops: PublicShopConfig[]
314
+ $rpcContext?: RpcContext,
315
+ $cache?: Cache
316
+ $currentShop?: PublicShopConfig,
317
+ $availableShops?: PublicShopConfig[]
190
318
  $log: Log
191
319
  }
192
320
  }
@@ -195,64 +323,13 @@ Missing RPC Overrides: ${Array.from(overridenRPCMethodNames).map((name) => `'${n
195
323
  }
196
324
  export {}`
197
325
  });
198
- addServerPlugin(resolve("./runtime/nitro/plugins/nitroLogger"));
199
- addServerPlugin(
200
- resolve("./runtime/nitro/plugins/nitroRuntimeStorageConfig")
201
- );
202
- if (stringToBoolean(process.env.SFC_PLUGIN_CONFIG_VALIDATION_ENABLED, true)) {
203
- logger.debug("Installing config validation plugin");
204
- addServerPlugin(resolve("./runtime/nitro/plugins/configValidation"));
205
- }
206
- if (stringToBoolean(process.env.SFC_PLUGIN_POWERED_BY_ENABLED, true)) {
207
- logger.debug("Installing X-Powered-By plugin");
208
- addServerPlugin(
209
- resolve("./runtime/nitro/plugins/nitroSetXPoweredByHeader")
210
- );
211
- }
212
- if (stringToBoolean(process.env.SFC_PLUGIN_RUNTIME_PERFORMANCE_ENABLED, true)) {
213
- logger.debug("Installing runtime performance plugin");
214
- addServerPlugin(resolve("./runtime/nitro/plugins/cacheRuntimeConfig"));
215
- }
216
326
  nuxt.hooks.hook("nitro:config", (nitroConfig) => {
217
- nitroConfig.externals = nitroConfig.externals || {};
218
- nitroConfig.externals.inline = nitroConfig.externals.inline || [];
219
- const runtimePaths = [
220
- "handler",
221
- "error/handler",
222
- "campaignKey",
223
- "cached",
224
- "context",
225
- "createLog",
226
- "log",
227
- "api/cacheAuth",
228
- "rpc/rpcCall",
229
- "rpc/rpcRequest",
230
- "server/middleware/oauth",
231
- "server/middleware/redirects",
232
- "server/middleware/cache",
233
- "server/utils/cacheStorage",
234
- "utils/zodSchema",
235
- "nitro/plugins/cacheRuntimeConfig",
236
- "nitro/plugins/nitroLogger",
237
- "nitro/plugins/nitroRuntimeStorageConfig",
238
- "nitro/plugins/nitroSetXPoweredByHeader",
239
- "nitro/plugins/configValidation"
240
- ];
241
- runtimePaths.forEach((path) => {
242
- const file = resolve(`./runtime/${path}`);
243
- nitroConfig.externals?.inline?.push(file);
327
+ nitroSetup(nitroConfig, {
328
+ apiBasePath,
329
+ customRpcImports,
330
+ coreMethodNames: [...rpcMethodNames],
331
+ usedDrivers: getUsedDrivers(nuxt.options.runtimeConfig.storefront)
244
332
  });
245
- nitroConfig.replace = nitroConfig.replace || {};
246
- nitroConfig.replace["process.env.SFC_OMIT_MD5"] = stringToBoolean(
247
- process.env.SFC_OMIT_MD5
248
- );
249
- nitroConfig.replace.__sfc_version = PACKAGE_VERSION;
250
- nitroConfig.virtual = {
251
- ...nitroConfig.virtual,
252
- "#virtual/storage-drivers": createVirtualDriverImport(
253
- nuxt.options.runtimeConfig.storefront
254
- )
255
- };
256
333
  });
257
334
  nuxt.hooks.hook("nitro:init", (nitro) => {
258
335
  nitro.hooks.hook("rollup:before", (_nitro, rollupConfig) => {
@@ -269,9 +346,6 @@ Missing RPC Overrides: ${Array.from(overridenRPCMethodNames).map((name) => `'${n
269
346
  };
270
347
  });
271
348
  });
272
- nuxt.options.alias["#rpcMethods"] = appResolve(
273
- options.rpcDir ?? "./rpcMethods"
274
- );
275
349
  nuxt.options.alias["#storefront/composables"] = resolve(
276
350
  "./runtime/composables"
277
351
  );
@@ -3,6 +3,16 @@ import { HttpStatusCode } from "@scayle/storefront-core";
3
3
  import { eventHandlerWithCacheAuth } from "./cacheAuth.js";
4
4
  export default eventHandlerWithCacheAuth(
5
5
  defineEventHandler(async (event) => {
6
+ if (!event.context.$rpcContext) {
7
+ return new Response("No $rpcContext was found for the request", {
8
+ status: HttpStatusCode.INTERNAL_SERVER_ERROR
9
+ });
10
+ }
11
+ if (!event.context.$cache) {
12
+ return new Response("No $cache was found for the request", {
13
+ status: HttpStatusCode.INTERNAL_SERVER_ERROR
14
+ });
15
+ }
6
16
  const logger = event.context.$rpcContext.log.space("purge-all");
7
17
  try {
8
18
  logger.debug("purging cache");
@@ -3,6 +3,16 @@ import { HttpStatusCode } from "@scayle/storefront-core";
3
3
  import { eventHandlerWithCacheAuth } from "./cacheAuth.js";
4
4
  export default eventHandlerWithCacheAuth(
5
5
  defineEventHandler(async (event) => {
6
+ if (!event.context.$rpcContext) {
7
+ return new Response("No $rpcContext was found for the request", {
8
+ status: HttpStatusCode.INTERNAL_SERVER_ERROR
9
+ });
10
+ }
11
+ if (!event.context.$cache) {
12
+ return new Response("No $cache was found for the request", {
13
+ status: HttpStatusCode.INTERNAL_SERVER_ERROR
14
+ });
15
+ }
6
16
  const tags = await readBody(event);
7
17
  const logger = event.context.$rpcContext.log.space("purge-tags");
8
18
  logger.debug(`purging tags: ${tags}`);
@@ -1,5 +1,5 @@
1
1
  import { defineEventHandler, readBody, createError, getRequestURL } from "h3";
2
- import { purifySensitiveData } from "@scayle/storefront-core";
2
+ import { purifySensitiveData, HttpStatusCode } from "@scayle/storefront-core";
3
3
  import { trace, SpanStatusCode } from "@opentelemetry/api";
4
4
  import { handler } from "../handler.js";
5
5
  import { resolveError } from "../error/handler.js";
@@ -9,6 +9,12 @@ const tracer = trace.getTracer(
9
9
  "__sfc_version"
10
10
  );
11
11
  export default defineEventHandler(async (event) => {
12
+ const $rpcContext = event.context.$rpcContext;
13
+ if (!$rpcContext) {
14
+ return new Response("No $rpcContext was found for the request", {
15
+ status: HttpStatusCode.INTERNAL_SERVER_ERROR
16
+ });
17
+ }
12
18
  const pathComponents = event.path.split("?")[0].split("/");
13
19
  const method = pathComponents[pathComponents.length - 1];
14
20
  const body = await readBody(event);
@@ -21,7 +27,7 @@ export default defineEventHandler(async (event) => {
21
27
  typeof body?.payload !== "object" ? { [typeof body?.payload]: body?.payload } : body?.payload
22
28
  )
23
29
  );
24
- event.context.$rpcContext.log.space("sfc").debug(`RPC Handler: ${method}, Payload: ${payloadToBeLogged}`);
30
+ $rpcContext.log.space("sfc").debug(`RPC Handler: ${method}, Payload: ${payloadToBeLogged}`);
25
31
  const nitroApp = useNitroApp();
26
32
  return await tracer.startActiveSpan(
27
33
  `storefront-nuxt.rpc/${method}`,
@@ -41,14 +47,14 @@ export default defineEventHandler(async (event) => {
41
47
  await nitroApp.hooks?.callHook(
42
48
  "storefront:rpc:before",
43
49
  method,
44
- event.context.$rpcContext,
50
+ $rpcContext,
45
51
  body?.payload
46
52
  );
47
- result = await handler(method, body?.payload, event.context.$rpcContext);
53
+ result = await handler(method, body?.payload, $rpcContext);
48
54
  await nitroApp.hooks.callHook(
49
55
  "storefront:rpc:after",
50
56
  method,
51
- event.context.$rpcContext,
57
+ $rpcContext,
52
58
  result
53
59
  );
54
60
  if (result instanceof Response && !result.ok) {
@@ -57,11 +63,11 @@ export default defineEventHandler(async (event) => {
57
63
  span.setStatus({ code: SpanStatusCode.OK });
58
64
  }
59
65
  } catch (e) {
60
- event.context.$rpcContext.log.space("sfc").error(`RPC Handler failed: ${method}`, e);
66
+ $rpcContext.log.space("sfc").error(`RPC Handler failed: ${method}`, e);
61
67
  await nitroApp.hooks.callHook(
62
68
  "storefront:rpc:error",
63
69
  method,
64
- event.context.$rpcContext,
70
+ $rpcContext,
65
71
  e
66
72
  );
67
73
  result = createError(resolveError(e));
@@ -1,5 +1,5 @@
1
1
  import { rpcMethods as coreRpcMethods } from "@scayle/storefront-core";
2
- import * as storefrontRpcMethods from "#rpcMethods";
2
+ import * as storefrontRpcMethods from "#virtual/customRpcMethods";
3
3
  export const handler = async (method, payload, rpcContext) => {
4
4
  const rpcMethods = {
5
5
  ...coreRpcMethods,
@@ -5,8 +5,12 @@ export default defineNuxtPlugin((nuxtApp) => {
5
5
  const currentShop = useCurrentShop();
6
6
  const availableShops = useAvailableShops();
7
7
  if (import.meta.server && nuxtApp.ssrContext) {
8
- currentShop.value = nuxtApp.ssrContext.event.context.$currentShop;
9
- availableShops.value = nuxtApp.ssrContext.event.context.$availableShops;
8
+ if (nuxtApp.ssrContext.event.context.$currentShop) {
9
+ currentShop.value = nuxtApp.ssrContext.event.context.$currentShop;
10
+ }
11
+ if (nuxtApp.ssrContext.event.context.$availableShops) {
12
+ availableShops.value = nuxtApp.ssrContext.event.context.$availableShops;
13
+ }
10
14
  }
11
15
  return {
12
16
  provide: {
@@ -5,7 +5,7 @@ type BootstrapPath = {
5
5
  path: string;
6
6
  originalPath: string;
7
7
  };
8
- export declare function getBootstrapPath(event: H3Event): BootstrapPath;
8
+ export declare function getBootstrapPath(url: URL, baseUrl: string): BootstrapPath;
9
9
  export declare const getShopByPath: (event: H3Event, shops: ShopConfig[], appBasePath: string) => ShopConfig | undefined;
10
10
  export declare const convertShopsToList: (shops: ShopConfig[] | ShopConfigIndexed) => ShopConfig[];
11
11
  export declare function getApiBasePath(storefrontConfig: ModuleBaseOptions, baseUrl: string): string;
@@ -4,9 +4,9 @@ import {
4
4
  getRequestURL
5
5
  } from "h3";
6
6
  import { joinURL } from "ufo";
7
- export function getBootstrapPath(event) {
8
- const url = getRequestURL(event);
9
- const path = url.pathname === "/__nuxt_error" ? url.searchParams.get("url") ?? url.pathname : url.pathname;
7
+ export function getBootstrapPath(url, baseUrl) {
8
+ const queryUrl = url.searchParams.get("url");
9
+ const path = url.pathname === joinURL(baseUrl, "/__nuxt_error") && queryUrl ? joinURL(baseUrl, queryUrl) : url.pathname;
10
10
  return { path, originalPath: url.pathname };
11
11
  }
12
12
  const getShopByDomain = (event, shops) => {
@@ -14,7 +14,8 @@ const getShopByDomain = (event, shops) => {
14
14
  return shops.find((s) => s.domain === host);
15
15
  };
16
16
  export const getShopByPath = (event, shops, appBasePath) => {
17
- const { path } = getBootstrapPath(event);
17
+ const url = getRequestURL(event);
18
+ const { path } = getBootstrapPath(url, appBasePath);
18
19
  const localeIndex = appBasePath !== "/" ? 2 : 1;
19
20
  const prefix = path.split("/")?.[localeIndex];
20
21
  const matchesShopPrefix = (shop) => Array.isArray(shop.path) ? shop.path.includes(prefix) : shop.path === prefix;
@@ -1,13 +1,4 @@
1
- import type { RpcContext, Cache, ShopUser } from '@scayle/storefront-core';
2
- import type { PublicShopConfig } from '../../../module.js';
3
- declare module 'h3' {
4
- interface H3EventContext {
5
- $rpcContext: RpcContext;
6
- $cache: Cache;
7
- $currentShop: PublicShopConfig;
8
- $availableShops: PublicShopConfig[];
9
- }
10
- }
1
+ import type { ShopUser } from '@scayle/storefront-core';
11
2
  export interface ShopSessionData {
12
3
  user?: ShopUser;
13
4
  accessToken?: string;
@@ -1,4 +1,4 @@
1
- import { defineEventHandler, getCookie, deleteCookie } from "h3";
1
+ import { defineEventHandler, getCookie, deleteCookie, getRequestURL } from "h3";
2
2
  import { decodeJwt } from "jose";
3
3
  import { randomUUID } from "uncrypto";
4
4
  import {
@@ -6,6 +6,7 @@ import {
6
6
  unsignCookie
7
7
  } from "@scayle/h3-session";
8
8
  import { trace, SpanStatusCode } from "@opentelemetry/api";
9
+ import { joinURL } from "ufo";
9
10
  import { buildContext } from "../../context.js";
10
11
  import { useCacheStorage, useSessionStorage } from "../utils/cacheStorage.js";
11
12
  import {
@@ -139,14 +140,15 @@ async function bootstrap(event, $shopConfig, $storefrontConfig, apiBasePath, con
139
140
  });
140
141
  }
141
142
  export default defineEventHandler(async (event) => {
142
- const { path, originalPath } = getBootstrapPath(event);
143
- if (path.startsWith("/__nuxt")) {
143
+ const config = useRuntimeConfig(event);
144
+ const url = getRequestURL(event);
145
+ const { path, originalPath } = getBootstrapPath(url, config.app.baseURL);
146
+ if (path.startsWith(joinURL(config.app.baseURL, "/__nuxt"))) {
144
147
  return;
145
148
  }
146
149
  if (path === "/favicon.ico") {
147
150
  return;
148
151
  }
149
- const config = useRuntimeConfig(event);
150
152
  const $storefrontConfig = config.storefront;
151
153
  if (path === `${$storefrontConfig.apiBasePath ?? "/api"}/up`) {
152
154
  return;
@@ -187,7 +189,7 @@ export default defineEventHandler(async (event) => {
187
189
  config
188
190
  );
189
191
  }
190
- if ($storefrontConfig.redirects?.enabled && !path.startsWith(apiBasePath) && originalPath !== "/__nuxt_error") {
192
+ if ($storefrontConfig.redirects?.enabled && !path.startsWith(apiBasePath) && originalPath !== joinURL(config.app.baseURL, "/__nuxt_error")) {
191
193
  await tracer.startActiveSpan(
192
194
  `storefront-nuxt.middleware/redirects`,
193
195
  {},
@@ -51,6 +51,9 @@ async function fetchRedirectWithCache(sourceUrl, storefrontAPIClient, redirectCa
51
51
  return sapiResult;
52
52
  }
53
53
  export async function useRedirects(event) {
54
+ if (!event.context.$rpcContext) {
55
+ return;
56
+ }
54
57
  const config = useRuntimeConfig();
55
58
  const $storefrontConfig = config.storefront;
56
59
  const options = $storefrontConfig.redirects;
package/dist/types.d.mts CHANGED
@@ -1,8 +1,9 @@
1
1
  import type { NuxtModule } from '@nuxt/schema'
2
2
 
3
- import type { default as Module, ModulePublicRuntimeConfig } from './module.js'
3
+ import type { default as Module, ModuleHooks, ModulePublicRuntimeConfig } from './module.js'
4
4
 
5
5
  declare module '@nuxt/schema' {
6
+ interface NuxtHooks extends ModuleHooks {}
6
7
  interface PublicRuntimeConfig extends ModulePublicRuntimeConfig {}
7
8
  }
8
9
 
package/dist/types.d.ts CHANGED
@@ -1,8 +1,9 @@
1
1
  import type { NuxtModule } from '@nuxt/schema'
2
2
 
3
- import type { default as Module, ModulePublicRuntimeConfig } from './module'
3
+ import type { default as Module, ModuleHooks, ModulePublicRuntimeConfig } from './module'
4
4
 
5
5
  declare module '@nuxt/schema' {
6
+ interface NuxtHooks extends ModuleHooks {}
6
7
  interface PublicRuntimeConfig extends ModulePublicRuntimeConfig {}
7
8
  }
8
9
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@scayle/storefront-nuxt",
3
3
  "type": "module",
4
- "version": "8.6.0",
4
+ "version": "8.7.0",
5
5
  "description": "Nuxt integration for the SCAYLE Commerce Engine and Storefront API",
6
6
  "author": "SCAYLE Commerce Engine",
7
7
  "license": "MIT",
@@ -90,27 +90,27 @@
90
90
  "schema-dts": "1.1.2"
91
91
  },
92
92
  "devDependencies": {
93
- "@nuxt/eslint": "0.7.5",
93
+ "@nuxt/eslint": "1.0.0",
94
94
  "@nuxt/kit": "3.14.1592",
95
95
  "@nuxt/module-builder": "0.8.4",
96
96
  "@nuxt/schema": "3.14.1592",
97
97
  "@nuxt/test-utils": "3.15.4",
98
98
  "@scayle/eslint-config-storefront": "4.4.1",
99
99
  "@scayle/eslint-plugin-vue-composable": "0.2.1",
100
- "@types/node": "22.10.10",
101
- "dprint": "0.48.0",
102
- "eslint": "9.18.0",
100
+ "@types/node": "22.13.1",
101
+ "dprint": "0.49.0",
102
+ "eslint": "9.19.0",
103
103
  "eslint-formatter-gitlab": "5.1.0",
104
- "fishery": "2.2.2",
104
+ "fishery": "2.2.3",
105
105
  "h3": "1.14.0",
106
106
  "nitropack": "2.9.7",
107
107
  "node-mocks-http": "1.16.2",
108
- "nuxi": "3.20.0",
108
+ "nuxi": "3.21.1",
109
109
  "nuxt": "3.14.1592",
110
110
  "publint": "0.2.12",
111
111
  "typescript": "5.7.3",
112
112
  "vue-tsc": "2.2.0",
113
- "vitest": "2.1.8"
113
+ "vitest": "2.1.9"
114
114
  },
115
115
  "peerDependencies": {
116
116
  "@nuxt/kit": "^3.12.2",
@@ -128,6 +128,6 @@
128
128
  "@nuxt/schema": "3.14.1592"
129
129
  },
130
130
  "volta": {
131
- "node": "22.13.0"
131
+ "node": "22.13.1"
132
132
  }
133
133
  }