@scayle/storefront-nuxt 7.67.2 → 7.68.1

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,40 @@
1
1
  # @scayle/storefront-nuxt
2
2
 
3
+ ## 7.68.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies
8
+ - @scayle/storefront-core@7.49.4
9
+
10
+ ## 7.68.0
11
+
12
+ ### Minor Changes
13
+
14
+ - Use unique session cookie names for each shop
15
+
16
+ To simplify the implementation and improve the stability of session handling, we now use a differently named cookie for each shop instead of depending on the `Path` attribute.
17
+
18
+ This is a change only to the framework internals, and should not have any visible impact or require any code changes.
19
+
20
+ Before: `Set-Cookie: $session=s:fa3746f9-88c8-4065-a6c9-0c7bee473dd8.pSoaN6Q7iFHHyWKE7s9gQAqdDzGb9fS8a478P7PHLxw; Path=/de`
21
+
22
+ After: `Set-Cookie: $session-1001=s:fa3746f9-88c8-4065-a6c9-0c7bee473dd8.pSoaN6Q7iFHHyWKE7s9gQAqdDzGb9fS8a478P7PHLxw; Path=/`
23
+
24
+ When a cookie is found that uses the old format, it will be migrated to the new.
25
+
26
+ ### Patch Changes
27
+
28
+ - We stop bootstrapping requests where we can't determine a shop based on the request parameters.
29
+ Previously, we fell back to the first shop configuration, which has now been removed.
30
+
31
+ In this case, no `$rpcContext` is available during the request event.
32
+
33
+ We also bootstrap the Nuxt Error Page with the correct shop config now.
34
+
35
+ - Updated dependencies
36
+ - @scayle/h3-session@0.4.0
37
+
3
38
  ## 7.67.2
4
39
 
5
40
  ### Patch Changes
package/dist/module.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scayle/storefront-nuxt",
3
- "version": "7.67.1",
3
+ "version": "7.68.0",
4
4
  "configKey": "storefront",
