@wix/sdk 1.14.4 → 1.15.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.
@@ -17,7 +17,7 @@ export type AppStrategy = AuthenticationStrategy<undefined> & {
17
17
  */
18
18
  elevated(): Promise<AppStrategy>;
19
19
  /**
20
- * Returns infromation about the active token
20
+ * Returns information about the active token
21
21
  */
22
22
  getTokenInfo(): Promise<{
23
23
  active: boolean;
@@ -73,7 +73,6 @@ export declare function AppStrategy(opts: {
73
73
  appId: string;
74
74
  appSecret?: string;
75
75
  publicKey?: string;
76
- authServerBaseUrl?: string;
77
76
  } & ({
78
77
  refreshToken?: string;
79
78
  } | {
@@ -81,3 +80,13 @@ export declare function AppStrategy(opts: {
81
80
  } | {
82
81
  accessToken?: string;
83
82
  })): AppStrategy;
83
+ export declare function getTokenInfo(token: string): Promise<{
84
+ active: boolean;
85
+ subjectType: 'APP' | 'USER' | 'MEMBER' | 'VISITOR' | 'UNKNOWN';
86
+ subjectId: string;
87
+ exp: number;
88
+ iat: number;
89
+ clientId?: string;
90
+ siteId: string;
91
+ instanceId?: string;
92
+ }>;
@@ -40,7 +40,6 @@ import { parsePublicKeyIfEncoded } from '../helpers.js';
40
40
  */
41
41
  // eslint-disable-next-line @typescript-eslint/no-redeclare
42
42
  export function AppStrategy(opts) {
43
- const authServerBaseUrl = opts.authServerBaseUrl ?? 'https://www.wixapis.com';
44
43
  let refreshToken = 'refreshToken' in opts ? opts.refreshToken : undefined;
45
44
  let cachedToken;
46
45
  return {
@@ -70,8 +69,7 @@ export function AppStrategy(opts) {
70
69
  if (!code || !instanceId) {
71
70
  throw new Error('Invalid OAuth callback URL. Make sure you pass the url including the code and instanceId query params.');
72
71
  }
73
- const tokenUrl = new URL('/oauth/access', authServerBaseUrl);
74
- const tokensRes = await fetch(tokenUrl.href, {
72
+ const tokensRes = await fetch('https://www.wixapis.com/oauth/access', {
75
73
  method: 'POST',
76
74
  headers: {
77
75
  'Content-Type': 'application/json',
@@ -104,10 +102,9 @@ export function AppStrategy(opts) {
104
102
  }
105
103
  if ('refreshToken' in opts || refreshToken) {
106
104
  if (!opts.appSecret) {
107
- throw new Error('App secret is required for retrieveing app-level access tokens. Make sure to pass it to the AppStrategy');
105
+ throw new Error('App secret is required for retrieving app-level access tokens. Make sure to pass it to the AppStrategy');
108
106
  }
109
- const tokenUrl = new URL('/oauth/access', authServerBaseUrl);
110
- const tokensRes = await fetch(tokenUrl.href, {
107
+ const tokensRes = await fetch('https://www.wixapis.com/oauth/access', {
111
108
  method: 'POST',
112
109
  headers: {
113
110
  'Content-Type': 'application/json',
@@ -132,10 +129,9 @@ export function AppStrategy(opts) {
132
129
  }
133
130
  else if ('instanceId' in opts) {
134
131
  if (!opts.appSecret) {
135
- throw new Error('App secret is required for retrieveing app-level access tokens. Make sure to pass it to the AppStrategy');
132
+ throw new Error('App secret is required for retrieving app-level access tokens. Make sure to pass it to the AppStrategy');
136
133
  }
137
- const tokenUrl = new URL('/oauth2/token', authServerBaseUrl);
138
- const tokensRes = await fetch(tokenUrl.href, {
134
+ const tokensRes = await fetch('https://www.wixapis.com/oauth2/token', {
139
135
  method: 'POST',
140
136
  headers: {
141
137
  'Content-Type': 'application/json',
@@ -162,7 +158,7 @@ export function AppStrategy(opts) {
162
158
  };
163
159
  }
164
160
  else if ('accessToken' in opts && opts.accessToken) {
165
- const tokenRes = await fetch(new URL('/oauth2/token', authServerBaseUrl), {
161
+ const tokenRes = await fetch('https://www.wixapis.com/oauth2/token', {
166
162
  method: 'POST',
167
163
  headers: {
168
164
  'Content-Type': 'application/json',
@@ -193,7 +189,7 @@ export function AppStrategy(opts) {
193
189
  },
194
190
  async elevated() {
195
191
  if ('accessToken' in opts && opts.accessToken) {
196
- const tokenInfo = await getTokenInfo(opts.accessToken, authServerBaseUrl);
192
+ const tokenInfo = await getTokenInfo(opts.accessToken);
197
193
  if (tokenInfo.clientId !== opts.appId) {
198
194
  throw new Error(`Invalid access token. The token is not issued for the app with ID "${opts.appId}"`);
199
195
  }
@@ -205,7 +201,6 @@ export function AppStrategy(opts) {
205
201
  appSecret: opts.appSecret,
206
202
  publicKey: opts.publicKey,
207
203
  instanceId: tokenInfo.instanceId,
208
- authServerBaseUrl: opts.authServerBaseUrl,
209
204
  });
210
205
  }
211
206
  else {
@@ -236,16 +231,15 @@ export function AppStrategy(opts) {
236
231
  if (!tokenToCheck) {
237
232
  throw new Error('Missing token to get info for. Either pass the token as an argument or provide it when initializing the AppStrategy');
238
233
  }
239
- return getTokenInfo(tokenToCheck, authServerBaseUrl);
234
+ return getTokenInfo(tokenToCheck);
240
235
  },
241
236
  getActiveToken() {
242
237
  return 'accessToken' in opts ? opts.accessToken : refreshToken;
243
238
  },
244
239
  };
245
240
  }
246
- async function getTokenInfo(token, authServerBaseUrl) {
247
- const tokenInfoUrl = new URL('/oauth2/token-info', authServerBaseUrl);
248
- const tokenInfoRes = await fetch(tokenInfoUrl.href, {
241
+ export async function getTokenInfo(token) {
242
+ const tokenInfoRes = await fetch('https://www.wixapis.com/oauth2/token-info', {
249
243
  method: 'POST',
250
244
  headers: {
251
245
  'Content-Type': 'application/json',
@@ -0,0 +1,35 @@
1
+ export declare const isWebMethodModules: (val: any) => val is WebMethodModules;
2
+ export type WebMethodClient = {
3
+ processRequest(request: Request, devMode?: boolean): Promise<Response>;
4
+ callWebMethod(options: {
5
+ filename: string;
6
+ method: string;
7
+ args: unknown[];
8
+ baseURL: string;
9
+ }): Promise<unknown>;
10
+ };
11
+ declare enum Permissions {
12
+ Anyone = "anyone",
13
+ Admin = "admin",
14
+ SiteMember = "site-member"
15
+ }
16
+ type WebMethod<T extends unknown[], R> = {
17
+ handler: (...args: T) => R;
18
+ permission: Permissions;
19
+ __type: 'web-method-module';
20
+ };
21
+ type WebMethodModule = Record<string, WebMethod<unknown[], unknown>>;
22
+ export type WebMethodModules = Record<string, () => Promise<WebMethodModule>>;
23
+ export declare function webMethodModules(fetchWithAuth: (urlOrRequest: string | URL | Request, requestInit?: RequestInit) => Promise<Response>): {
24
+ initModule(webMethodModule: WebMethodModules): void;
25
+ client: {
26
+ processRequest(request: Request, devMode?: boolean | undefined): Promise<Response>;
27
+ callWebMethod({ baseURL, filename, method, args }: {
28
+ filename: string;
29
+ method: string;
30
+ args: unknown[];
31
+ baseURL: string;
32
+ }): Promise<any>;
33
+ };
34
+ };
35
+ export {};
@@ -0,0 +1,112 @@
1
+ import { serializeError } from 'serialize-error';
2
+ import { getTokenInfo } from './auth/AppStrategy.js';
3
+ export const isWebMethodModules = (val) => val.__type === 'web-method-module';
4
+ var Permissions;
5
+ (function (Permissions) {
6
+ Permissions["Anyone"] = "anyone";
7
+ Permissions["Admin"] = "admin";
8
+ Permissions["SiteMember"] = "site-member";
9
+ })(Permissions || (Permissions = {}));
10
+ async function checkPermission(request, permission) {
11
+ const accessToken = request.headers.get('Authorization');
12
+ if (!accessToken) {
13
+ throw new Error('Request is missing authentication data');
14
+ }
15
+ const { subjectType } = await getTokenInfo(accessToken);
16
+ switch (permission) {
17
+ case Permissions.Anyone: {
18
+ if (subjectType !== 'VISITOR' &&
19
+ subjectType !== 'MEMBER' &&
20
+ subjectType !== 'USER') {
21
+ throw new Error('Insufficient permissions');
22
+ }
23
+ break;
24
+ }
25
+ case Permissions.SiteMember: {
26
+ if (subjectType !== 'MEMBER' && subjectType !== 'USER') {
27
+ throw new Error('Insufficient permissions');
28
+ }
29
+ break;
30
+ }
31
+ case Permissions.Admin: {
32
+ if (subjectType !== 'USER') {
33
+ throw new Error('Insufficient permissions');
34
+ }
35
+ break;
36
+ }
37
+ }
38
+ }
39
+ const urlRegex = /\/_webMethods\/(.+\..+)\/(.+\..+)/;
40
+ // /_webMethods/backend/my-module.web.js/multiply.ajax
41
+ function extractUrlParts(url) {
42
+ const parts = url.match(urlRegex);
43
+ if (parts) {
44
+ return [parts[1], parts[2].replace('.ajax', '')];
45
+ }
46
+ }
47
+ function isRequestBodyValid(body) {
48
+ return !!body && typeof body === 'object' && Array.isArray(body);
49
+ }
50
+ const productionErrorMessage = 'Error: Unable to handle the request. Contact the site administrator or view site monitoring logs for more information.';
51
+ export function webMethodModules(fetchWithAuth) {
52
+ const webMethods = {};
53
+ const client = {
54
+ async processRequest(request, devMode = false) {
55
+ const urlParts = extractUrlParts(request.url);
56
+ if (!urlParts) {
57
+ return new Response('invalid request', { status: 400 });
58
+ }
59
+ const [file, method] = urlParts;
60
+ const body = (await request.json());
61
+ if (!isRequestBodyValid(body)) {
62
+ return new Response('invalid request', { status: 400 });
63
+ }
64
+ const loadWebMethodFile = webMethods[`/${file}`];
65
+ try {
66
+ if (!loadWebMethodFile) {
67
+ throw new Error(`Error loading web module ${file}: Cannot find module '${file}'`);
68
+ }
69
+ const webMethodFile = await loadWebMethodFile();
70
+ const webMethod = webMethodFile[method];
71
+ if (!webMethod) {
72
+ throw new Error(`Error loading function from web module ${file}: function '${method}' not found`);
73
+ }
74
+ await checkPermission(request, webMethod.permission);
75
+ return Response.json({
76
+ result: await webMethod.handler(...body),
77
+ });
78
+ }
79
+ catch (error) {
80
+ const serializedError = serializeError(error, { maxDepth: 1 });
81
+ return Response.json({
82
+ result: devMode || !(error instanceof Error)
83
+ ? serializedError
84
+ : {
85
+ ...serializedError,
86
+ message: productionErrorMessage,
87
+ stack: productionErrorMessage,
88
+ },
89
+ exception: true,
90
+ });
91
+ }
92
+ },
93
+ async callWebMethod({ baseURL, filename, method, args }) {
94
+ const response = await fetchWithAuth(`${baseURL}/_webMethods/${filename}/${method}.ajax`, {
95
+ body: JSON.stringify(args),
96
+ method: 'POST',
97
+ });
98
+ const json = await response.json();
99
+ // request failed
100
+ if (json.exception === true) {
101
+ throw json.result;
102
+ }
103
+ return json.result;
104
+ },
105
+ };
106
+ return {
107
+ initModule(webMethodModule) {
108
+ Object.assign(webMethods, webMethodModule);
109
+ },
110
+ client,
111
+ };
112
+ }
@@ -3,6 +3,7 @@ import { EmptyObject } from 'type-fest/source/empty-object.js';
3
3
  import type { GraphQLFormattedError } from 'graphql';
4
4
  import { EventHandlersClient } from './event-handlers-modules.js';
5
5
  import { ServicePluginsClient } from './service-plugin-modules.js';
6
+ import { WebMethodClient, WebMethodModules } from './web-method-modules.js';
6
7
  export type ContextType = 'global' | 'module';
7
8
  type Headers = Record<string, string>;
8
9
  /**
@@ -46,11 +47,13 @@ export type WixClient<H extends Host<any> | undefined = undefined, Z extends Aut
46
47
  }>;
47
48
  webhooks: EventHandlersClient;
48
49
  servicePlugins: ServicePluginsClient;
50
+ webMethods: WebMethodClient;
49
51
  } & BuildDescriptors<T, H>;
50
52
  export declare function createClient<H extends Host<any> | undefined = undefined, Z extends AuthenticationStrategy<H> = AuthenticationStrategy<H>, T extends Descriptors = EmptyObject>(config: {
51
53
  modules?: H extends Host<any> ? AssertHostMatches<T, H> : T;
52
54
  auth?: Z;
53
55
  headers?: Headers;
54
56
  host?: H;
57
+ webMethods?: WebMethodModules;
55
58
  }): WixClient<H, Z, T>;
56
59
  export {};
@@ -9,6 +9,7 @@ import { buildRESTDescriptor } from './rest-modules.js';
9
9
  import { eventHandlersModules, isEventHandlerModule, } from './event-handlers-modules.js';
10
10
  import { isServicePluginModule, servicePluginsModules, } from './service-plugin-modules.js';
11
11
  import { runWithoutContext } from '@wix/sdk-runtime/context';
12
+ import { isWebMethodModules, webMethodModules, } from './web-method-modules.js';
12
13
  export function createClient(config) {
13
14
  const _headers = config.headers || { Authorization: '' };
14
15
  const authStrategy = config.auth ||
@@ -17,8 +18,26 @@ export function createClient(config) {
17
18
  };
18
19
  const boundGetAuthHeaders = authStrategy.getAuthHeaders.bind(undefined, config.host);
19
20
  authStrategy.getAuthHeaders = boundGetAuthHeaders;
21
+ const fetchWithAuth = async (urlOrRequest, requestInit) => {
22
+ if (typeof urlOrRequest === 'string' || urlOrRequest instanceof URL) {
23
+ return fetch(urlOrRequest, {
24
+ ...requestInit,
25
+ headers: {
26
+ ...requestInit?.headers,
27
+ ...(await boundGetAuthHeaders()).headers,
28
+ },
29
+ });
30
+ }
31
+ else {
32
+ for (const [k, v] of Object.entries((await boundGetAuthHeaders()).headers)) {
33
+ urlOrRequest.headers.set(k, v);
34
+ }
35
+ return fetch(urlOrRequest, requestInit);
36
+ }
37
+ };
20
38
  const { client: servicePluginsClient, initModule: initServicePluginModule } = servicePluginsModules(authStrategy);
21
39
  const { client: eventHandlersClient, initModule: initEventHandlerModule } = eventHandlersModules(authStrategy);
40
+ const { client: webMethodClient, initModule } = webMethodModules(fetchWithAuth);
22
41
  const boundFetch = async (url, options) => {
23
42
  const authHeaders = await boundGetAuthHeaders();
24
43
  const defaultContentTypeHeader = getDefaultContentHeader(options);
@@ -46,6 +65,9 @@ export function createClient(config) {
46
65
  else if (isHostModule(modules) && config.host) {
47
66
  return buildHostModule(modules, config.host);
48
67
  }
68
+ else if (isWebMethodModules(modules)) {
69
+ return initModule(modules);
70
+ }
49
71
  else if (typeof modules === 'function') {
50
72
  // The generated namespaces all have the error classes on them and
51
73
  // a class is also a function, so we need to explicitly ignore these
@@ -126,23 +148,7 @@ export function createClient(config) {
126
148
  finalUrl.protocol = 'https';
127
149
  return boundFetch(finalUrl.toString(), options);
128
150
  },
129
- fetchWithAuth: async (urlOrRequest, requestInit) => {
130
- if (typeof urlOrRequest === 'string' || urlOrRequest instanceof URL) {
131
- return fetch(urlOrRequest, {
132
- ...requestInit,
133
- headers: {
134
- ...requestInit?.headers,
135
- ...(await boundGetAuthHeaders()).headers,
136
- },
137
- });
138
- }
139
- else {
140
- for (const [k, v] of Object.entries((await boundGetAuthHeaders()).headers)) {
141
- urlOrRequest.headers.set(k, v);
142
- }
143
- return fetch(urlOrRequest, requestInit);
144
- }
145
- },
151
+ fetchWithAuth,
146
152
  async graphql(query, variables, opts = {
147
153
  apiVersion: 'alpha',
148
154
  }) {
@@ -161,6 +167,7 @@ export function createClient(config) {
161
167
  return { data: data ?? {}, errors };
162
168
  },
163
169
  webhooks: eventHandlersClient,
170
+ webMethods: webMethodClient,
164
171
  servicePlugins: servicePluginsClient,
165
172
  };
166
173
  }
@@ -17,7 +17,7 @@ export type AppStrategy = AuthenticationStrategy<undefined> & {
17
17
  */
18
18
  elevated(): Promise<AppStrategy>;
19
19
  /**
20
- * Returns infromation about the active token
20
+ * Returns information about the active token
21
21
  */
22
22
  getTokenInfo(): Promise<{
23
23
  active: boolean;
@@ -73,7 +73,6 @@ export declare function AppStrategy(opts: {
73
73
  appId: string;
74
74
  appSecret?: string;
75
75
  publicKey?: string;
76
- authServerBaseUrl?: string;
77
76
  } & ({
78
77
  refreshToken?: string;
79
78
  } | {
@@ -81,3 +80,13 @@ export declare function AppStrategy(opts: {
81
80
  } | {
82
81
  accessToken?: string;
83
82
  })): AppStrategy;
83
+ export declare function getTokenInfo(token: string): Promise<{
84
+ active: boolean;
85
+ subjectType: 'APP' | 'USER' | 'MEMBER' | 'VISITOR' | 'UNKNOWN';
86
+ subjectId: string;
87
+ exp: number;
88
+ iat: number;
89
+ clientId?: string;
90
+ siteId: string;
91
+ instanceId?: string;
92
+ }>;
@@ -15,15 +15,26 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
15
15
  }) : function(o, v) {
16
16
  o["default"] = v;
17
17
  });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
25
35
  Object.defineProperty(exports, "__esModule", { value: true });
26
36
  exports.AppStrategy = AppStrategy;
37
+ exports.getTokenInfo = getTokenInfo;
27
38
  const helpers_js_1 = require("../helpers.js");
28
39
  /**
29
40
  * Creates an authentication strategy for Wix Apps OAuth installation process.
@@ -66,7 +77,6 @@ const helpers_js_1 = require("../helpers.js");
66
77
  */
67
78
  // eslint-disable-next-line @typescript-eslint/no-redeclare
68
79
  function AppStrategy(opts) {
69
- const authServerBaseUrl = opts.authServerBaseUrl ?? 'https://www.wixapis.com';
70
80
  let refreshToken = 'refreshToken' in opts ? opts.refreshToken : undefined;
71
81
  let cachedToken;
72
82
  return {
@@ -96,8 +106,7 @@ function AppStrategy(opts) {
96
106
  if (!code || !instanceId) {
97
107
  throw new Error('Invalid OAuth callback URL. Make sure you pass the url including the code and instanceId query params.');
98
108
  }
99
- const tokenUrl = new URL('/oauth/access', authServerBaseUrl);
100
- const tokensRes = await fetch(tokenUrl.href, {
109
+ const tokensRes = await fetch('https://www.wixapis.com/oauth/access', {
101
110
  method: 'POST',
102
111
  headers: {
103
112
  'Content-Type': 'application/json',
@@ -130,10 +139,9 @@ function AppStrategy(opts) {
130
139
  }
131
140
  if ('refreshToken' in opts || refreshToken) {
132
141
  if (!opts.appSecret) {
133
- throw new Error('App secret is required for retrieveing app-level access tokens. Make sure to pass it to the AppStrategy');
142
+ throw new Error('App secret is required for retrieving app-level access tokens. Make sure to pass it to the AppStrategy');
134
143
  }
135
- const tokenUrl = new URL('/oauth/access', authServerBaseUrl);
136
- const tokensRes = await fetch(tokenUrl.href, {
144
+ const tokensRes = await fetch('https://www.wixapis.com/oauth/access', {
137
145
  method: 'POST',
138
146
  headers: {
139
147
  'Content-Type': 'application/json',
@@ -158,10 +166,9 @@ function AppStrategy(opts) {
158
166
  }
159
167
  else if ('instanceId' in opts) {
160
168
  if (!opts.appSecret) {
161
- throw new Error('App secret is required for retrieveing app-level access tokens. Make sure to pass it to the AppStrategy');
169
+ throw new Error('App secret is required for retrieving app-level access tokens. Make sure to pass it to the AppStrategy');
162
170
  }
163
- const tokenUrl = new URL('/oauth2/token', authServerBaseUrl);
164
- const tokensRes = await fetch(tokenUrl.href, {
171
+ const tokensRes = await fetch('https://www.wixapis.com/oauth2/token', {
165
172
  method: 'POST',
166
173
  headers: {
167
174
  'Content-Type': 'application/json',
@@ -188,7 +195,7 @@ function AppStrategy(opts) {
188
195
  };
189
196
  }
190
197
  else if ('accessToken' in opts && opts.accessToken) {
191
- const tokenRes = await fetch(new URL('/oauth2/token', authServerBaseUrl), {
198
+ const tokenRes = await fetch('https://www.wixapis.com/oauth2/token', {
192
199
  method: 'POST',
193
200
  headers: {
194
201
  'Content-Type': 'application/json',
@@ -219,7 +226,7 @@ function AppStrategy(opts) {
219
226
  },
220
227
  async elevated() {
221
228
  if ('accessToken' in opts && opts.accessToken) {
222
- const tokenInfo = await getTokenInfo(opts.accessToken, authServerBaseUrl);
229
+ const tokenInfo = await getTokenInfo(opts.accessToken);
223
230
  if (tokenInfo.clientId !== opts.appId) {
224
231
  throw new Error(`Invalid access token. The token is not issued for the app with ID "${opts.appId}"`);
225
232
  }
@@ -231,7 +238,6 @@ function AppStrategy(opts) {
231
238
  appSecret: opts.appSecret,
232
239
  publicKey: opts.publicKey,
233
240
  instanceId: tokenInfo.instanceId,
234
- authServerBaseUrl: opts.authServerBaseUrl,
235
241
  });
236
242
  }
237
243
  else {
@@ -262,16 +268,15 @@ function AppStrategy(opts) {
262
268
  if (!tokenToCheck) {
263
269
  throw new Error('Missing token to get info for. Either pass the token as an argument or provide it when initializing the AppStrategy');
264
270
  }
265
- return getTokenInfo(tokenToCheck, authServerBaseUrl);
271
+ return getTokenInfo(tokenToCheck);
266
272
  },
267
273
  getActiveToken() {
268
274
  return 'accessToken' in opts ? opts.accessToken : refreshToken;
269
275
  },
270
276
  };
271
277
  }
272
- async function getTokenInfo(token, authServerBaseUrl) {
273
- const tokenInfoUrl = new URL('/oauth2/token-info', authServerBaseUrl);
274
- const tokenInfoRes = await fetch(tokenInfoUrl.href, {
278
+ async function getTokenInfo(token) {
279
+ const tokenInfoRes = await fetch('https://www.wixapis.com/oauth2/token-info', {
275
280
  method: 'POST',
276
281
  headers: {
277
282
  'Content-Type': 'application/json',
@@ -0,0 +1,35 @@
1
+ export declare const isWebMethodModules: (val: any) => val is WebMethodModules;
2
+ export type WebMethodClient = {
3
+ processRequest(request: Request, devMode?: boolean): Promise<Response>;
4
+ callWebMethod(options: {
5
+ filename: string;
6
+ method: string;
7
+ args: unknown[];
8
+ baseURL: string;
9
+ }): Promise<unknown>;
10
+ };
11
+ declare enum Permissions {
12
+ Anyone = "anyone",
13
+ Admin = "admin",
14
+ SiteMember = "site-member"
15
+ }
16
+ type WebMethod<T extends unknown[], R> = {
17
+ handler: (...args: T) => R;
18
+ permission: Permissions;
19
+ __type: 'web-method-module';
20
+ };
21
+ type WebMethodModule = Record<string, WebMethod<unknown[], unknown>>;
22
+ export type WebMethodModules = Record<string, () => Promise<WebMethodModule>>;
23
+ export declare function webMethodModules(fetchWithAuth: (urlOrRequest: string | URL | Request, requestInit?: RequestInit) => Promise<Response>): {
24
+ initModule(webMethodModule: WebMethodModules): void;
25
+ client: {
26
+ processRequest(request: Request, devMode?: boolean | undefined): Promise<Response>;
27
+ callWebMethod({ baseURL, filename, method, args }: {
28
+ filename: string;
29
+ method: string;
30
+ args: unknown[];
31
+ baseURL: string;
32
+ }): Promise<any>;
33
+ };
34
+ };
35
+ export {};
@@ -0,0 +1,117 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isWebMethodModules = void 0;
4
+ exports.webMethodModules = webMethodModules;
5
+ const serialize_error_1 = require("serialize-error");
6
+ const AppStrategy_js_1 = require("./auth/AppStrategy.js");
7
+ const isWebMethodModules = (val) => val.__type === 'web-method-module';
8
+ exports.isWebMethodModules = isWebMethodModules;
9
+ var Permissions;
10
+ (function (Permissions) {
11
+ Permissions["Anyone"] = "anyone";
12
+ Permissions["Admin"] = "admin";
13
+ Permissions["SiteMember"] = "site-member";
14
+ })(Permissions || (Permissions = {}));
15
+ async function checkPermission(request, permission) {
16
+ const accessToken = request.headers.get('Authorization');
17
+ if (!accessToken) {
18
+ throw new Error('Request is missing authentication data');
19
+ }
20
+ const { subjectType } = await (0, AppStrategy_js_1.getTokenInfo)(accessToken);
21
+ switch (permission) {
22
+ case Permissions.Anyone: {
23
+ if (subjectType !== 'VISITOR' &&
24
+ subjectType !== 'MEMBER' &&
25
+ subjectType !== 'USER') {
26
+ throw new Error('Insufficient permissions');
27
+ }
28
+ break;
29
+ }
30
+ case Permissions.SiteMember: {
31
+ if (subjectType !== 'MEMBER' && subjectType !== 'USER') {
32
+ throw new Error('Insufficient permissions');
33
+ }
34
+ break;
35
+ }
36
+ case Permissions.Admin: {
37
+ if (subjectType !== 'USER') {
38
+ throw new Error('Insufficient permissions');
39
+ }
40
+ break;
41
+ }
42
+ }
43
+ }
44
+ const urlRegex = /\/_webMethods\/(.+\..+)\/(.+\..+)/;
45
+ // /_webMethods/backend/my-module.web.js/multiply.ajax
46
+ function extractUrlParts(url) {
47
+ const parts = url.match(urlRegex);
48
+ if (parts) {
49
+ return [parts[1], parts[2].replace('.ajax', '')];
50
+ }
51
+ }
52
+ function isRequestBodyValid(body) {
53
+ return !!body && typeof body === 'object' && Array.isArray(body);
54
+ }
55
+ const productionErrorMessage = 'Error: Unable to handle the request. Contact the site administrator or view site monitoring logs for more information.';
56
+ function webMethodModules(fetchWithAuth) {
57
+ const webMethods = {};
58
+ const client = {
59
+ async processRequest(request, devMode = false) {
60
+ const urlParts = extractUrlParts(request.url);
61
+ if (!urlParts) {
62
+ return new Response('invalid request', { status: 400 });
63
+ }
64
+ const [file, method] = urlParts;
65
+ const body = (await request.json());
66
+ if (!isRequestBodyValid(body)) {
67
+ return new Response('invalid request', { status: 400 });
68
+ }
69
+ const loadWebMethodFile = webMethods[`/${file}`];
70
+ try {
71
+ if (!loadWebMethodFile) {
72
+ throw new Error(`Error loading web module ${file}: Cannot find module '${file}'`);
73
+ }
74
+ const webMethodFile = await loadWebMethodFile();
75
+ const webMethod = webMethodFile[method];
76
+ if (!webMethod) {
77
+ throw new Error(`Error loading function from web module ${file}: function '${method}' not found`);
78
+ }
79
+ await checkPermission(request, webMethod.permission);
80
+ return Response.json({
81
+ result: await webMethod.handler(...body),
82
+ });
83
+ }
84
+ catch (error) {
85
+ const serializedError = (0, serialize_error_1.serializeError)(error, { maxDepth: 1 });
86
+ return Response.json({
87
+ result: devMode || !(error instanceof Error)
88
+ ? serializedError
89
+ : {
90
+ ...serializedError,
91
+ message: productionErrorMessage,
92
+ stack: productionErrorMessage,
93
+ },
94
+ exception: true,
95
+ });
96
+ }
97
+ },
98
+ async callWebMethod({ baseURL, filename, method, args }) {
99
+ const response = await fetchWithAuth(`${baseURL}/_webMethods/${filename}/${method}.ajax`, {
100
+ body: JSON.stringify(args),
101
+ method: 'POST',
102
+ });
103
+ const json = await response.json();
104
+ // request failed
105
+ if (json.exception === true) {
106
+ throw json.result;
107
+ }
108
+ return json.result;
109
+ },
110
+ };
111
+ return {
112
+ initModule(webMethodModule) {
113
+ Object.assign(webMethods, webMethodModule);
114
+ },
115
+ client,
116
+ };
117
+ }
@@ -3,6 +3,7 @@ import { EmptyObject } from 'type-fest/source/empty-object.js';
3
3
  import type { GraphQLFormattedError } from 'graphql';
4
4
  import { EventHandlersClient } from './event-handlers-modules.js';
5
5
  import { ServicePluginsClient } from './service-plugin-modules.js';
6
+ import { WebMethodClient, WebMethodModules } from './web-method-modules.js';
6
7
  export type ContextType = 'global' | 'module';
7
8
  type Headers = Record<string, string>;
8
9
  /**
@@ -46,11 +47,13 @@ export type WixClient<H extends Host<any> | undefined = undefined, Z extends Aut
46
47
  }>;
47
48
  webhooks: EventHandlersClient;
48
49
  servicePlugins: ServicePluginsClient;
50
+ webMethods: WebMethodClient;
49
51
  } & BuildDescriptors<T, H>;
50
52
  export declare function createClient<H extends Host<any> | undefined = undefined, Z extends AuthenticationStrategy<H> = AuthenticationStrategy<H>, T extends Descriptors = EmptyObject>(config: {
51
53
  modules?: H extends Host<any> ? AssertHostMatches<T, H> : T;
52
54
  auth?: Z;
53
55
  headers?: Headers;
54
56
  host?: H;
57
+ webMethods?: WebMethodModules;
55
58
  }): WixClient<H, Z, T>;
56
59
  export {};
@@ -12,6 +12,7 @@ const rest_modules_js_1 = require("./rest-modules.js");
12
12
  const event_handlers_modules_js_1 = require("./event-handlers-modules.js");
13
13
  const service_plugin_modules_js_1 = require("./service-plugin-modules.js");
14
14
  const context_1 = require("@wix/sdk-runtime/context");
15
+ const web_method_modules_js_1 = require("./web-method-modules.js");
15
16
  function createClient(config) {
16
17
  const _headers = config.headers || { Authorization: '' };
17
18
  const authStrategy = config.auth ||
@@ -20,8 +21,26 @@ function createClient(config) {
20
21
  };
21
22
  const boundGetAuthHeaders = authStrategy.getAuthHeaders.bind(undefined, config.host);
22
23
  authStrategy.getAuthHeaders = boundGetAuthHeaders;
24
+ const fetchWithAuth = async (urlOrRequest, requestInit) => {
25
+ if (typeof urlOrRequest === 'string' || urlOrRequest instanceof URL) {
26
+ return fetch(urlOrRequest, {
27
+ ...requestInit,
28
+ headers: {
29
+ ...requestInit?.headers,
30
+ ...(await boundGetAuthHeaders()).headers,
31
+ },
32
+ });
33
+ }
34
+ else {
35
+ for (const [k, v] of Object.entries((await boundGetAuthHeaders()).headers)) {
36
+ urlOrRequest.headers.set(k, v);
37
+ }
38
+ return fetch(urlOrRequest, requestInit);
39
+ }
40
+ };
23
41
  const { client: servicePluginsClient, initModule: initServicePluginModule } = (0, service_plugin_modules_js_1.servicePluginsModules)(authStrategy);
24
42
  const { client: eventHandlersClient, initModule: initEventHandlerModule } = (0, event_handlers_modules_js_1.eventHandlersModules)(authStrategy);
43
+ const { client: webMethodClient, initModule } = (0, web_method_modules_js_1.webMethodModules)(fetchWithAuth);
25
44
  const boundFetch = async (url, options) => {
26
45
  const authHeaders = await boundGetAuthHeaders();
27
46
  const defaultContentTypeHeader = (0, helpers_js_1.getDefaultContentHeader)(options);
@@ -49,6 +68,9 @@ function createClient(config) {
49
68
  else if ((0, host_modules_js_1.isHostModule)(modules) && config.host) {
50
69
  return (0, host_modules_js_1.buildHostModule)(modules, config.host);
51
70
  }
71
+ else if ((0, web_method_modules_js_1.isWebMethodModules)(modules)) {
72
+ return initModule(modules);
73
+ }
52
74
  else if (typeof modules === 'function') {
53
75
  // The generated namespaces all have the error classes on them and
54
76
  // a class is also a function, so we need to explicitly ignore these
@@ -129,23 +151,7 @@ function createClient(config) {
129
151
  finalUrl.protocol = 'https';
130
152
  return boundFetch(finalUrl.toString(), options);
131
153
  },
132
- fetchWithAuth: async (urlOrRequest, requestInit) => {
133
- if (typeof urlOrRequest === 'string' || urlOrRequest instanceof URL) {
134
- return fetch(urlOrRequest, {
135
- ...requestInit,
136
- headers: {
137
- ...requestInit?.headers,
138
- ...(await boundGetAuthHeaders()).headers,
139
- },
140
- });
141
- }
142
- else {
143
- for (const [k, v] of Object.entries((await boundGetAuthHeaders()).headers)) {
144
- urlOrRequest.headers.set(k, v);
145
- }
146
- return fetch(urlOrRequest, requestInit);
147
- }
148
- },
154
+ fetchWithAuth,
149
155
  async graphql(query, variables, opts = {
150
156
  apiVersion: 'alpha',
151
157
  }) {
@@ -164,6 +170,7 @@ function createClient(config) {
164
170
  return { data: data ?? {}, errors };
165
171
  },
166
172
  webhooks: eventHandlersClient,
173
+ webMethods: webMethodClient,
167
174
  servicePlugins: servicePluginsClient,
168
175
  };
169
176
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/sdk",
3
- "version": "1.14.4",
3
+ "version": "1.15.1",
4
4
  "license": "UNLICENSED",
5
5
  "author": {
6
6
  "name": "Ronny Ringel",
@@ -63,34 +63,35 @@
63
63
  "*.{js,ts}": "yarn lint"
64
64
  },
65
65
  "dependencies": {
66
- "@wix/identity": "^1.0.86",
67
- "@wix/image-kit": "^1.93.0",
68
- "@wix/redirects": "^1.0.61",
66
+ "@wix/identity": "^1.0.89",
67
+ "@wix/image-kit": "^1.96.0",
68
+ "@wix/redirects": "^1.0.64",
69
69
  "@wix/sdk-context": "^0.0.1",
70
- "@wix/sdk-runtime": "0.3.23",
71
- "@wix/sdk-types": "^1.12.4",
70
+ "@wix/sdk-runtime": "0.3.25",
71
+ "@wix/sdk-types": "^1.12.5",
72
72
  "jose": "^5.9.6",
73
- "type-fest": "^4.27.0"
73
+ "serialize-error": "^8.1.0",
74
+ "type-fest": "^4.29.0"
74
75
  },
75
76
  "optionalDependencies": {
76
77
  "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
77
78
  },
78
79
  "devDependencies": {
79
80
  "@types/is-ci": "^3.0.4",
80
- "@types/node": "^20.17.6",
81
+ "@types/node": "^20.17.9",
81
82
  "@vitest/ui": "^1.6.0",
82
- "@wix/ecom": "^1.0.829",
83
- "@wix/events": "^1.0.338",
84
- "@wix/metro": "^1.0.89",
85
- "@wix/metro-runtime": "^1.1854.0",
86
- "@wix/sdk-runtime": "0.3.23",
83
+ "@wix/ecom": "^1.0.845",
84
+ "@wix/events": "^1.0.347",
85
+ "@wix/metro": "^1.0.90",
86
+ "@wix/metro-runtime": "^1.1864.0",
87
+ "@wix/sdk-runtime": "0.3.25",
87
88
  "eslint": "^8.57.1",
88
89
  "eslint-config-sdk": "0.0.0",
89
90
  "graphql": "^16.8.0",
90
91
  "is-ci": "^3.0.1",
91
92
  "jsdom": "^22.1.0",
92
- "msw": "^2.6.5",
93
- "typescript": "^5.6.3",
93
+ "msw": "^2.6.6",
94
+ "typescript": "^5.7.2",
94
95
  "vitest": "^1.6.0",
95
96
  "vitest-teamcity-reporter": "^0.3.1"
96
97
  },
@@ -117,5 +118,5 @@
117
118
  "wallaby": {
118
119
  "autoDetect": true
119
120
  },
120
- "falconPackageHash": "94303cb167edda6e2a316a0d1cbbc1262c00a356ac99dd3f186349ed"
121
+ "falconPackageHash": "3ea83646bf7d33d3735730b3593ad9567e0c166c2a4c76515505bf41"
121
122
  }