@scayle/storefront-nuxt 7.48.1 → 7.49.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,24 @@
1
1
  # @scayle/storefront-nuxt
2
2
 
3
+ ## 7.49.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Support `Response` returns in RPC methods
8
+ - Handle 401 from CO and delete session
9
+
10
+ ### Patch Changes
11
+
12
+ - Updated dependencies
13
+ - @scayle/storefront-core@7.36.0
14
+
15
+ ## 7.48.2
16
+
17
+ ### Patch Changes
18
+
19
+ - Updated dependencies
20
+ - @scayle/storefront-core@7.35.0
21
+
3
22
  ## 7.48.1
4
23
 
5
24
  ### Patch Changes
package/dist/index.mjs CHANGED
@@ -1,2 +1,3 @@
1
1
  export { rpcCall } from './rpc.mjs';
2
2
  export * from '@scayle/storefront-core';
3
+ import '@scayle/storefront-core/dist/utils/response';
package/dist/module.json CHANGED
@@ -4,5 +4,5 @@
4
4
  "compatibility": {
5
5
  "nuxt": "^3.7.0"
6
6
  },
7
- "version": "7.48.0"
7
+ "version": "7.48.2"
8
8
  }
package/dist/rpc.d.mts CHANGED
@@ -9,6 +9,6 @@ import 'unstorage';
9
9
  * It makes an HTTP request on the client and direct call on the server using $fetch
10
10
  * https://nuxt.com/docs/api/utils/dollarfetch
11
11
  */
12
- declare const rpcCall: <N extends RpcMethodName, P extends RpcMethodParameters<N>, TResult extends Awaited<RpcMethodReturnType<N>>, TakesParameters = P extends RpcContext ? undefined : boolean>(nuxtApp: NuxtApp, method: N, shop: PublicShopConfig) => TakesParameters extends undefined ? () => Promise<TResult> : (params: P) => Promise<TResult>;
12
+ declare const rpcCall: <N extends RpcMethodName, P extends RpcMethodParameters<N>, TResult extends Exclude<Awaited<RpcMethodReturnType<N>>, Response>, TakesParameters = P extends RpcContext ? undefined : boolean>(nuxtApp: NuxtApp, method: N, shop: PublicShopConfig) => TakesParameters extends undefined ? () => Promise<TResult> : (params: P) => Promise<TResult>;
13
13
 
14
14
  export { rpcCall };
package/dist/rpc.d.ts CHANGED
@@ -9,6 +9,6 @@ import 'unstorage';
9
9
  * It makes an HTTP request on the client and direct call on the server using $fetch
10
10
  * https://nuxt.com/docs/api/utils/dollarfetch
11
11
  */
12
- declare const rpcCall: <N extends RpcMethodName, P extends RpcMethodParameters<N>, TResult extends Awaited<RpcMethodReturnType<N>>, TakesParameters = P extends RpcContext ? undefined : boolean>(nuxtApp: NuxtApp, method: N, shop: PublicShopConfig) => TakesParameters extends undefined ? () => Promise<TResult> : (params: P) => Promise<TResult>;
12
+ declare const rpcCall: <N extends RpcMethodName, P extends RpcMethodParameters<N>, TResult extends Exclude<Awaited<RpcMethodReturnType<N>>, Response>, TakesParameters = P extends RpcContext ? undefined : boolean>(nuxtApp: NuxtApp, method: N, shop: PublicShopConfig) => TakesParameters extends undefined ? () => Promise<TResult> : (params: P) => Promise<TResult>;
13
13
 
14
14
  export { rpcCall };
package/dist/rpc.mjs CHANGED
@@ -1,3 +1,5 @@
1
+ import { unwrap } from '@scayle/storefront-core/dist/utils/response';
2
+
1
3
  const rpcCall = (nuxtApp, method, shop) => {
2
4
  const fetch = nuxtApp.ssrContext?.event.$fetch ?? $fetch;
3
5
  return async (params = void 0) => {
@@ -11,7 +13,7 @@ const rpcCall = (nuxtApp, method, shop) => {
11
13
  "x-shop-id": shop.shopId.toString()
12
14
  }
13
15
  });