5
5
  "compatibility": {
6
6
  "nuxt": "^3.9.0"
package/dist/module.mjs CHANGED
@@ -40,7 +40,7 @@ export default {
40
40
  }`;
41
41
  }
42
42
  const PACKAGE_NAME = "@scayle/storefront-nuxt";
43
- const PACKAGE_VERSION = "7.67.1";
43
+ const PACKAGE_VERSION = "7.68.0";
44
44
  const module = defineNuxtModule({
45
45
  meta: {
46
46
  name: PACKAGE_NAME,
@@ -1,24 +1,20 @@
1
- import { defineEventHandler, getRequestURL } from "h3";
1
+ import { defineEventHandler, getCookie, deleteCookie } from "h3";
2
2
  import { decodeJwt } from "jose";
3
3
  import { randomUUID } from "uncrypto";
4
- import { useSession } from "@scayle/h3-session";
4
+ import {
5
+ useSession,
6
+ unsignCookie
7
+ } from "@scayle/h3-session";
5
8
  import { buildContext } from "../../context.mjs";
6
9
  import { useCacheStorage, useSessionStorage } from "../utils/cacheStorage.mjs";
7
10
  import {
8
11
  convertStoresToList,
9
- getCurrentShopForRequest,
10
- getPublicShopData
12
+ getCurrentShopConfigForRequest,
13
+ getPublicShopData,
14
+ getBootstrapPath
11
15
  } from "./bootstrap.utils.mjs";
12
16
  import { useRedirects } from "./redirects.mjs";
13
17
  import { useRuntimeConfig } from "#imports";
14
- const getShopConfig = (event, config) => {
15
- const storefrontConfig = config.storefront;
16
- const $shopConfig = getCurrentShopForRequest(
17
- event,
18
- storefrontConfig
19
- );
20
- return $shopConfig;
21
- };
22
18
  const generateSessionId = (event) => {
23
19
  let userId;
24
20
  if (event?.context.session?.data?.accessToken) {
@@ -27,6 +23,16 @@ const generateSessionId = (event) => {
27
23
  }
28
24
  return userId ? `${userId}:${randomUUID()}` : randomUUID();
29
25
  };
26
+ async function getOldSessionData(event, cookieName, store, secret) {
27
+ const rawCookie = getCookie(event, cookieName);
28
+ const normalizedSecrets = Array.isArray(secret) ? secret : [secret];
29
+ const sessionId = rawCookie ? await unsignCookie(rawCookie, normalizedSecrets) : null;
30
+ if (sessionId) {
31
+ const sessionData = await store.get(sessionId);
32
+ await store.destroy(sessionId);
33
+ return sessionData;
34
+ }
35
+ }
30
36
  async function bootstrap(event, $shopConfig, $storefrontConfig, apiBasePath, config) {
31
37
  const log = event.context.$log;
32
38
  const bootstrapLog = log.space("bootstrap");
@@ -53,6 +59,14 @@ async function bootstrap(event, $shopConfig, $storefrontConfig, apiBasePath, con
53
59
  );
54
60
  bootstrapLog.debug("Attaching session to request");
55
61
  try {
62
+ const cookieName = sessionConfig?.cookieName ?? "$session";
63
+ const secret = sessionConfig?.secret ?? "current-secret";
64
+ const oldSessionData = await getOldSessionData(
65
+ event,
66
+ cookieName,
67
+ sessionStorage,
68
+ secret
69
+ );
56
70
  await useSession(event, {
57
71
  ...sessionConfig,
58
72
  store: sessionStorage,
@@ -62,12 +76,21 @@ async function bootstrap(event, $shopConfig, $storefrontConfig, apiBasePath, con
62
76
  maxAge: sessionConfig?.maxAge,
63
77
  // secure: sessionConfig.isHttps, TODO
64
78
  sameSite: sessionConfig.sameSite,
65
- domain: sessionConfig?.domain,
66
- path: $storefrontConfig.shopSelector === "path" && $shopConfig.path ? `/${$shopConfig.path}` : void 0
79
+ domain: sessionConfig?.domain
67
80
  },
68
- name: sessionConfig?.cookieName ?? "$session",
69
- secret: sessionConfig?.secret ?? "current-secret"
81
+ name: `${cookieName}-${$shopConfig.shopId}`,
82
+ secret
70
83
  });
84
+ if (oldSessionData) {
85
+ event.context.session.data = oldSessionData;
86
+ event.context.session.save();
87
+ }
88
+ const oldSessionCookie = getCookie(event, cookieName);
89
+ if (oldSessionCookie) {
90
+ deleteCookie(event, cookieName, {
91
+ path: $storefrontConfig.shopSelector === "path" && $shopConfig.path ? `/${$shopConfig.path}` : void 0
92
+ });
93
+ }
71
94
  } catch (e) {
72
95
  bootstrapLog.error("Error attaching session: ", e);
73
96
  }
@@ -102,11 +125,6 @@ async function bootstrap(event, $shopConfig, $storefrontConfig, apiBasePath, con
102
125
  event
103
126
  });
104
127
  }
105
- function getBootstrapPath(event) {
106
- const url = getRequestURL(event);
107
- const path = url.pathname === "/__nuxt_error" ? url.searchParams.get("url") ?? url.pathname : url.pathname;
108
- return { path, originalPath: url.pathname };
109
- }
110
128
  export default defineEventHandler(async (event) => {
111
129
  try {
112
130
  const { path, originalPath } = getBootstrapPath(event);
@@ -121,7 +139,17 @@ export default defineEventHandler(async (event) => {
121
139
  if (path === `${$storefrontConfig.apiBasePath ?? "/api"}/up`) {
122
140
  return;
123
141
  }
124
- const $shopConfig = getShopConfig(event, config);
142
+ const $shopConfig = getCurrentShopConfigForRequest(
143
+ event,
144
+ $storefrontConfig
145
+ );
146
+ if (!$shopConfig) {
147
+ event.context.$log.debug(
148
+ "Could not find shop config for this request",
149
+ path
150
+ );
151
+ return;
152
+ }
125
153
  const apiBasePath = ($storefrontConfig.shopSelector === "path" && $shopConfig.path ? `/${$shopConfig.path}` : "") + ($shopConfig.apiBasePath || $storefrontConfig.apiBasePath || "/api");
126
154
  if (!event.context.$rpcContext) {
127
155
  event.context.$log.debug("Bootstrapping request: " + path);
@@ -1,7 +1,13 @@
1
1
  import { type H3Event } from 'h3';
2
2
  import type { ModuleOptions, PublicShopConfig, ShopConfig, ShopConfigIndexed } from '../../../module';
3
+ type BootstrapPath = {
4
+ path: string;
5
+ originalPath: string;
6
+ };
7
+ export declare function getBootstrapPath(event: H3Event): BootstrapPath;
3
8
  export declare const convertStoresToList: (stores: ShopConfig[] | ShopConfigIndexed) => ShopConfig[];
4
- export declare function getCurrentShopForRequest(event: H3Event, config: Pick<ModuleOptions, 'stores' | 'shopSelector'>): ShopConfig;
9
+ export declare function getCurrentShopConfigForRequest(event: H3Event, config: Pick<ModuleOptions, 'stores' | 'shopSelector'>): ShopConfig | undefined;
5
10
  export declare function getPublicShopData(data: ShopConfig, currentShopId: number, apiBasePath: string, publicShopData?: (keyof ShopConfig)[]): PublicShopConfig & {
6
11
  isActive: boolean;
7
12
  };
13
+ export {};
@@ -1,18 +1,21 @@
1
- import { getRequestHeaders, getRequestHost } from "h3";
1
+ import {
2
+ getRequestHeaders,
3
+ getRequestHost,
4
+ getRequestURL
5
+ } from "h3";
2
6
  import { isArray, listify } from "radash";
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;
10
+ return { path, originalPath: url.pathname };
11
+ }
3
12
  const getStoreByDomain = (event, stores) => {
4
13
  const host = getRequestHost(event, { xForwardedHost: true });
5
14
  return stores.find((s) => s.domain === host);
6
15
  };
7
16
  const getStoreByPath = (event, stores) => {
8
- if (!event.path) {
9
- return;
10
- }
11
- const url = new URL(
12
- event.path,
13
- `https://${getRequestHost(event, { xForwardedHost: true })}`
14
- );
15
- const prefix = url.pathname.split("/")[1];
17
+ const { path } = getBootstrapPath(event);
18
+ const prefix = path.split("/")[1];
16
19
  return stores.find((shop) => prefix === shop.path);
