@wix/sdk 1.3.0 → 1.4.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.
@@ -1,3 +1,73 @@
1
+ // src/ambassador-modules.ts
2
+ import { transformError } from "@wix/metro-runtime/velo";
3
+ var parseMethod = (method) => {
4
+ switch (method) {
5
+ case "get":
6
+ case "GET":
7
+ return "GET";
8
+ case "post":
9
+ case "POST":
10
+ return "POST";
11
+ case "put":
12
+ case "PUT":
13
+ return "PUT";
14
+ case "delete":
15
+ case "DELETE":
16
+ return "DELETE";
17
+ case "patch":
18
+ case "PATCH":
19
+ return "PATCH";
20
+ case "head":
21
+ case "HEAD":
22
+ return "HEAD";
23
+ case "options":
24
+ case "OPTIONS":
25
+ return "OPTIONS";
26
+ default:
27
+ throw new Error(`Unknown method: ${method}`);
28
+ }
29
+ };
30
+ var toHTTPModule = (factory) => (httpClient) => async (payload) => {
31
+ let requestOptions;
32
+ const HTTPFactory = (context) => {
33
+ requestOptions = factory(payload)(context);
34
+ if (requestOptions.url === void 0) {
35
+ throw new Error(
36
+ "Url was not successfully created for this request, please reach out to support channels for assistance."
37
+ );
38
+ }
39
+ const { method, url, params } = requestOptions;
40
+ return {
41
+ ...requestOptions,
42
+ method: parseMethod(method),
43
+ url,
44
+ data: requestOptions.data,
45
+ params
46
+ };
47
+ };
48
+ try {
49
+ const response = await httpClient.request(HTTPFactory);
50
+ if (requestOptions === void 0) {
51
+ throw new Error(
52
+ "Request options were not created for this request, please reach out to support channels for assistance."
53
+ );
54
+ }
55
+ const transformations = Array.isArray(requestOptions.transformResponse) ? requestOptions.transformResponse : [requestOptions.transformResponse];
56
+ let data = response.data;
57
+ transformations.forEach((transform) => {
58
+ if (transform) {
59
+ data = transform(response.data, response.headers);
60
+ }
61
+ });
62
+ return data;
63
+ } catch (e) {
64
+ throw transformError(e);
65
+ }
66
+ };
67
+ var ambassadorModuleOptions = () => ({
68
+ HTTPHost: self.location.host
69
+ });
70
+
1
71
  // src/common.ts
2
72
  var PUBLIC_METADATA_KEY = "__metadata";
3
73
  var API_URL = "www.wixapis.com";
@@ -37,16 +107,17 @@ function objectToKeyValue(input) {
37
107
  }
38
108
 
39
109
  // src/rest-modules.ts