14
- return data;
16
+ return await unwrap(data);
15
17
  };
16
18
  };
17
19
 
@@ -7,7 +7,7 @@ export interface RpcOptions {
7
7
  lazy?: boolean;
8
8
  }
9
9
  export type Status = 'idle' | 'pending' | 'success' | 'error';
10
- export declare const useRpc: <N extends RpcMethodName, P extends RpcMethodParameters<N>, TResult extends Awaited<RpcMethodReturnType<N>>, TParams = P extends RpcContext ? undefined : P>(method: N, key: string, params?: any, options?: RpcOptions) => Promise<{
10
+ export declare const useRpc: <N extends RpcMethodName, P extends RpcMethodParameters<N>, TResult extends Exclude<Awaited<RpcMethodReturnType<N>>, Response>, TParams = P extends RpcContext ? undefined : P>(method: N, key: string, params?: any, options?: RpcOptions) => Promise<{
11
11
  data: import("vue").Ref<TResult>;
12
12
  fetching: import("vue").Ref<boolean>;
13
13
  fetch: () => Promise<void>;
@@ -1,3 +1,4 @@
1
+ import { unwrap } from "@scayle/storefront-core/dist/utils/response";
1
2
  import { useNuxtApp, useState, createError } from "nuxt/app";
2
3
  import { useCoreLog } from "../useCoreLog.mjs";
3
4
  import { resolveError } from "../../error/handler.mjs";
@@ -33,17 +34,19 @@ export const useRpc = async (method, key, params, options = { autoFetch: true, l
33
34
  fetching.value = true;
34
35
  const apiBasePath = currentShop.value?.apiBasePath ?? "/api";
35
36
  const fetch2 = getFetch(nuxtApp, log);
36
- data.value = await fetch2(`/rpc/${method}`, {
37
- // @ts-ignore
38
- method: "POST",
39
- body: {
40
- payload: toValue(params)
41
- },
42
- baseURL: apiBasePath,
43
- headers: {
44
- "x-shop-id": currentShop.value?.shopId + ""
45
- }
46
- });
37
+ data.value = await unwrap(
38
+ await fetch2(`/rpc/${method}`, {
39
+ // @ts-ignore
40
+ method: "POST",
41
+ body: {
42
+ payload: toValue(params)
43
+ },
44
+ baseURL: apiBasePath,
45
+ headers: {
46
+ "x-shop-id": currentShop.value?.shopId + ""
47
+ }
48
+ })
49
+ );
47
50
  } catch (e) {
48
51
  error.value = handleError(e);
49
52
  } finally {
@@ -8,7 +8,9 @@ export declare function useSession(): {
8
8
  revokeToken: () => Promise<{
9
9
  result: boolean;
10
10
  }>;
11
- forgetPassword: (params: import("@scayle/storefront-core").SendResetPasswordEmailRequest) => Promise<{
11
+ forgetPassword: (params: {
12
+ email: string;
13
+ }) => Promise<{
12
14
  success: boolean;
13
15
  }>;
14
16
  resetPasswordByHash: (params: import("utility-types").Optional<import("@scayle/storefront-core").UpdatePasswordByHashRequest, "shop_id">) => Promise<undefined>;
@@ -1,5 +1,5 @@
1
1
  declare const resolveError: (error: Error | unknown) => {
2
- statusCode: 401 | 500 | 400 | 100 | 101 | 102 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 226 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 421 | 422 | 423 | 424 | 426 | 428 | 429 | 431 | 451 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 510 | 511;
2
+ statusCode: 500 | 401 | 400 | 100 | 101 | 102 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 226 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 421 | 422 | 423 | 424 | 426 | 428 | 429 | 431 | 451 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 510 | 511;
3
3
  statusMessage: string;
4
4
  name: string;
5
5
  };
@@ -5,4 +5,4 @@ import type { RpcContext, RpcMethodName, RpcMethodParameters, RpcMethodReturnTyp
5
5
  * @param payload
6
6
  * @param rpcContext
7
7
  */
8
- export declare const handler: <N extends RpcMethodName, P extends RpcMethodParameters<N>, TResult extends Awaited<RpcMethodReturnType<N>>, TakesParameters = P extends RpcContext ? undefined : boolean, TParams = TakesParameters extends undefined ? null : P>(method: N, payload: TParams, rpcContext: RpcContext) => Promise<TResult>;
8
+ export declare const handler: <N extends RpcMethodName, P extends RpcMethodParameters<N>, TResult extends Awaited<RpcMethodReturnType<N>>, TakesParameters = P extends RpcContext ? undefined : boolean, TParams = TakesParameters extends undefined ? null : P>(method: N, payload: TParams, rpcContext: RpcContext) => Promise<TResult | Response>;
@@ -11,14 +11,24 @@ export const handler = async (method, payload, rpcContext) => {
11
11
  const log = rpcContext.log.space("sfc").space("rpc");
12
12
  return await log.time(`Calling RPC method: ${method}`, async () => {
13
13
  const fun = rpcMethods[method];
14
- if (fun.length === 1) {
14
+ try {
15
+ if (fun.length === 1) {
16
+ return await fun(
17
+ rpcContext
18
+ );
19
+ }
15
20
  return await fun(
21
+ payload,
16
22
  rpcContext
17
23
  );
24
+ } catch (e) {
25
+ if (e instanceof Error) {
26
+ return new Response(JSON.stringify({ message: e.message }), {
27
+ status: 500
28
+ });
29
+ } else {
30
+ return new Response(null, { status: 500 });
31
+ }
18
32
  }
19
- return await fun(
20
- payload,
21
- rpcContext
22
- );
23
33
  });
24
34
  };
@@ -6,4 +6,4 @@ import type { PublicShopConfig } from '../../types/module';
6
6
  * It makes an HTTP request on the client and direct call on the server using $fetch
7
7
  * https://nuxt.com/docs/api/utils/dollarfetch
8
8
  */
9
- export declare const rpcCall: <N extends RpcMethodName, P extends RpcMethodParameters<N>, TResult extends Awaited<RpcMethodReturnType<N>>, TakesParameters = P extends RpcContext ? undefined : boolean>(nuxtApp: NuxtApp, method: N, shop: PublicShopConfig) => TakesParameters extends undefined ? () => Promise<TResult> : (params: P) => Promise<TResult>;
9
+ export declare const rpcCall: <N extends RpcMethodName, P extends RpcMethodParameters<N>, TResult extends Exclude<Awaited<RpcMethodReturnType<N>>, Response>, TakesParameters = P extends RpcContext ? undefined : boolean>(nuxtApp: NuxtApp, method: N, shop: PublicShopConfig) => TakesParameters extends undefined ? () => Promise<TResult> : (params: P) => Promise<TResult>;
@@ -1,3 +1,4 @@
1
+ import { unwrap } from "@scayle/storefront-core/dist/utils/response";
1
2
  export const rpcCall = (nuxtApp, method, shop) => {
2
3
  const fetch = nuxtApp.ssrContext?.event.$fetch ?? $fetch;
3
4
  return async (params = void 0) => {
@@ -11,6 +12,6 @@ export const rpcCall = (nuxtApp, method, shop) => {
11
12
  "x-shop-id": shop.shopId.toString()
12
13
  }
13
14
  });
14
- return data;
15
+ return await unwrap(data);
15
16
  };
16
17
  };
@@ -1,5 +1,5 @@
1
1
  import { defineEventHandler, getRequestURL } from "h3";
2
- import { createRemoteJWKSet, decodeJwt } from "jose";
2
+ import { decodeJwt } from "jose";
3
3
  import { randomUUID } from "uncrypto";
4
4
  import { useSession } from "@scayle/h3-session";
5
5
  import { buildContext } from "../../context.mjs";
@@ -9,7 +9,6 @@ import {
9
9
  getCurrentShopForRequest,
10
10
  getPublicShopData
11
11
  } from "./bootstrap.utils.mjs";
12
- import { useOauth } from "./oauth.mjs";
13
12
  import { useRedirects } from "./redirects.mjs";
14
13
  import { useRuntimeConfig } from "#imports";
15
14
  const getShopConfig = (event, config) => {
@@ -20,26 +19,6 @@ const getShopConfig = (event, config) => {
20
19
  );
21
20
  return $shopConfig;
22
21
  };
23
- async function handleOauth(event, storefrontConfig, log) {
24
- if (!storefrontConfig.oauth.apiHost) {
25
- log.error("oauth.apiHost must be set in the module options.");
26
- return false;
27
- }
28
- if (!storefrontConfig.oauth.clientId) {
29
- log.error("oauth.clientId must be set in the module options.");
30
- return false;
31
- }
32
- if (!storefrontConfig.oauth.clientSecret) {
33
- log.error("oauth.clientSecret must be set in the module options.");
34
- return false;
35
- }
36
- const authURL = storefrontConfig.oauth.apiHost;
37
- const JWKS = createRemoteJWKSet(new URL("/.well-known/jwks.json", authURL), {
38
- cacheMaxAge: 5 * 60 * 1e3
39
- // Cache Keys for 5 minutes.
40
- });
41
- await useOauth(event, JWKS);
42
- }
43
22
  async function bootstrap(event, url, $shopConfig, $storefrontConfig, apiBasePath, config) {
44
23
  const log = event.context.$log;
45
24
  const bootstrapLog = log.space("bootstrap");
@@ -129,7 +108,6 @@ export default defineEventHandler(async (event) => {
129
108
  }
130
109
  const $shopConfig = getShopConfig(event, config);
131
110
  const apiBasePath = ($storefrontConfig.shopSelector === "path" && $shopConfig.path ? `/${$shopConfig.path}` : "") + ($shopConfig.apiBasePath || $storefrontConfig.apiBasePath || "/api");
132
- const log = event.context.$log;
133
111
  if (!event.context.$rpcContext) {
134
112
  await bootstrap(
135
113
  event,
@@ -143,7 +121,6 @@ export default defineEventHandler(async (event) => {
143
121
  if ($storefrontConfig.redirects?.enabled && !url.pathname.startsWith(apiBasePath)) {
144
122
  await useRedirects(event);
145
123
  }
146
- await handleOauth(event, $storefrontConfig, log);
147
124
  } catch (e) {
148
125
  console.error(e);
149
126
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@scayle/storefront-nuxt",
3
3
  "type": "module",
4
- "version": "7.48.1",
4
+ "version": "7.49.0",
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.8.2",
64
64
  "@scayle/h3-session": "0.3.4",
65
- "@scayle/storefront-core": "7.34.1",
65
+ "@scayle/storefront-core": "7.36.0",
66
66
  "@scayle/unstorage-compression-driver": "0.1.1",
67
67
  "@vueuse/core": "10.7.1",
68
68
  "consola": "3.2.3",
69
69
  "core-js": "3.35.0",
70
- "defu": "6.1.3",
70
+ "defu": "6.1.4",
71
71
  "h3": "1.9.0",
72
72
  "jose": "4.15.4",
73
73
  "knitwork": "1.0.0",
@@ -85,14 +85,14 @@
85
85
  "@nuxt/test-utils": "3.9.0",
86
86
  "@scayle/eslint-config-storefront": "3.2.6",
87
87
  "@scayle/prettier-config-storefront": "2.0.2",
88
- "@types/node": "20.10.6",
88
+ "@types/node": "20.10.7",
89
89
  "eslint": "8.56.0",
90
90
  "eslint-formatter-gitlab": "5.1.0",
91
91
  "node-mocks-http": "1.14.1",
92
92
  "nuxt": "3.8.2",
93
93
  "prettier": "3.0.0",
94
94
  "publint": "0.2.7",
95
- "vitest": "1.1.1",
95
+ "vitest": "1.1.3",
96
96
  "vue-tsc": "1.8.27"
97
97
  },
98
98
  "peerDependencies": {
@@ -1,3 +0,0 @@
1
- import { H3Event } from 'h3';
2
- import type { JWTVerifyGetKey } from 'jose';
3
- export declare function useOauth(event: H3Event, jwks: JWTVerifyGetKey): Promise<void>;
@@ -1,112 +0,0 @@
1
- import { errors as joseErrors, jwtVerify } from "jose";
2
- import { rpcMethods } from "@scayle/storefront-core";
3
- async function updateSession(event, data) {
4
- const session = event.context.session;
5
- Object.assign(session.data, data);
6
- await session.save();
7
- }
8
- const updateUser = async (event) => {
9
- const session = event.context.session;
10
- const user = await rpcMethods.fetchUser(
11
- { accessToken: session.data.accessToken, callback: "" },
12
- event.context.$rpcContext
13
- );
14
- await updateSession(event, {
15
- user
16
- });
17
- };
18
- const updateAccessTokenWithRefreshToken = async (event, sessionData, log) => {
19
- if (!sessionData?.refreshToken) {
20
- return false;
21
- }
22
- try {
23
- const context = event.context.$rpcContext;
24
- const { success } = await rpcMethods.refreshAccessToken(context);
25
- if (!success) {
26
- await revokeTokensAndDestroySession(event, log);
27
- return true;
28
- }
29
- try {
30
- await updateUser(event);
31
- return true;
32
- } catch (e) {
33
- log.error("api/oauth/me", {
34
- message: "fetching user failed",
35
- error: e
36
- });
37
- }
38
- } catch (e) {
39
- log.error("v1/oauth/token", {
40
- message: "refresh access token failed, so the local tokens were deleted",
41
- error: e
42
- });
43
- }
44
- return false;
45
- };
46
- const revokeTokensAndDestroySession = async (event, log) => {
47
- log.debug("revokeTokensAndDestroySession");
48
- const context = event.context.$rpcContext;
49
- try {
50
- await rpcMethods.oauthRevokeToken(context);
51
- } catch (e) {
52
- log.error("v1/oauth/tokens", {
53
- message: "error when revoking an access token",
54
- error: e
55
- });
56
- } finally {
57
- await context.destroySession();
58
- }
59
- };
60
- const updateSessionWithUser = async (event, sessionData, log) => {
61
- try {
62
- const user = await rpcMethods.fetchUser(
63
- { accessToken: sessionData.accessToken, callback: "" },
64
- event.context.$rpcContext
65
- );
66
- await updateSession(event, {
67
- user
68
- });
69
- } catch (e) {
70
- log.error("api/oauth/me", {
71
- message: "fetching user failed",
72
- error: e
73
- });
74
- }
75
- };
76
- export async function useOauth(event, jwks) {
77
- const context = event.context.$rpcContext;
78
- const log = event.context.$rpcContext.log.space("core").space("session");
79
- const sessionData = event.context.session.data;
80
- log.debug("validating oauth");
81
- if (!sessionData?.accessToken && !sessionData?.refreshToken) {
82
- log.debug("no tokens saved on session; skipping validation");
83
- return;
84
- }
85
- if (!sessionData?.accessToken) {
86
- log.debug("destroying session");
87
- await context.destroySession();
88
- return;
89
- }
90
- try {
91
- log.debug("validating existing session");
92
- await jwtVerify(sessionData.accessToken, jwks, { clockTolerance: 5 });
93
- if (!sessionData?.user) {
94
- log.debug("updating user session");
95
- await updateSessionWithUser(event, sessionData, log);
96
- }
97
- return;
98
- } catch (e) {
99
- log.debug("error validating session");
100
- if (e instanceof joseErrors.JWTExpired) {
101
- log.debug("token expired");
102
- const wasTokenSuccessfullyRefreshed = await updateAccessTokenWithRefreshToken(event, sessionData, log);
103
- if (wasTokenSuccessfullyRefreshed) {
104
- log.debug("token successfully refreshed");
105
- return;
106
- }
107
- }
108
- log.error("Could not verify JWT", e);
109
- }
110
- log.debug("revoking token and destroying session");
111
- await revokeTokensAndDestroySession(event, log);
112
- }