17
20
  };
18
21
  export const convertStoresToList = (stores) => {
@@ -21,7 +24,7 @@ export const convertStoresToList = (stores) => {
21
24
  }
22
25
  return listify(stores, (_key, value) => ({ ...value }));
23
26
  };
24
- export function getCurrentShopForRequest(event, config) {
27
+ export function getCurrentShopConfigForRequest(event, config) {
25
28
  let $shopConfig;
26
29
  const { shopSelector, stores } = config;
27
30
  const storesList = convertStoresToList(stores);
@@ -34,10 +37,6 @@ export function getCurrentShopForRequest(event, config) {
34
37
  } else if (shopSelector === "path") {
35
38
  $shopConfig = getStoreByPath(event, storesList);
36
39
  }
37
- $shopConfig = $shopConfig || storesList[0];
38
- if (!$shopConfig) {
39
- throw new Error("Could not find shop config");
40
- }
41
40
  return $shopConfig;
42
41
  }
43
42
  export function getPublicShopData(data, currentShopId, apiBasePath, publicShopData = []) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@scayle/storefront-nuxt",
3
3
  "type": "module",
4
- "version": "7.67.2",
4
+ "version": "7.68.1",
5
5
  "description": "Nuxt integration for the SCAYLE Commerce Engine and Storefront API",
6
6
  "author": "SCAYLE Commerce Engine",
7
7
  "license": "MIT",
@@ -62,12 +62,12 @@
62
62
  "dependencies": {
63
63
  "@nuxt/kit": "3.11.1",
64
64
  "@opentelemetry/api": "1.8.0",
65
- "@scayle/h3-session": "0.3.6",
66
- "@scayle/storefront-core": "7.49.3",
65
+ "@scayle/h3-session": "0.4.0",
66
+ "@scayle/storefront-core": "7.49.4",
67
67
  "@scayle/unstorage-compression-driver": "0.1.3",
68
68
  "@vueuse/core": "10.9.0",
69
69
  "consola": "3.2.3",
70
- "core-js": "3.37.0",
70
+ "core-js": "3.37.1",
71
71
  "defu": "6.1.4",
72
72
  "jose": "^5.2.0",
73
73
  "knitwork": "1.1.0",
@@ -80,23 +80,23 @@
80
80
  "yn": "5.0.0"
81
81
  },
82
82
  "devDependencies": {
83
- "@nuxt/eslint": "0.3.10",
83
+ "@nuxt/eslint": "0.3.12",
84
84
  "@nuxt/module-builder": "0.5.5",
85
85
  "@nuxt/schema": "3.11.1",
86
86
  "@nuxt/test-utils": "3.12.1",
87
87
  "@scayle/eslint-config-storefront": "4.1.0",
88
88
  "@scayle/eslint-plugin-vue-composable": "0.1.1",
89
- "@types/node": "20.12.7",
89
+ "@types/node": "20.12.12",
90
90
  "dprint": "0.45.1",
91
- "eslint": "9.1.1",
91
+ "eslint": "9.2.0",
92
92
  "eslint-formatter-gitlab": "5.1.0",
93
93
  "h3": "1.11.1",
94
94
  "node-mocks-http": "1.14.1",
95
95
  "nuxi": "3.11.1",
96
96
  "nuxt": "3.11.1",
97
97
  "publint": "0.2.7",
98
- "vitest": "1.5.2",
99
- "vue-tsc": "2.0.15"
98
+ "vitest": "1.6.0",
99
+ "vue-tsc": "2.0.17"
100
100
  },
101
101
  "peerDependencies": {
102
102
  "h3": "^1.10.0",
@@ -111,6 +111,6 @@
111
111
  "@nuxt/schema": "3.11.1"
112
112
  },
113
113
  "volta": {
114
- "node": "20.12.2"
114
+ "node": "20.13.1"
115
115
  }
116
116
  }