40
- function buildRESTDescriptor(origFunc, publicMetadata, boundFetch) {
110
+ var getDefaultDomain = (method, url) => method === "GET" && !FORCE_WRITE_API_URLS.some((write_url) => url === write_url) ? READ_ONLY_API_URL : API_URL;
111
+ function buildRESTDescriptor(origFunc, publicMetadata, boundFetch, options) {
41
112
  return origFunc({
42
113
  request: async (factory) => {
43
- var _a, _b;
44
- const requestOptions = factory({ host: API_URL });
114
+ var _a, _b, _c;
115
+ const requestOptions = factory({ host: (options == null ? void 0 : options.HTTPHost) || API_URL });
45
116
  let request = requestOptions;
46
117
  if (request.method === "GET" && ((_a = request.fallback) == null ? void 0 : _a.length) && request.params.toString().length > 4e3) {
47
118
  request = requestOptions.fallback[0];
48
119
  }
49
- const domain = request.method === "GET" && !FORCE_WRITE_API_URLS.some((url2) => request.url === url2) ? READ_ONLY_API_URL : API_URL;
120
+ const domain = (_b = options == null ? void 0 : options.HTTPHost) != null ? _b : getDefaultDomain(request.method, request.url);
50
121
  let url = `https://${domain}${request.url}`;
51
122
  if (request.params && request.params.toString()) {
52
123
  url += `?${request.params.toString()}`;
@@ -86,7 +157,7 @@ function buildRESTDescriptor(origFunc, publicMetadata, boundFetch) {
86
157
  statusText: res.statusText
87
158
  };
88
159
  } catch (e) {
89
- if ((_b = e.message) == null ? void 0 : _b.includes("fetch is not defined")) {
160
+ if ((_c = e.message) == null ? void 0 : _c.includes("fetch is not defined")) {
90
161
  console.error("Node.js v18+ is required");
91
162
  }
92
163
  throw e;
@@ -138,10 +209,15 @@ function createClient(config) {
138
209
  if (isHostModule(modules) && config.host) {
139
210
  return buildHostModule(modules, config.host);
140
211
  } else if (typeof modules === "function") {
212
+ const { module, options } = modules.__isAmbassador ? {
213
+ module: toHTTPModule(modules),
214
+ options: ambassadorModuleOptions()
215
+ } : { module: modules, options: void 0 };
141
216
  return buildRESTDescriptor(
142
- modules,
217
+ module,
143
218
  metadata != null ? metadata : {},
144
- boundFetch
219
+ boundFetch,
220
+ options
145
221
  );
146
222
  } else if (isObject(modules)) {
147
223
  return Object.fromEntries(
@@ -745,14 +821,98 @@ function ApiKeyStrategy({
745
821
  };
746
822
  }
747
823
 
824
+ // src/auth/WixAppOAuthStrategy.ts
825
+ function WixAppOAuthStrategy(opts) {
826
+ let refreshToken = opts.refreshToken;
827
+ return {
828
+ getInstallUrl({ redirectUrl }) {
829
+ return `https://www.wix.com/installer/install?appId=${opts.appId}&redirectUrl=${redirectUrl}`;
830
+ },
831
+ async handleOAuthCallback(url, oauthOpts) {
832
+ const params = new URLSearchParams(new URL(url).search);
833
+ const state = params.get("state");
834
+ if (state && (oauthOpts == null ? void 0 : oauthOpts.state) && state !== oauthOpts.state) {
835
+ throw new Error(
836
+ `Invalid OAuth callback URL. Expected state to be "${oauthOpts.state}" but got "${state}"`
837
+ );
838
+ }
839
+ const code = params.get("code");
840
+ const instanceId = params.get("instanceId");
841
+ if (!code || !instanceId) {
842
+ throw new Error(
843
+ "Invalid OAuth callback URL. Make sure you pass the url including the code and instanceId query params."
844
+ );
845
+ }
846
+ const tokensRes = await fetch("https://www.wixapis.com/oauth/access", {
847
+ method: "POST",
848
+ headers: {
849
+ "Content-Type": "application/json"
850
+ },
851
+ body: JSON.stringify({
852
+ code,
853
+ client_id: opts.appId,
854
+ client_secret: opts.appSecret,
855
+ grant_type: "authorization_code"
856
+ })
857
+ });
858
+ if (tokensRes.status !== 200) {
859
+ throw new Error(
860
+ `Failed to exchange authorization code for refresh token. Unexpected status code from Wix OAuth API: ${tokensRes.status}`
861
+ );
862
+ }
863
+ const tokens = await tokensRes.json();
864
+ refreshToken = tokens.refresh_token;
865
+ return {
866
+ instanceId,
867
+ accessToken: tokens.access_token,
868
+ refreshToken: tokens.refresh_token
869
+ };
870
+ },
871
+ async getAuthHeaders() {
872
+ if (!refreshToken) {
873
+ throw new Error(
874
+ "Missing refresh token. Either pass it to the WixAppOAuthStrategy or use the handleOAuthCallback method to retrieve it."
875
+ );
876
+ }
877
+ const tokensRes = await fetch("https://www.wixapis.com/oauth/access", {
878
+ method: "POST",
879
+ headers: {
880
+ "Content-Type": "application/json"
881
+ },
882
+ body: JSON.stringify({
883
+ refresh_token: refreshToken,
884
+ client_id: opts.appId,
885
+ client_secret: opts.appSecret,
886
+ grant_type: "refresh_token"
887
+ })
888
+ });
889
+ if (tokensRes.status !== 200) {
890
+ throw new Error(
891
+ `Failed to exchange refresh token for access token. Unexpected status code from Wix OAuth API: ${tokensRes.status}`
892
+ );
893
+ }
894
+ const tokens = await tokensRes.json();
895
+ refreshToken = tokens.refresh_token;
896
+ return {
897
+ headers: {
898
+ Authorization: tokens.access_token
899
+ }
900
+ };
901
+ }
902
+ };
903
+ }
904
+
748
905
  // src/index.ts
749
906
  export * from "@wix/sdk-types";
750
907
  export {
908
+ API_URL,
751
909
  ApiKeyStrategy,
752
910
  LoginState,
753
911
  OAuthStrategy,
754
912
  TokenRole,
913
+ WixAppOAuthStrategy,
755
914
  createClient,
756
915
  decodeText,
916
+ getDefaultDomain,
757
917
  media
758
918
  };
package/build/index.d.mts CHANGED
@@ -7,6 +7,36 @@ import { authentication } from '@wix/identity';
7
7
  type PublicMetadata = {
8
8
  PACKAGE_NAME?: string;
9
9
  };
10
+ declare const API_URL = "www.wixapis.com";
11
+
12
+ declare const getDefaultDomain: (method: string, url: string) => "www.wixapis.com" | "readonly.wixapis.com";
13
+
14
+ type RequestContext = {
15
+ isSSR: boolean;
16
+ host: string;
17
+ protocol?: string;
18
+ };
19
+ /**
20
+ * Ambassador request options types are copied mostly from AxiosRequestConfig.
21
+ * They are copied and not imported to reduce the amount of dependencies (to reduce install time).
22
+ * https://github.com/axios/axios/blob/3f53eb6960f05a1f88409c4b731a40de595cb825/index.d.ts#L307-L315
23
+ */
24
+ type Method = 'get' | 'GET' | 'delete' | 'DELETE' | 'head' | 'HEAD' | 'options' | 'OPTIONS' | 'post' | 'POST' | 'put' | 'PUT' | 'patch' | 'PATCH' | 'purge' | 'PURGE' | 'link' | 'LINK' | 'unlink' | 'UNLINK';
25
+ type ResponseTransformer = (data: any, headers?: any) => any;
26
+ type AmbassadorRequestOptions<T = any> = {
27
+ _?: T;
28
+ url?: string;
29
+ method?: Method;
30
+ params?: any;
31
+ data?: any;
32
+ transformResponse?: ResponseTransformer | ResponseTransformer[];
33
+ };
34
+ type AmbassadorFactory<Request, Response> = {
35
+ (payload: Request): (context: RequestContext) => AmbassadorRequestOptions<Response>;
36
+ __isAmbassador: boolean;
37
+ };
38
+ type AmbassadorFunctionDescriptor<Request = any, Response = any> = AmbassadorFactory<Request, Response>;
39
+ type BuildAmbassadorFunction<T extends AmbassadorFunctionDescriptor> = T extends AmbassadorFunctionDescriptor<infer Request, infer Response> ? (req: Request) => Promise<Response> : never;
10
40
 
11
41
  type Headers = {
12
42
  Authorization: string;
@@ -17,10 +47,13 @@ type Headers = {
17
47
  * Any non-descriptor properties are removed from the returned object, including descriptors that
18
48
  * do not match the given host (as they will not work with the given host).
19
49
  */
20
- type BuildDescriptors<T extends Descriptors, H extends Host<any> | undefined> = BuildRESTDescriptors<T> & (H extends Host<any> ? BuildHostDescriptors<T> : {});
50
+ type BuildDescriptors<T extends Descriptors, H extends Host<any> | undefined> = BuildRESTDescriptors<T> & BuildAmbassadorDescriptors<T> & (H extends Host<any> ? BuildHostDescriptors<T> : {});
21
51
  type BuildRESTDescriptors<T extends Descriptors> = T extends RESTFunctionDescriptor ? BuildRESTFunction<T> : ConditionalExcept<{
22
52
  [Key in keyof T]: T[Key] extends Descriptors ? BuildRESTDescriptors<T[Key]> : never;
23
53
  }, EmptyObject>;
54
+ type BuildAmbassadorDescriptors<T extends Descriptors> = T extends AmbassadorFunctionDescriptor ? BuildAmbassadorFunction<T> : ConditionalExcept<{
55
+ [Key in keyof T]: T[Key] extends Descriptors ? BuildAmbassadorDescriptors<T[Key]> : never;
56
+ }, EmptyObject>;
24
57
  type BuildHostDescriptors<T extends Descriptors> = T extends HostModule<any, any> ? HostModuleAPI<T> : ConditionalExcept<{
25
58
  [Key in keyof T]: T[Key] extends Descriptors ? BuildHostDescriptors<T[Key]> : never;
26
59
  }, EmptyObject>;
@@ -29,7 +62,7 @@ type BuildHostDescriptors<T extends Descriptors> = T extends HostModule<any, any
29
62
  * can either be a REST module or a host module.
30
63
  * This type is recursive, so it can describe nested modules.
31
64
  */
32
- type Descriptors = RESTFunctionDescriptor | HostModule<any, any> | {
65
+ type Descriptors = RESTFunctionDescriptor | AmbassadorFunctionDescriptor | HostModule<any, any> | {
33
66
  [key: string]: Descriptors | PublicMetadata | any;
34
67
  };
35
68
  /**
@@ -222,4 +255,58 @@ declare function ApiKeyStrategy({ siteId, accountId, apiKey, }: {
222
255
  apiKey: string;
223
256
  } & Context): IApiKeyStrategy;
224
257
 
225
- export { AccessToken, ApiKeyStrategy, AssertHostMatches, BuildDescriptors, CalculateNextState, Descriptors, IApiKeyStrategy, IOAuthStrategy, LoginParams, LoginState, OAuthStrategy, OauthData, OauthPKCE, ProcessableState, RefreshToken, RegisterParams, StateMachine, Token, TokenResponse, TokenRole, Tokens, WixClient, createClient, decodeText, media };
258
+ type WixAppOAuthStrategy = AuthenticationStrategy & {
259
+ getInstallUrl({ redirectUrl }: {
260
+ redirectUrl: string;
261
+ }): string;
262
+ handleOAuthCallback(url: string, opts?: {
263
+ state: string;
264
+ }): Promise<{
265
+ instanceId: string;
266
+ accessToken: string;
267
+ refreshToken: string;
268
+ }>;
269
+ };
270
+ /**
271
+ * Creates an authentication strategy for Wix Apps OAuth installation process.
272
+ * Use this authentication strategy when making requests to Wix APIs from your Wix App backend.
273
+ * @param opts Options for initializing the authentication strategy
274
+ * @param opts.appId The Wix App ID
275
+ * @param opts.appSecret The Wix App Secret
276
+ * @param opts.refreshToken An optional refresh token previously retrieved from Wix OAuth API
277
+ * @returns An authentication strategy that can be used with WixClient
278
+ * @example
279
+ * ```ts
280
+ * import { WixAppOAuthStrategy, createClient } from '@wix/sdk';
281
+ * import { products } from '@wix/stores';
282
+ *
283
+ * const client = createClient({
284
+ * auth: WixAppOAuthStrategy({
285
+ * appId: 'appId',
286
+ * appSecret: 'appSecret',
287
+ * }),
288
+ * modules: { products },
289
+ * });
290
+ *
291
+ * const installUrl = client.auth.getInstallUrl({ redirectUrl: 'https://example.com' });
292
+ * // Redirect the user to the installUrl
293
+ *
294
+ * ...
295
+ *
296
+ * // in the callback handler of your http server
297
+ * // req.url is the url of the callback request
298
+ * const { instanceId, refreshToken } = await client.auth.handleOAuthCallback(req.url);
299
+ *
300
+ * // store the instanceId and refreshToken in your database
301
+ * // use the authorized client
302
+ * const products = await client.products.queryProducts().find();
303
+ *
304
+ * ```
305
+ */
306
+ declare function WixAppOAuthStrategy(opts: {
307
+ appId: string;
308
+ appSecret: string;
309
+ refreshToken?: string;
310
+ }): WixAppOAuthStrategy;
311
+
312
+ export { API_URL, AccessToken, ApiKeyStrategy, AssertHostMatches, BuildDescriptors, CalculateNextState, Descriptors, IApiKeyStrategy, IOAuthStrategy, LoginParams, LoginState, OAuthStrategy, OauthData, OauthPKCE, ProcessableState, RefreshToken, RegisterParams, StateMachine, Token, TokenResponse, TokenRole, Tokens, WixAppOAuthStrategy, WixClient, createClient, decodeText, getDefaultDomain, media };
package/build/index.d.ts CHANGED
@@ -7,6 +7,36 @@ import { authentication } from '@wix/identity';
7
7
  type PublicMetadata = {
8
8
  PACKAGE_NAME?: string;
9
9
  };
10
+ declare const API_URL = "www.wixapis.com";
11
+
12
+ declare const getDefaultDomain: (method: string, url: string) => "www.wixapis.com" | "readonly.wixapis.com";
13
+
14
+ type RequestContext = {
15
+ isSSR: boolean;
16
+ host: string;
17
+ protocol?: string;
18
+ };
19
+ /**
20
+ * Ambassador request options types are copied mostly from AxiosRequestConfig.
21
+ * They are copied and not imported to reduce the amount of dependencies (to reduce install time).
22
+ * https://github.com/axios/axios/blob/3f53eb6960f05a1f88409c4b731a40de595cb825/index.d.ts#L307-L315
23
+ */
24
+ type Method = 'get' | 'GET' | 'delete' | 'DELETE' | 'head' | 'HEAD' | 'options' | 'OPTIONS' | 'post' | 'POST' | 'put' | 'PUT' | 'patch' | 'PATCH' | 'purge' | 'PURGE' | 'link' | 'LINK' | 'unlink' | 'UNLINK';
25
+ type ResponseTransformer = (data: any, headers?: any) => any;
26
+ type AmbassadorRequestOptions<T = any> = {
27
+ _?: T;
28
+ url?: string;
29
+ method?: Method;
30
+ params?: any;
31
+ data?: any;
32
+ transformResponse?: ResponseTransformer | ResponseTransformer[];
33
+ };
34
+ type AmbassadorFactory<Request, Response> = {
35
+ (payload: Request): (context: RequestContext) => AmbassadorRequestOptions<Response>;
36
+ __isAmbassador: boolean;
37
+ };
38
+ type AmbassadorFunctionDescriptor<Request = any, Response = any> = AmbassadorFactory<Request, Response>;
39
+ type BuildAmbassadorFunction<T extends AmbassadorFunctionDescriptor> = T extends AmbassadorFunctionDescriptor<infer Request, infer Response> ? (req: Request) => Promise<Response> : never;
10
40
 
11
41
  type Headers = {
12
42
  Authorization: string;
@@ -17,10 +47,13 @@ type Headers = {
17
47
  * Any non-descriptor properties are removed from the returned object, including descriptors that
18
48
  * do not match the given host (as they will not work with the given host).
19
49
  */
20
- type BuildDescriptors<T extends Descriptors, H extends Host<any> | undefined> = BuildRESTDescriptors<T> & (H extends Host<any> ? BuildHostDescriptors<T> : {});
50
+ type BuildDescriptors<T extends Descriptors, H extends Host<any> | undefined> = BuildRESTDescriptors<T> & BuildAmbassadorDescriptors<T> & (H extends Host<any> ? BuildHostDescriptors<T> : {});
21
51
  type BuildRESTDescriptors<T extends Descriptors> = T extends RESTFunctionDescriptor ? BuildRESTFunction<T> : ConditionalExcept<{
22
52
  [Key in keyof T]: T[Key] extends Descriptors ? BuildRESTDescriptors<T[Key]> : never;
23
53
  }, EmptyObject>;
54
+ type BuildAmbassadorDescriptors<T extends Descriptors> = T extends AmbassadorFunctionDescriptor ? BuildAmbassadorFunction<T> : ConditionalExcept<{
55
+ [Key in keyof T]: T[Key] extends Descriptors ? BuildAmbassadorDescriptors<T[Key]> : never;
56
+ }, EmptyObject>;
24
57
  type BuildHostDescriptors<T extends Descriptors> = T extends HostModule<any, any> ? HostModuleAPI<T> : ConditionalExcept<{
25
58
  [Key in keyof T]: T[Key] extends Descriptors ? BuildHostDescriptors<T[Key]> : never;
26
59
  }, EmptyObject>;
@@ -29,7 +62,7 @@ type BuildHostDescriptors<T extends Descriptors> = T extends HostModule<any, any
29
62
  * can either be a REST module or a host module.
30
63
  * This type is recursive, so it can describe nested modules.
31
64
  */
32
- type Descriptors = RESTFunctionDescriptor | HostModule<any, any> | {
65
+ type Descriptors = RESTFunctionDescriptor | AmbassadorFunctionDescriptor | HostModule<any, any> | {
33
66
  [key: string]: Descriptors | PublicMetadata | any;
34
67
  };
35
68
  /**
@@ -222,4 +255,58 @@ declare function ApiKeyStrategy({ siteId, accountId, apiKey, }: {
222
255
  apiKey: string;
223
256
  } & Context): IApiKeyStrategy;
224
257
 
225
- export { AccessToken, ApiKeyStrategy, AssertHostMatches, BuildDescriptors, CalculateNextState, Descriptors, IApiKeyStrategy, IOAuthStrategy, LoginParams, LoginState, OAuthStrategy, OauthData, OauthPKCE, ProcessableState, RefreshToken, RegisterParams, StateMachine, Token, TokenResponse, TokenRole, Tokens, WixClient, createClient, decodeText, media };
258
+ type WixAppOAuthStrategy = AuthenticationStrategy & {
259
+ getInstallUrl({ redirectUrl }: {
260
+ redirectUrl: string;
261
+ }): string;
262
+ handleOAuthCallback(url: string, opts?: {
263
+ state: string;
264
+ }): Promise<{
265
+ instanceId: string;
266
+ accessToken: string;
267
+ refreshToken: string;
268
+ }>;
269
+ };
270
+ /**
271
+ * Creates an authentication strategy for Wix Apps OAuth installation process.
272
+ * Use this authentication strategy when making requests to Wix APIs from your Wix App backend.
273
+ * @param opts Options for initializing the authentication strategy
274
+ * @param opts.appId The Wix App ID
275
+ * @param opts.appSecret The Wix App Secret
276
+ * @param opts.refreshToken An optional refresh token previously retrieved from Wix OAuth API
277
+ * @returns An authentication strategy that can be used with WixClient
278
+ * @example
279
+ * ```ts
280
+ * import { WixAppOAuthStrategy, createClient } from '@wix/sdk';
281
+ * import { products } from '@wix/stores';
282
+ *
283
+ * const client = createClient({
284
+ * auth: WixAppOAuthStrategy({
285
+ * appId: 'appId',
286
+ * appSecret: 'appSecret',
287
+ * }),
288
+ * modules: { products },
289
+ * });
290
+ *
291
+ * const installUrl = client.auth.getInstallUrl({ redirectUrl: 'https://example.com' });
292
+ * // Redirect the user to the installUrl
293
+ *
294
+ * ...
295
+ *
296
+ * // in the callback handler of your http server
297
+ * // req.url is the url of the callback request
298
+ * const { instanceId, refreshToken } = await client.auth.handleOAuthCallback(req.url);
299
+ *
300
+ * // store the instanceId and refreshToken in your database
301
+ * // use the authorized client
302
+ * const products = await client.products.queryProducts().find();
303
+ *
304
+ * ```
305
+ */
306
+ declare function WixAppOAuthStrategy(opts: {
307
+ appId: string;
308
+ appSecret: string;
309
+ refreshToken?: string;
310
+ }): WixAppOAuthStrategy;
311
+
312
+ export { API_URL, AccessToken, ApiKeyStrategy, AssertHostMatches, BuildDescriptors, CalculateNextState, Descriptors, IApiKeyStrategy, IOAuthStrategy, LoginParams, LoginState, OAuthStrategy, OauthData, OauthPKCE, ProcessableState, RefreshToken, RegisterParams, StateMachine, Token, TokenResponse, TokenRole, Tokens, WixAppOAuthStrategy, WixClient, createClient, decodeText, getDefaultDomain, media };
package/build/index.js CHANGED
@@ -31,16 +31,89 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  // src/index.ts
32
32
  var src_exports = {};
33
33
  __export(src_exports, {
34
+ API_URL: () => API_URL,
34
35
  ApiKeyStrategy: () => ApiKeyStrategy,
35
36
  LoginState: () => LoginState,
36
37
  OAuthStrategy: () => OAuthStrategy,
37
38
  TokenRole: () => TokenRole,
39
+ WixAppOAuthStrategy: () => WixAppOAuthStrategy,
38
40
  createClient: () => createClient,
39
41
  decodeText: () => decodeText,
42
+ getDefaultDomain: () => getDefaultDomain,
40
43
  media: () => media
41
44
  });
42
45
  module.exports = __toCommonJS(src_exports);
43
46
 
47
+ // src/ambassador-modules.ts
48
+ var import_velo = require("@wix/metro-runtime/velo");
49
+ var parseMethod = (method) => {
50
+ switch (method) {
51
+ case "get":
52
+ case "GET":
53
+ return "GET";
54
+ case "post":
55
+ case "POST":
56
+ return "POST";
57
+ case "put":
58
+ case "PUT":
59
+ return "PUT";
60
+ case "delete":
61
+ case "DELETE":
62
+ return "DELETE";
63
+ case "patch":
64
+ case "PATCH":
65
+ return "PATCH";
66
+ case "head":
67
+ case "HEAD":
68
+ return "HEAD";
69
+ case "options":
70
+ case "OPTIONS":
71
+ return "OPTIONS";
72
+ default:
73
+ throw new Error(`Unknown method: ${method}`);
74
+ }
75
+ };
76
+ var toHTTPModule = (factory) => (httpClient) => async (payload) => {
77
+ let requestOptions;
78
+ const HTTPFactory = (context) => {
79
+ requestOptions = factory(payload)(context);
80
+ if (requestOptions.url === void 0) {
81
+ throw new Error(
82
+ "Url was not successfully created for this request, please reach out to support channels for assistance."
83
+ );
84
+ }
85
+ const { method, url, params } = requestOptions;
86
+ return {
87
+ ...requestOptions,
88
+ method: parseMethod(method),
89
+ url,
90
+ data: requestOptions.data,
91
+ params
92
+ };
93
+ };
94
+ try {
95
+ const response = await httpClient.request(HTTPFactory);
96
+ if (requestOptions === void 0) {
97
+ throw new Error(
98
+ "Request options were not created for this request, please reach out to support channels for assistance."
99
+ );
100
+ }
101
+ const transformations = Array.isArray(requestOptions.transformResponse) ? requestOptions.transformResponse : [requestOptions.transformResponse];
102
+ let data = response.data;
103
+ transformations.forEach((transform) => {
104
+ if (transform) {
105
+ data = transform(response.data, response.headers);
106
+ }
107
+ });
108
+ return data;
109
+ } catch (e) {
110
+ throw (0, import_velo.transformError)(e);
111
+ }
112
+ };
113
+ var ambassadorModuleOptions = () => ({
114
+ HTTPHost: self.location.host
115
+ });
116
+
44
117
  // src/common.ts
45
118
  var PUBLIC_METADATA_KEY = "__metadata";
46
119
  var API_URL = "www.wixapis.com";
@@ -79,15 +152,16 @@ function objectToKeyValue(input) {
79
152
  }
80
153
 
81
154
  // src/rest-modules.ts
82
- function buildRESTDescriptor(origFunc, publicMetadata, boundFetch) {
155
+ var getDefaultDomain = (method, url) => method === "GET" && !FORCE_WRITE_API_URLS.some((write_url) => url === write_url) ? READ_ONLY_API_URL : API_URL;
156
+ function buildRESTDescriptor(origFunc, publicMetadata, boundFetch, options) {
83
157
  return origFunc({
84
158
  request: async (factory) => {
85
- const requestOptions = factory({ host: API_URL });
159
+ const requestOptions = factory({ host: options?.HTTPHost || API_URL });
86
160
  let request = requestOptions;
87
161
  if (request.method === "GET" && request.fallback?.length && request.params.toString().length > 4e3) {
88
162
  request = requestOptions.fallback[0];
89
163
  }
90
- const domain = request.method === "GET" && !FORCE_WRITE_API_URLS.some((url2) => request.url === url2) ? READ_ONLY_API_URL : API_URL;
164
+ const domain = options?.HTTPHost ?? getDefaultDomain(request.method, request.url);
91
165
  let url = `https://${domain}${request.url}`;
92
166
  if (request.params && request.params.toString()) {
93
167
  url += `?${request.params.toString()}`;
@@ -179,10 +253,15 @@ function createClient(config) {
179
253
  if (isHostModule(modules) && config.host) {
180
254
  return buildHostModule(modules, config.host);
181
255
  } else if (typeof modules === "function") {
256
+ const { module: module2, options } = modules.__isAmbassador ? {
257
+ module: toHTTPModule(modules),
258
+ options: ambassadorModuleOptions()
259
+ } : { module: modules, options: void 0 };
182
260
  return buildRESTDescriptor(
183
- modules,
261
+ module2,
184
262
  metadata ?? {},
185
- boundFetch
263
+ boundFetch,
264
+ options
186
265
  );
187
266
  } else if (isObject(modules)) {
188
267
  return Object.fromEntries(
@@ -779,16 +858,100 @@ function ApiKeyStrategy({
779
858
  };
780
859
  }
781
860
 
861
+ // src/auth/WixAppOAuthStrategy.ts
862
+ function WixAppOAuthStrategy(opts) {
863
+ let refreshToken = opts.refreshToken;
864
+ return {
865
+ getInstallUrl({ redirectUrl }) {
866
+ return `https://www.wix.com/installer/install?appId=${opts.appId}&redirectUrl=${redirectUrl}`;
867
+ },
868
+ async handleOAuthCallback(url, oauthOpts) {
869
+ const params = new URLSearchParams(new URL(url).search);
870
+ const state = params.get("state");
871
+ if (state && oauthOpts?.state && state !== oauthOpts.state) {
872
+ throw new Error(
873
+ `Invalid OAuth callback URL. Expected state to be "${oauthOpts.state}" but got "${state}"`
874
+ );
875
+ }
876
+ const code = params.get("code");
877
+ const instanceId = params.get("instanceId");
878
+ if (!code || !instanceId) {
879
+ throw new Error(
880
+ "Invalid OAuth callback URL. Make sure you pass the url including the code and instanceId query params."
881
+ );
882
+ }
883
+ const tokensRes = await fetch("https://www.wixapis.com/oauth/access", {
884
+ method: "POST",
885
+ headers: {
886
+ "Content-Type": "application/json"
887
+ },
888
+ body: JSON.stringify({
889
+ code,
890
+ client_id: opts.appId,
891
+ client_secret: opts.appSecret,
892
+ grant_type: "authorization_code"
893
+ })
894
+ });
895
+ if (tokensRes.status !== 200) {
896
+ throw new Error(
897
+ `Failed to exchange authorization code for refresh token. Unexpected status code from Wix OAuth API: ${tokensRes.status}`
898
+ );
899
+ }
900
+ const tokens = await tokensRes.json();
901
+ refreshToken = tokens.refresh_token;
902
+ return {
903
+ instanceId,
904
+ accessToken: tokens.access_token,
905
+ refreshToken: tokens.refresh_token
906
+ };
907
+ },
908
+ async getAuthHeaders() {
909
+ if (!refreshToken) {
910
+ throw new Error(
911
+ "Missing refresh token. Either pass it to the WixAppOAuthStrategy or use the handleOAuthCallback method to retrieve it."
912
+ );
913
+ }
914
+ const tokensRes = await fetch("https://www.wixapis.com/oauth/access", {
915
+ method: "POST",
916
+ headers: {
917
+ "Content-Type": "application/json"
918
+ },
919
+ body: JSON.stringify({
920
+ refresh_token: refreshToken,
921
+ client_id: opts.appId,
922
+ client_secret: opts.appSecret,
923
+ grant_type: "refresh_token"
924
+ })
925
+ });
926
+ if (tokensRes.status !== 200) {
927
+ throw new Error(
928
+ `Failed to exchange refresh token for access token. Unexpected status code from Wix OAuth API: ${tokensRes.status}`
929
+ );
930
+ }
931
+ const tokens = await tokensRes.json();
932
+ refreshToken = tokens.refresh_token;
933
+ return {
934
+ headers: {
935
+ Authorization: tokens.access_token
936
+ }
937
+ };
938
+ }
939
+ };
940
+ }
941
+
782
942
  // src/index.ts
783
943
  __reExport(src_exports, require("@wix/sdk-types"), module.exports);
784
944
  // Annotate the CommonJS export names for ESM import in node:
785
945
  0 && (module.exports = {
946
+ API_URL,
786
947
  ApiKeyStrategy,
787
948
  LoginState,
788
949
  OAuthStrategy,
789
950
  TokenRole,
951
+ WixAppOAuthStrategy,
790
952
  createClient,
791
953
  decodeText,
954
+ getDefaultDomain,
792
955
  media,
793
956
  ...require("@wix/sdk-types")
794
957
  });
package/build/index.mjs CHANGED
@@ -1,3 +1,73 @@
1
+ // src/ambassador-modules.ts
2
+ import { transformError } from "@wix/metro-runtime/velo";
3
+ var parseMethod = (method) => {
4
+ switch (method) {
5
+ case "get":
6
+ case "GET":
7
+ return "GET";
8
+ case "post":
9
+ case "POST":
10
+ return "POST";
11
+ case "put":
12
+ case "PUT":
13
+ return "PUT";
14
+ case "delete":
15
+ case "DELETE":
16
+ return "DELETE";
17
+ case "patch":
18
+ case "PATCH":
19
+ return "PATCH";
20
+ case "head":
21
+ case "HEAD":
22
+ return "HEAD";
23
+ case "options":
24
+ case "OPTIONS":
25
+ return "OPTIONS";
26
+ default:
27
+ throw new Error(`Unknown method: ${method}`);
28
+ }
29
+ };
30
+ var toHTTPModule = (factory) => (httpClient) => async (payload) => {
31
+ let requestOptions;
32
+ const HTTPFactory = (context) => {
33
+ requestOptions = factory(payload)(context);
34
+ if (requestOptions.url === void 0) {
35
+ throw new Error(
36
+ "Url was not successfully created for this request, please reach out to support channels for assistance."
37
+ );
38
+ }
39
+ const { method, url, params } = requestOptions;
40
+ return {
41
+ ...requestOptions,
42
+ method: parseMethod(method),
43
+ url,
44
+ data: requestOptions.data,
45
+ params
46
+ };
47
+ };
48
+ try {
49
+ const response = await httpClient.request(HTTPFactory);
50
+ if (requestOptions === void 0) {
51
+ throw new Error(
52
+ "Request options were not created for this request, please reach out to support channels for assistance."
53
+ );
54
+ }
55
+ const transformations = Array.isArray(requestOptions.transformResponse) ? requestOptions.transformResponse : [requestOptions.transformResponse];
56
+ let data = response.data;
57
+ transformations.forEach((transform) => {
58
+ if (transform) {
59
+ data = transform(response.data, response.headers);
60
+ }
61
+ });
62
+ return data;
63
+ } catch (e) {
64
+ throw transformError(e);
65
+ }
66
+ };
67
+ var ambassadorModuleOptions = () => ({
68
+ HTTPHost: self.location.host
69
+ });
70
+
1
71
  // src/common.ts
2
72
  var PUBLIC_METADATA_KEY = "__metadata";
3
73
  var API_URL = "www.wixapis.com";
@@ -36,15 +106,16 @@ function objectToKeyValue(input) {
36
106
  }
37
107
 
38
108
  // src/rest-modules.ts
39
- function buildRESTDescriptor(origFunc, publicMetadata, boundFetch) {
109
+ var getDefaultDomain = (method, url) => method === "GET" && !FORCE_WRITE_API_URLS.some((write_url) => url === write_url) ? READ_ONLY_API_URL : API_URL;
110
+ function buildRESTDescriptor(origFunc, publicMetadata, boundFetch, options) {
40
111
  return origFunc({
41
112
  request: async (factory) => {
42
- const requestOptions = factory({ host: API_URL });
113
+ const requestOptions = factory({ host: options?.HTTPHost || API_URL });
43
114
  let request = requestOptions;
44
115
  if (request.method === "GET" && request.fallback?.length && request.params.toString().length > 4e3) {
45
116
  request = requestOptions.fallback[0];
46
117
  }
47
- const domain = request.method === "GET" && !FORCE_WRITE_API_URLS.some((url2) => request.url === url2) ? READ_ONLY_API_URL : API_URL;
118
+ const domain = options?.HTTPHost ?? getDefaultDomain(request.method, request.url);
48
119
  let url = `https://${domain}${request.url}`;
49
120
  if (request.params && request.params.toString()) {
50
121
  url += `?${request.params.toString()}`;
@@ -136,10 +207,15 @@ function createClient(config) {
136
207
  if (isHostModule(modules) && config.host) {
137
208
  return buildHostModule(modules, config.host);
138
209
  } else if (typeof modules === "function") {
210
+ const { module, options } = modules.__isAmbassador ? {
211
+ module: toHTTPModule(modules),
212
+ options: ambassadorModuleOptions()
213
+ } : { module: modules, options: void 0 };
139
214
  return buildRESTDescriptor(
140
- modules,
215
+ module,
141
216
  metadata ?? {},
142
- boundFetch
217
+ boundFetch,
218
+ options
143
219
  );
144
220
  } else if (isObject(modules)) {
145
221
  return Object.fromEntries(
@@ -736,14 +812,98 @@ function ApiKeyStrategy({
736
812
  };
737
813
  }
738
814
 
815
+ // src/auth/WixAppOAuthStrategy.ts
816
+ function WixAppOAuthStrategy(opts) {
817
+ let refreshToken = opts.refreshToken;
818
+ return {
819
+ getInstallUrl({ redirectUrl }) {
820
+ return `https://www.wix.com/installer/install?appId=${opts.appId}&redirectUrl=${redirectUrl}`;
821
+ },
822
+ async handleOAuthCallback(url, oauthOpts) {
823
+ const params = new URLSearchParams(new URL(url).search);
824
+ const state = params.get("state");
825
+ if (state && oauthOpts?.state && state !== oauthOpts.state) {
826
+ throw new Error(
827
+ `Invalid OAuth callback URL. Expected state to be "${oauthOpts.state}" but got "${state}"`
828
+ );
829
+ }
830
+ const code = params.get("code");
831
+ const instanceId = params.get("instanceId");
832
+ if (!code || !instanceId) {
833
+ throw new Error(
834
+ "Invalid OAuth callback URL. Make sure you pass the url including the code and instanceId query params."
835
+ );
836
+ }
837
+ const tokensRes = await fetch("https://www.wixapis.com/oauth/access", {
838
+ method: "POST",
839
+ headers: {
840
+ "Content-Type": "application/json"
841
+ },
842
+ body: JSON.stringify({
843
+ code,
844
+ client_id: opts.appId,
845
+ client_secret: opts.appSecret,
846
+ grant_type: "authorization_code"
847
+ })
848
+ });
849
+ if (tokensRes.status !== 200) {
850
+ throw new Error(
851
+ `Failed to exchange authorization code for refresh token. Unexpected status code from Wix OAuth API: ${tokensRes.status}`
852
+ );
853
+ }
854
+ const tokens = await tokensRes.json();
855
+ refreshToken = tokens.refresh_token;
856
+ return {
857
+ instanceId,
858
+ accessToken: tokens.access_token,
859
+ refreshToken: tokens.refresh_token
860
+ };
861
+ },
862
+ async getAuthHeaders() {
863
+ if (!refreshToken) {
864
+ throw new Error(
865
+ "Missing refresh token. Either pass it to the WixAppOAuthStrategy or use the handleOAuthCallback method to retrieve it."
866
+ );
867
+ }
868
+ const tokensRes = await fetch("https://www.wixapis.com/oauth/access", {
869
+ method: "POST",
870
+ headers: {
871
+ "Content-Type": "application/json"
872
+ },
873
+ body: JSON.stringify({
874
+ refresh_token: refreshToken,
875
+ client_id: opts.appId,
876
+ client_secret: opts.appSecret,
877
+ grant_type: "refresh_token"
878
+ })
879
+ });
880
+ if (tokensRes.status !== 200) {
881
+ throw new Error(
882
+ `Failed to exchange refresh token for access token. Unexpected status code from Wix OAuth API: ${tokensRes.status}`
883
+ );
884
+ }
885
+ const tokens = await tokensRes.json();
886
+ refreshToken = tokens.refresh_token;
887
+ return {
888
+ headers: {
889
+ Authorization: tokens.access_token
890
+ }
891
+ };
892
+ }
893
+ };
894
+ }
895
+
739
896
  // src/index.ts
740
897
  export * from "@wix/sdk-types";
741
898
  export {
899
+ API_URL,
742
900
  ApiKeyStrategy,
743
901
  LoginState,
744
902
  OAuthStrategy,
745
903
  TokenRole,
904
+ WixAppOAuthStrategy,
746
905
  createClient,
747
906
  decodeText,
907
+ getDefaultDomain,
748
908
  media
749
909
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/sdk",
3
- "version": "1.3.0",
3
+ "version": "1.4.1",
4
4
  "license": "UNLICENSED",
5
5
  "author": {
6
6
  "name": "Ronny Ringel",
@@ -30,28 +30,32 @@
30
30
  "*.{js,ts}": "yarn lint"
31
31
  },
32
32
  "dependencies": {
33
- "@babel/runtime": "^7.22.11",
34
- "@wix/identity": "^1.0.54",
35
- "@wix/image-kit": "^1.36.0",
36
- "@wix/redirects": "^1.0.23",
37
- "@wix/sdk-types": "1.3.0",
33
+ "@babel/runtime": "^7.22.15",
34
+ "@wix/identity": "^1.0.57",
35
+ "@wix/image-kit": "^1.37.0",
36
+ "@wix/metro-runtime": "^1.1529.0",
37
+ "@wix/redirects": "^1.0.24",
38
+ "@wix/sdk-types": "1.4.0",
38
39
  "pkce-challenge": "^3.1.0",
39
40
  "querystring": "^0.2.1",
40
41
  "type-fest": "^3.13.1"
41
42
  },
42
43
  "devDependencies": {
43
- "@swc/core": "^1.3.80",
44
+ "@swc/core": "^1.3.83",
44
45
  "@swc/jest": "^0.2.29",
45
46
  "@types/jest": "^27.5.2",
46
- "@types/node": "^16.18.46",
47
- "@wix/ecom": "^1.0.342",
48
- "@wix/events": "^1.0.115",
49
- "@wix/metro": "^1.0.65",
47
+ "@types/node": "^16.18.50",
48
+ "@types/node-fetch": "^2.6.4",
49
+ "@wix/ecom": "^1.0.351",
50
+ "@wix/events": "^1.0.117",
51
+ "@wix/metro": "^1.0.67",
50
52
  "eslint": "^7.32.0",
51
53
  "eslint-config-sdk": "0.0.0",
52
54
  "is-ci": "^3.0.1",
53
55
  "jest": "^27.5.1",
54
56
  "jest-teamcity": "^1.11.0",
57
+ "nock": "^13.3.3",
58
+ "node-fetch": "2.6.13",
55
59
  "ts-jest": "^27.1.5",
56
60
  "tsup": "^7.2.0",
57
61
  "typescript": "~4.9.5"
@@ -79,5 +83,5 @@
79
83
  "wallaby": {
80
84
  "autoDetect": true
81
85
  },
82
- "falconPackageHash": "2a6a54a97f2776cdbc13af01a1ac8a5a0b6c6522b2a4bbff0a80f8b6"
86
+ "falconPackageHash": "9b6b240d14e5fef75a1df39838bcd3c97317d7c97d86f43073bb4306"
83
87
  }