@wix/sdk 1.3.0 → 1.4.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.
@@ -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
+ function buildRESTDescriptor(origFunc, publicMetadata, boundFetch, options) {
41
111
  return origFunc({
42
112
  request: async (factory) => {
43
- var _a, _b;
44
- const requestOptions = factory({ host: API_URL });
113
+ var _a, _b, _c;
114
+ const requestOptions = factory({ host: (options == null ? void 0 : options.HTTPHost) || API_URL });
45
115
  let request = requestOptions;
46
116
  if (request.method === "GET" && ((_a = request.fallback) == null ? void 0 : _a.length) && request.params.toString().length > 4e3) {
47
117
  request = requestOptions.fallback[0];
48
118
  }
49
- const domain = request.method === "GET" && !FORCE_WRITE_API_URLS.some((url2) => request.url === url2) ? READ_ONLY_API_URL : API_URL;
119
+ const getDefaultDomain = () => 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();
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,13 +821,96 @@ 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,
757
916
  media
package/build/index.d.mts CHANGED
@@ -7,6 +7,34 @@ 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
+ type RequestContext = {
13
+ isSSR: boolean;
14
+ host: string;
15
+ protocol?: string;
16
+ };
17
+ /**
18
+ * Ambassador request options types are copied mostly from AxiosRequestConfig.
19
+ * They are copied and not imported to reduce the amount of dependencies (to reduce install time).
20
+ * https://github.com/axios/axios/blob/3f53eb6960f05a1f88409c4b731a40de595cb825/index.d.ts#L307-L315
21
+ */
22
+ type Method = 'get' | 'GET' | 'delete' | 'DELETE' | 'head' | 'HEAD' | 'options' | 'OPTIONS' | 'post' | 'POST' | 'put' | 'PUT' | 'patch' | 'PATCH' | 'purge' | 'PURGE' | 'link' | 'LINK' | 'unlink' | 'UNLINK';
23
+ type ResponseTransformer = (data: any, headers?: any) => any;
24
+ type AmbassadorRequestOptions<T = any> = {
25
+ _?: T;
26
+ url?: string;
27
+ method?: Method;
28
+ params?: any;
29
+ data?: any;
30
+ transformResponse?: ResponseTransformer | ResponseTransformer[];
31
+ };
32
+ type AmbassadorFactory<Request, Response> = {
33
+ (payload: Request): (context: RequestContext) => AmbassadorRequestOptions<Response>;
34
+ __isAmbassador: boolean;
35
+ };
36
+ type AmbassadorFunctionDescriptor<Request = any, Response = any> = AmbassadorFactory<Request, Response>;
37
+ type BuildAmbassadorFunction<T extends AmbassadorFunctionDescriptor> = T extends AmbassadorFunctionDescriptor<infer Request, infer Response> ? (req: Request) => Promise<Response> : never;
10
38
 
11
39
  type Headers = {
12
40
  Authorization: string;
@@ -17,10 +45,13 @@ type Headers = {
17
45
  * Any non-descriptor properties are removed from the returned object, including descriptors that
18
46
  * do not match the given host (as they will not work with the given host).
19
47
  */
20
- type BuildDescriptors<T extends Descriptors, H extends Host<any> | undefined> = BuildRESTDescriptors<T> & (H extends Host<any> ? BuildHostDescriptors<T> : {});
48
+ type BuildDescriptors<T extends Descriptors, H extends Host<any> | undefined> = BuildRESTDescriptors<T> & BuildAmbassadorDescriptors<T> & (H extends Host<any> ? BuildHostDescriptors<T> : {});
21
49
  type BuildRESTDescriptors<T extends Descriptors> = T extends RESTFunctionDescriptor ? BuildRESTFunction<T> : ConditionalExcept<{
22
50
  [Key in keyof T]: T[Key] extends Descriptors ? BuildRESTDescriptors<T[Key]> : never;
23
51
  }, EmptyObject>;
52
+ type BuildAmbassadorDescriptors<T extends Descriptors> = T extends AmbassadorFunctionDescriptor ? BuildAmbassadorFunction<T> : ConditionalExcept<{
53
+ [Key in keyof T]: T[Key] extends Descriptors ? BuildAmbassadorDescriptors<T[Key]> : never;
54
+ }, EmptyObject>;
24
55
  type BuildHostDescriptors<T extends Descriptors> = T extends HostModule<any, any> ? HostModuleAPI<T> : ConditionalExcept<{
25
56
  [Key in keyof T]: T[Key] extends Descriptors ? BuildHostDescriptors<T[Key]> : never;
26
57
  }, EmptyObject>;
@@ -29,7 +60,7 @@ type BuildHostDescriptors<T extends Descriptors> = T extends HostModule<any, any
29
60
  * can either be a REST module or a host module.
30
61
  * This type is recursive, so it can describe nested modules.
31
62
  */
32
- type Descriptors = RESTFunctionDescriptor | HostModule<any, any> | {
63
+ type Descriptors = RESTFunctionDescriptor | AmbassadorFunctionDescriptor | HostModule<any, any> | {
33
64
  [key: string]: Descriptors | PublicMetadata | any;
34
65
  };
35
66
  /**
@@ -222,4 +253,58 @@ declare function ApiKeyStrategy({ siteId, accountId, apiKey, }: {
222
253
  apiKey: string;
223
254
  } & Context): IApiKeyStrategy;
224
255
 
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 };
256
+ type WixAppOAuthStrategy = AuthenticationStrategy & {
257
+ getInstallUrl({ redirectUrl }: {
258
+ redirectUrl: string;
259
+ }): string;
260
+ handleOAuthCallback(url: string, opts?: {
261
+ state: string;
262
+ }): Promise<{
263
+ instanceId: string;
264
+ accessToken: string;
265
+ refreshToken: string;
266
+ }>;
267
+ };
268
+ /**
269
+ * Creates an authentication strategy for Wix Apps OAuth installation process.
270
+ * Use this authentication strategy when making requests to Wix APIs from your Wix App backend.
271
+ * @param opts Options for initializing the authentication strategy
272
+ * @param opts.appId The Wix App ID
273
+ * @param opts.appSecret The Wix App Secret
274
+ * @param opts.refreshToken An optional refresh token previously retrieved from Wix OAuth API
275
+ * @returns An authentication strategy that can be used with WixClient
276
+ * @example
277
+ * ```ts
278
+ * import { WixAppOAuthStrategy, createClient } from '@wix/sdk';
279
+ * import { products } from '@wix/stores';
280
+ *
281
+ * const client = createClient({
282
+ * auth: WixAppOAuthStrategy({
283
+ * appId: 'appId',
284
+ * appSecret: 'appSecret',
285
+ * }),
286
+ * modules: { products },
287
+ * });
288
+ *
289
+ * const installUrl = client.auth.getInstallUrl({ redirectUrl: 'https://example.com' });
290
+ * // Redirect the user to the installUrl
291
+ *
292
+ * ...
293
+ *
294
+ * // in the callback handler of your http server
295
+ * // req.url is the url of the callback request
296
+ * const { instanceId, refreshToken } = await client.auth.handleOAuthCallback(req.url);
297
+ *
298
+ * // store the instanceId and refreshToken in your database
299
+ * // use the authorized client
300
+ * const products = await client.products.queryProducts().find();
301
+ *
302
+ * ```
303
+ */
304
+ declare function WixAppOAuthStrategy(opts: {
305
+ appId: string;
306
+ appSecret: string;
307
+ refreshToken?: string;
308
+ }): WixAppOAuthStrategy;
309
+
310
+ 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, media };
package/build/index.d.ts CHANGED
@@ -7,6 +7,34 @@ 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
+ type RequestContext = {
13
+ isSSR: boolean;
14
+ host: string;
15
+ protocol?: string;
16
+ };
17
+ /**
18
+ * Ambassador request options types are copied mostly from AxiosRequestConfig.
19
+ * They are copied and not imported to reduce the amount of dependencies (to reduce install time).
20
+ * https://github.com/axios/axios/blob/3f53eb6960f05a1f88409c4b731a40de595cb825/index.d.ts#L307-L315
21
+ */
22
+ type Method = 'get' | 'GET' | 'delete' | 'DELETE' | 'head' | 'HEAD' | 'options' | 'OPTIONS' | 'post' | 'POST' | 'put' | 'PUT' | 'patch' | 'PATCH' | 'purge' | 'PURGE' | 'link' | 'LINK' | 'unlink' | 'UNLINK';
23
+ type ResponseTransformer = (data: any, headers?: any) => any;
24
+ type AmbassadorRequestOptions<T = any> = {
25
+ _?: T;
26
+ url?: string;
27
+ method?: Method;
28
+ params?: any;
29
+ data?: any;
30
+ transformResponse?: ResponseTransformer | ResponseTransformer[];
31
+ };
32
+ type AmbassadorFactory<Request, Response> = {
33
+ (payload: Request): (context: RequestContext) => AmbassadorRequestOptions<Response>;
34
+ __isAmbassador: boolean;
35
+ };
36
+ type AmbassadorFunctionDescriptor<Request = any, Response = any> = AmbassadorFactory<Request, Response>;
37
+ type BuildAmbassadorFunction<T extends AmbassadorFunctionDescriptor> = T extends AmbassadorFunctionDescriptor<infer Request, infer Response> ? (req: Request) => Promise<Response> : never;
10
38
 
11
39
  type Headers = {
12
40
  Authorization: string;
@@ -17,10 +45,13 @@ type Headers = {
17
45
  * Any non-descriptor properties are removed from the returned object, including descriptors that
18
46
  * do not match the given host (as they will not work with the given host).
19
47
  */
20
- type BuildDescriptors<T extends Descriptors, H extends Host<any> | undefined> = BuildRESTDescriptors<T> & (H extends Host<any> ? BuildHostDescriptors<T> : {});
48
+ type BuildDescriptors<T extends Descriptors, H extends Host<any> | undefined> = BuildRESTDescriptors<T> & BuildAmbassadorDescriptors<T> & (H extends Host<any> ? BuildHostDescriptors<T> : {});
21
49
  type BuildRESTDescriptors<T extends Descriptors> = T extends RESTFunctionDescriptor ? BuildRESTFunction<T> : ConditionalExcept<{
22
50
  [Key in keyof T]: T[Key] extends Descriptors ? BuildRESTDescriptors<T[Key]> : never;
23
51
  }, EmptyObject>;
52
+ type BuildAmbassadorDescriptors<T extends Descriptors> = T extends AmbassadorFunctionDescriptor ? BuildAmbassadorFunction<T> : ConditionalExcept<{
53
+ [Key in keyof T]: T[Key] extends Descriptors ? BuildAmbassadorDescriptors<T[Key]> : never;
54
+ }, EmptyObject>;
24
55
  type BuildHostDescriptors<T extends Descriptors> = T extends HostModule<any, any> ? HostModuleAPI<T> : ConditionalExcept<{
25
56
  [Key in keyof T]: T[Key] extends Descriptors ? BuildHostDescriptors<T[Key]> : never;
26
57
  }, EmptyObject>;
@@ -29,7 +60,7 @@ type BuildHostDescriptors<T extends Descriptors> = T extends HostModule<any, any
29
60
  * can either be a REST module or a host module.
30
61
  * This type is recursive, so it can describe nested modules.
31
62
  */
32
- type Descriptors = RESTFunctionDescriptor | HostModule<any, any> | {
63
+ type Descriptors = RESTFunctionDescriptor | AmbassadorFunctionDescriptor | HostModule<any, any> | {
33
64
  [key: string]: Descriptors | PublicMetadata | any;
34
65
  };
35
66
  /**
@@ -222,4 +253,58 @@ declare function ApiKeyStrategy({ siteId, accountId, apiKey, }: {
222
253
  apiKey: string;
223
254
  } & Context): IApiKeyStrategy;
224
255
 
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 };
256
+ type WixAppOAuthStrategy = AuthenticationStrategy & {
257
+ getInstallUrl({ redirectUrl }: {
258
+ redirectUrl: string;
259
+ }): string;
260
+ handleOAuthCallback(url: string, opts?: {
261
+ state: string;
262
+ }): Promise<{
263
+ instanceId: string;
264
+ accessToken: string;
265
+ refreshToken: string;
266
+ }>;
267
+ };
268
+ /**
269
+ * Creates an authentication strategy for Wix Apps OAuth installation process.
270
+ * Use this authentication strategy when making requests to Wix APIs from your Wix App backend.
271
+ * @param opts Options for initializing the authentication strategy
272
+ * @param opts.appId The Wix App ID
273
+ * @param opts.appSecret The Wix App Secret
274
+ * @param opts.refreshToken An optional refresh token previously retrieved from Wix OAuth API
275
+ * @returns An authentication strategy that can be used with WixClient
276
+ * @example
277
+ * ```ts
278
+ * import { WixAppOAuthStrategy, createClient } from '@wix/sdk';
279
+ * import { products } from '@wix/stores';
280
+ *
281
+ * const client = createClient({
282
+ * auth: WixAppOAuthStrategy({
283
+ * appId: 'appId',
284
+ * appSecret: 'appSecret',
285
+ * }),
286
+ * modules: { products },
287
+ * });
288
+ *
289
+ * const installUrl = client.auth.getInstallUrl({ redirectUrl: 'https://example.com' });
290
+ * // Redirect the user to the installUrl
291
+ *
292
+ * ...
293
+ *
294
+ * // in the callback handler of your http server
295
+ * // req.url is the url of the callback request
296
+ * const { instanceId, refreshToken } = await client.auth.handleOAuthCallback(req.url);
297
+ *
298
+ * // store the instanceId and refreshToken in your database
299
+ * // use the authorized client
300
+ * const products = await client.products.queryProducts().find();
301
+ *
302
+ * ```
303
+ */
304
+ declare function WixAppOAuthStrategy(opts: {
305
+ appId: string;
306
+ appSecret: string;
307
+ refreshToken?: string;
308
+ }): WixAppOAuthStrategy;
309
+
310
+ 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, media };
package/build/index.js CHANGED
@@ -31,16 +31,88 @@ 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,
40
42
  media: () => media
41
43
  });
42
44
  module.exports = __toCommonJS(src_exports);
43
45
 
46
+ // src/ambassador-modules.ts
47
+ var import_velo = require("@wix/metro-runtime/velo");
48
+ var parseMethod = (method) => {
49
+ switch (method) {
50
+ case "get":
51
+ case "GET":
52
+ return "GET";
53
+ case "post":
54
+ case "POST":
55
+ return "POST";
56
+ case "put":
57
+ case "PUT":
58
+ return "PUT";
59
+ case "delete":
60
+ case "DELETE":
61
+ return "DELETE";
62
+ case "patch":
63
+ case "PATCH":
64
+ return "PATCH";
65
+ case "head":
66
+ case "HEAD":
67
+ return "HEAD";
68
+ case "options":
69
+ case "OPTIONS":
70
+ return "OPTIONS";
71
+ default:
72
+ throw new Error(`Unknown method: ${method}`);
73
+ }
74
+ };
75
+ var toHTTPModule = (factory) => (httpClient) => async (payload) => {
76
+ let requestOptions;
77
+ const HTTPFactory = (context) => {
78
+ requestOptions = factory(payload)(context);
79
+ if (requestOptions.url === void 0) {
80
+ throw new Error(
81
+ "Url was not successfully created for this request, please reach out to support channels for assistance."
82
+ );
83
+ }
84
+ const { method, url, params } = requestOptions;
85
+ return {
86
+ ...requestOptions,
87
+ method: parseMethod(method),
88
+ url,
89
+ data: requestOptions.data,
90
+ params
91
+ };
92
+ };
93
+ try {
94
+ const response = await httpClient.request(HTTPFactory);
95
+ if (requestOptions === void 0) {
96
+ throw new Error(
97
+ "Request options were not created for this request, please reach out to support channels for assistance."
98
+ );
99
+ }
100
+ const transformations = Array.isArray(requestOptions.transformResponse) ? requestOptions.transformResponse : [requestOptions.transformResponse];
101
+ let data = response.data;
102
+ transformations.forEach((transform) => {
103
+ if (transform) {
104
+ data = transform(response.data, response.headers);
105
+ }
106
+ });
107
+ return data;
108
+ } catch (e) {
109
+ throw (0, import_velo.transformError)(e);
110
+ }
111
+ };
112
+ var ambassadorModuleOptions = () => ({
113
+ HTTPHost: self.location.host
114
+ });
115
+
44
116
  // src/common.ts
45
117
  var PUBLIC_METADATA_KEY = "__metadata";
46
118
  var API_URL = "www.wixapis.com";
@@ -79,15 +151,16 @@ function objectToKeyValue(input) {
79
151
  }
80
152
 
81
153
  // src/rest-modules.ts
82
- function buildRESTDescriptor(origFunc, publicMetadata, boundFetch) {
154
+ function buildRESTDescriptor(origFunc, publicMetadata, boundFetch, options) {
83
155
  return origFunc({
84
156
  request: async (factory) => {
85
- const requestOptions = factory({ host: API_URL });
157
+ const requestOptions = factory({ host: options?.HTTPHost || API_URL });
86
158
  let request = requestOptions;
87
159
  if (request.method === "GET" && request.fallback?.length && request.params.toString().length > 4e3) {
88
160
  request = requestOptions.fallback[0];
89
161
  }
90
- const domain = request.method === "GET" && !FORCE_WRITE_API_URLS.some((url2) => request.url === url2) ? READ_ONLY_API_URL : API_URL;
162
+ const getDefaultDomain = () => request.method === "GET" && !FORCE_WRITE_API_URLS.some((url2) => request.url === url2) ? READ_ONLY_API_URL : API_URL;
163
+ const domain = options?.HTTPHost ?? getDefaultDomain();
91
164
  let url = `https://${domain}${request.url}`;
92
165
  if (request.params && request.params.toString()) {
93
166
  url += `?${request.params.toString()}`;
@@ -179,10 +252,15 @@ function createClient(config) {
179
252
  if (isHostModule(modules) && config.host) {
180
253
  return buildHostModule(modules, config.host);
181
254
  } else if (typeof modules === "function") {
255
+ const { module: module2, options } = modules.__isAmbassador ? {
256
+ module: toHTTPModule(modules),
257
+ options: ambassadorModuleOptions()
258
+ } : { module: modules, options: void 0 };
182
259
  return buildRESTDescriptor(
183
- modules,
260
+ module2,
184
261
  metadata ?? {},
185
- boundFetch
262
+ boundFetch,
263
+ options
186
264
  );
187
265
  } else if (isObject(modules)) {
188
266
  return Object.fromEntries(
@@ -779,14 +857,97 @@ function ApiKeyStrategy({
779
857
  };
780
858
  }
781
859
 
860
+ // src/auth/WixAppOAuthStrategy.ts
861
+ function WixAppOAuthStrategy(opts) {
862
+ let refreshToken = opts.refreshToken;
863
+ return {
864
+ getInstallUrl({ redirectUrl }) {
865
+ return `https://www.wix.com/installer/install?appId=${opts.appId}&redirectUrl=${redirectUrl}`;
866
+ },
867
+ async handleOAuthCallback(url, oauthOpts) {
868
+ const params = new URLSearchParams(new URL(url).search);
869
+ const state = params.get("state");
870
+ if (state && oauthOpts?.state && state !== oauthOpts.state) {
871
+ throw new Error(
872
+ `Invalid OAuth callback URL. Expected state to be "${oauthOpts.state}" but got "${state}"`
873
+ );
874
+ }
875
+ const code = params.get("code");
876
+ const instanceId = params.get("instanceId");
877
+ if (!code || !instanceId) {
878
+ throw new Error(
879
+ "Invalid OAuth callback URL. Make sure you pass the url including the code and instanceId query params."
880
+ );
881
+ }
882
+ const tokensRes = await fetch("https://www.wixapis.com/oauth/access", {
883
+ method: "POST",
884
+ headers: {
885
+ "Content-Type": "application/json"
886
+ },
887
+ body: JSON.stringify({
888
+ code,
889
+ client_id: opts.appId,
890
+ client_secret: opts.appSecret,
891
+ grant_type: "authorization_code"
892
+ })
893
+ });
894
+ if (tokensRes.status !== 200) {
895
+ throw new Error(
896
+ `Failed to exchange authorization code for refresh token. Unexpected status code from Wix OAuth API: ${tokensRes.status}`
897
+ );
898
+ }
899
+ const tokens = await tokensRes.json();
900
+ refreshToken = tokens.refresh_token;
901
+ return {
902
+ instanceId,
903
+ accessToken: tokens.access_token,
904
+ refreshToken: tokens.refresh_token
905
+ };
906
+ },
907
+ async getAuthHeaders() {
908
+ if (!refreshToken) {
909
+ throw new Error(
910
+ "Missing refresh token. Either pass it to the WixAppOAuthStrategy or use the handleOAuthCallback method to retrieve it."
911
+ );
912
+ }
913
+ const tokensRes = await fetch("https://www.wixapis.com/oauth/access", {
914
+ method: "POST",
915
+ headers: {
916
+ "Content-Type": "application/json"
917
+ },
918
+ body: JSON.stringify({
919
+ refresh_token: refreshToken,
920
+ client_id: opts.appId,
921
+ client_secret: opts.appSecret,
922
+ grant_type: "refresh_token"
923
+ })
924
+ });
925
+ if (tokensRes.status !== 200) {
926
+ throw new Error(
927
+ `Failed to exchange refresh token for access token. Unexpected status code from Wix OAuth API: ${tokensRes.status}`
928
+ );
929
+ }
930
+ const tokens = await tokensRes.json();
931
+ refreshToken = tokens.refresh_token;
932
+ return {
933
+ headers: {
934
+ Authorization: tokens.access_token
935
+ }
936
+ };
937
+ }
938
+ };
939
+ }
940
+
782
941
  // src/index.ts
783
942
  __reExport(src_exports, require("@wix/sdk-types"), module.exports);
784
943
  // Annotate the CommonJS export names for ESM import in node:
785
944
  0 && (module.exports = {
945
+ API_URL,
786
946
  ApiKeyStrategy,
787
947
  LoginState,
788
948
  OAuthStrategy,
789
949
  TokenRole,
950
+ WixAppOAuthStrategy,
790
951
  createClient,
791
952
  decodeText,
792
953
  media,
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
+ function buildRESTDescriptor(origFunc, publicMetadata, boundFetch, options) {
40
110
  return origFunc({
41
111
  request: async (factory) => {
42
- const requestOptions = factory({ host: API_URL });
112
+ const requestOptions = factory({ host: options?.HTTPHost || API_URL });
43
113
  let request = requestOptions;
44
114
  if (request.method === "GET" && request.fallback?.length && request.params.toString().length > 4e3) {
45
115
  request = requestOptions.fallback[0];
46
116
  }
47
- const domain = request.method === "GET" && !FORCE_WRITE_API_URLS.some((url2) => request.url === url2) ? READ_ONLY_API_URL : API_URL;
117
+ const getDefaultDomain = () => request.method === "GET" && !FORCE_WRITE_API_URLS.some((url2) => request.url === url2) ? READ_ONLY_API_URL : API_URL;
118
+ const domain = options?.HTTPHost ?? getDefaultDomain();
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,13 +812,96 @@ 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,
748
907
  media
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/sdk",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
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.56",
35
+ "@wix/image-kit": "^1.37.0",
36
+ "@wix/metro-runtime": "^1.1528.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.350",
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": "e29cd59910eaf06e19edd72d4a7e2cfb431c4a4e6ebb29eeff606342"
83
87
  }