@wix/sdk 1.12.7 → 1.12.9

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.
@@ -73,6 +73,7 @@ export declare function AppStrategy(opts: {
73
73
  appId: string;
74
74
  appSecret?: string;
75
75
  publicKey?: string;
76
+ authServerBaseUrl?: string;
76
77
  } & ({
77
78
  refreshToken?: string;
78
79
  } | {
@@ -40,6 +40,7 @@ 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';
43
44
  let refreshToken = 'refreshToken' in opts ? opts.refreshToken : undefined;
44
45
  return {
45
46
  getInstallUrl({ redirectUrl, token, state }) {
@@ -68,7 +69,8 @@ export function AppStrategy(opts) {
68
69
  if (!code || !instanceId) {
69
70
  throw new Error('Invalid OAuth callback URL. Make sure you pass the url including the code and instanceId query params.');
70
71
  }
71
- const tokensRes = await fetch('https://www.wixapis.com/oauth/access', {
72
+ const tokenUrl = new URL('/oauth/access', authServerBaseUrl);
73
+ const tokensRes = await fetch(tokenUrl.href, {
72
74
  method: 'POST',
73
75
  headers: {
74
76
  'Content-Type': 'application/json',
@@ -96,7 +98,8 @@ export function AppStrategy(opts) {
96
98
  if (!opts.appSecret) {
97
99
  throw new Error('App secret is required for retrieveing app-level access tokens. Make sure to pass it to the AppStrategy');
98
100
  }
99
- const tokensRes = await fetch('https://www.wixapis.com/oauth/access', {
101
+ const tokenUrl = new URL('/oauth/access', authServerBaseUrl);
102
+ const tokensRes = await fetch(tokenUrl.href, {
100
103
  method: 'POST',
101
104
  headers: {
102
105
  'Content-Type': 'application/json',
@@ -123,7 +126,8 @@ export function AppStrategy(opts) {
123
126
  if (!opts.appSecret) {
124
127
  throw new Error('App secret is required for retrieveing app-level access tokens. Make sure to pass it to the AppStrategy');
125
128
  }
126
- const tokensRes = await fetch('https://www.wixapis.com/oauth2/token', {
129
+ const tokenUrl = new URL('/oauth2/token', authServerBaseUrl);
130
+ const tokensRes = await fetch(tokenUrl.href, {
127
131
  method: 'POST',
128
132
  headers: {
129
133
  'Content-Type': 'application/json',
@@ -158,7 +162,7 @@ export function AppStrategy(opts) {
158
162
  },
159
163
  async elevated() {
160
164
  if ('accessToken' in opts && opts.accessToken) {
161
- const tokenInfo = await getTokenInfo(opts.accessToken);
165
+ const tokenInfo = await getTokenInfo(opts.accessToken, authServerBaseUrl);
162
166
  if (tokenInfo.clientId !== opts.appId) {
163
167
  throw new Error(`Invalid access token. The token is not issued for the app with ID "${opts.appId}"`);
164
168
  }
@@ -170,6 +174,7 @@ export function AppStrategy(opts) {
170
174
  appSecret: opts.appSecret,
171
175
  publicKey: opts.publicKey,
172
176
  instanceId: tokenInfo.instanceId,
177
+ authServerBaseUrl: opts.authServerBaseUrl,
173
178
  });
174
179
  }
175
180
  else {
@@ -200,12 +205,13 @@ export function AppStrategy(opts) {
200
205
  if (!tokenToCheck) {
201
206
  throw new Error('Missing token to get info for. Either pass the token as an argument or provide it when initializing the AppStrategy');
202
207
  }
203
- return getTokenInfo(tokenToCheck);
208
+ return getTokenInfo(tokenToCheck, authServerBaseUrl);
204
209
  },
205
210
  };
206
211
  }
207
- async function getTokenInfo(token) {
208
- const tokenInfoRes = await fetch('https://www.wixapis.com/oauth2/token-info', {
212
+ async function getTokenInfo(token, authServerBaseUrl) {
213
+ const tokenInfoUrl = new URL('/oauth2/token-info', authServerBaseUrl);
214
+ const tokenInfoRes = await fetch(tokenInfoUrl.href, {
209
215
  method: 'POST',
210
216
  headers: {
211
217
  'Content-Type': 'application/json',
@@ -1,4 +1,48 @@
1
- import { BaseEventMetadata, EventDefinition, EventHandler } from '@wix/sdk-types';
1
+ import { AuthenticationStrategy, EventDefinition, EventHandler, EventIdentity } from '@wix/sdk-types';
2
+ import { Emitter } from 'nanoevents';
2
3
  export declare const isEventHandlerModule: (val: any) => val is EventDefinition<unknown, string>;
3
4
  export declare function buildEventDefinition<T extends EventDefinition<any, string>>(eventDefinition: T, registerHandler: (eventDefinition: T, handler: EventHandler<T>) => void): (handler: EventHandler<T>) => void;
4
- export declare function runHandler<T extends EventDefinition>(eventDefinition: T, handler: EventHandler<T>, payload: unknown, baseEventMetadata: BaseEventMetadata): void | Promise<void>;
5
+ type ResolvePossibleEvents<T extends EventDefinition<any>[]> = {
6
+ [K in keyof T]: T[K] extends EventDefinition<any> ? {
7
+ eventType: T[K]['type'];
8
+ payload: T[K]['__payload'];
9
+ } : never;
10
+ } extends (infer U)[] ? U : never;
11
+ export type ProcessedEvent<T extends EventDefinition<any>[] = []> = {
12
+ instanceId: string;
13
+ identity?: EventIdentity;
14
+ } & (T['length'] extends 0 ? {
15
+ eventType: string;
16
+ payload: unknown;
17
+ } : ResolvePossibleEvents<T>);
18
+ export type EventHandlersClient = Emitter<{
19
+ registered: (event: EventDefinition<any>) => void;
20
+ }> & {
21
+ getRegisteredEvents(): Map<string, {
22
+ eventDefinition: EventDefinition;
23
+ handler: EventHandler<EventDefinition>;
24
+ }[]>;
25
+ process<ExpectedEvents extends EventDefinition<any>[] = []>(jwt: string, opts?: {
26
+ expectedEvents: ExpectedEvents;
27
+ }): Promise<ProcessedEvent<ExpectedEvents>>;
28
+ processRequest<ExpectedEvents extends EventDefinition<any>[] = []>(request: Request, opts?: {
29
+ expectedEvents: ExpectedEvents;
30
+ }): Promise<ProcessedEvent<ExpectedEvents>>;
31
+ parseJWT(jwt: string): Promise<ProcessedEvent>;
32
+ parseRequest(request: Request): Promise<ProcessedEvent>;
33
+ executeHandlers(event: ProcessedEvent): Promise<void>;
34
+ apps: {
35
+ AppInstalled: EventDefinition<{
36
+ appId: string;
37
+ originInstanceId: string;
38
+ }, 'AppInstalled'>;
39
+ AppRemoved: EventDefinition<{
40
+ appId: string;
41
+ }, 'AppRemoved'>;
42
+ };
43
+ };
44
+ export declare function eventHandlersModules(authStrategy: AuthenticationStrategy<any>): {
45
+ initModule(eventDefinition: EventDefinition<any, string>): (handler: EventHandler<EventDefinition<any, string>>) => void;
46
+ client: EventHandlersClient;
47
+ };
48
+ export {};
@@ -1,10 +1,12 @@
1
+ import { EventDefinition, } from '@wix/sdk-types';
2
+ import { createNanoEvents } from 'nanoevents';
1
3
  export const isEventHandlerModule = (val) => val.__type === 'event-definition';
2
4
  export function buildEventDefinition(eventDefinition, registerHandler) {
3
5
  return (handler) => {
4
6
  registerHandler(eventDefinition, handler);
5
7
  };
6
8
  }
7
- export function runHandler(eventDefinition, handler, payload, baseEventMetadata) {
9
+ function runHandler(eventDefinition, handler, payload, baseEventMetadata) {
8
10
  let envelope;
9
11
  if (eventDefinition.isDomainEvent) {
10
12
  const domainEventPayload = payload;
@@ -46,3 +48,99 @@ export function runHandler(eventDefinition, handler, payload, baseEventMetadata)
46
48
  const transformFromRESTFn = eventDefinition.transformations ?? ((x) => x);
47
49
  return handler(transformFromRESTFn(envelope));
48
50
  }
51
+ export function eventHandlersModules(authStrategy) {
52
+ const eventHandlers = new Map();
53
+ const webhooksEmitter = createNanoEvents();
54
+ const client = {
55
+ ...webhooksEmitter,
56
+ getRegisteredEvents: () => eventHandlers,
57
+ async process(jwt, opts = {
58
+ expectedEvents: [],
59
+ }) {
60
+ const { eventType, identity, instanceId, payload } = await this.parseJWT(jwt);
61
+ const allExpectedEvents = [
62
+ ...opts.expectedEvents,
63
+ ...Array.from(eventHandlers.keys()).map((type) => ({ type })),
64
+ ];
65
+ if (allExpectedEvents.length > 0 &&
66
+ !allExpectedEvents.some(({ type }) => type === eventType)) {
67
+ throw new Error(`Unexpected event type: ${eventType}. Expected one of: ${allExpectedEvents
68
+ .map((x) => x.type)
69
+ .join(', ')}`);
70
+ }
71
+ const handlers = eventHandlers.get(eventType) ?? [];
72
+ await Promise.all(handlers.map(({ eventDefinition, handler }) => runHandler(eventDefinition, handler, payload, {
73
+ instanceId,
74
+ identity,
75
+ })));
76
+ return {
77
+ instanceId,
78
+ eventType,
79
+ payload,
80
+ identity,
81
+ };
82
+ },
83
+ async processRequest(request, opts) {
84
+ const body = await request.text();
85
+ return this.process(body, opts);
86
+ },
87
+ async parseJWT(jwt) {
88
+ if (!authStrategy.decodeJWT) {
89
+ throw new Error('decodeJWT is not supported by the authentication strategy');
90
+ }
91
+ const { decoded, valid } = await authStrategy.decodeJWT(jwt);
92
+ if (!valid) {
93
+ throw new Error('JWT is not valid');
94
+ }
95
+ if (typeof decoded.data !== 'string') {
96
+ throw new Error(`Unexpected type of JWT data: expected string, got ${typeof decoded.data}`);
97
+ }
98
+ const parsedDecoded = JSON.parse(decoded.data);
99
+ const eventType = parsedDecoded.eventType;
100
+ const instanceId = parsedDecoded.instanceId;
101
+ const identity = parsedDecoded.identity
102
+ ? JSON.parse(parsedDecoded.identity)
103
+ : undefined;
104
+ const payload = JSON.parse(parsedDecoded.data);
105
+ return {
106
+ instanceId,
107
+ eventType,
108
+ payload,
109
+ identity,
110
+ };
111
+ },
112
+ async parseRequest(request) {
113
+ const jwt = await request.text();
114
+ return this.parseJWT(jwt);
115
+ },
116
+ async executeHandlers(event) {
117
+ const allExpectedEvents = Array.from(eventHandlers.keys()).map((type) => ({ type }));
118
+ if (allExpectedEvents.length > 0 &&
119
+ !allExpectedEvents.some(({ type }) => type === event.eventType)) {
120
+ throw new Error(`Unexpected event type: ${event.eventType}. Expected one of: ${allExpectedEvents
121
+ .map((x) => x.type)
122
+ .join(', ')}`);
123
+ }
124
+ const handlers = eventHandlers.get(event.eventType) ?? [];
125
+ await Promise.all(handlers.map(({ eventDefinition, handler }) => runHandler(eventDefinition, handler, event.payload, {
126
+ instanceId: event.instanceId,
127
+ identity: event.identity,
128
+ })));
129
+ },
130
+ apps: {
131
+ AppInstalled: EventDefinition('AppInstalled')(),
132
+ AppRemoved: EventDefinition('AppRemoved')(),
133
+ },
134
+ };
135
+ return {
136
+ initModule(eventDefinition) {
137
+ return (handler) => {
138
+ const handlers = eventHandlers.get(eventDefinition.type) ?? [];
139
+ handlers.push({ eventDefinition, handler });
140
+ eventHandlers.set(eventDefinition.type, handlers);
141
+ webhooksEmitter.emit('registered', eventDefinition);
142
+ };
143
+ },
144
+ client,
145
+ };
146
+ }
@@ -1,8 +1,33 @@
1
- import { ServicePluginContract, ServicePluginDefinition } from '@wix/sdk-types';
1
+ import { AuthenticationStrategy, ServicePluginContract, ServicePluginDefinition } from '@wix/sdk-types';
2
+ import { Emitter } from 'nanoevents';
2
3
  export declare const isServicePluginModule: (val: any) => val is ServicePluginDefinition<ServicePluginContract>;
3
- export declare function buildServicePluginDefinition<T extends ServicePluginDefinition<any>>(servicePluginDefinition: T, registrServicePluginImplementation: (servicePluginDefinition: T, implementation: T['__contract']) => void, decodeJWT?: (token: string, verifyCallerClaims?: boolean) => Promise<{
4
- decoded: {
5
- data: string;
6
- };
7
- valid: boolean;
8
- }>): (implementation: T['__contract']) => void;
4
+ export type UnknownServicePluginResponse = unknown;
5
+ type ServicePluginRequestMetadata = {
6
+ instanceId: string;
7
+ appExtensionType: string;
8
+ };
9
+ type ServicePluginRequest = {
10
+ metadata: ServicePluginRequestMetadata;
11
+ request: unknown;
12
+ };
13
+ export type ServicePluginsClient = Emitter<{
14
+ registered: (servicePluginDefinition: ServicePluginDefinition<ServicePluginContract>) => void;
15
+ }> & {
16
+ getRegisteredServicePlugins(): Map<string, {
17
+ servicePluginDefinition: ServicePluginDefinition<any>;
18
+ implementation: ServicePluginContract;
19
+ }[]>;
20
+ process(request: {
21
+ url: string;
22
+ body: string;
23
+ }): Promise<unknown>;
24
+ processRequest(request: Request): Promise<Response>;
25
+ parseJWT(jwt: string): Promise<ServicePluginRequest>;
26
+ parseRequest(request: Request): Promise<ServicePluginRequest>;
27
+ executeHandler(servicePluginRequest: ServicePluginRequest, url: string): Promise<UnknownServicePluginResponse>;
28
+ };
29
+ export declare function servicePluginsModules(authStrategy: AuthenticationStrategy<any>): {
30
+ initModule<T extends ServicePluginDefinition<any>>(servicePluginDefinition: T): (implementation: T["__contract"]) => void;
31
+ client: ServicePluginsClient;
32
+ };
33
+ export {};
@@ -1,6 +1,74 @@
1
+ import { createNanoEvents } from 'nanoevents';
1
2
  export const isServicePluginModule = (val) => val.__type === 'service-plugin-definition';
2
- export function buildServicePluginDefinition(servicePluginDefinition, registrServicePluginImplementation, decodeJWT) {
3
- return (implementation) => {
4
- registrServicePluginImplementation(servicePluginDefinition, implementation);
3
+ export function servicePluginsModules(authStrategy) {
4
+ const servicePluginsImplementations = new Map();
5
+ const servicePluginsEmitter = createNanoEvents();
6
+ const client = {
7
+ ...servicePluginsEmitter,
8
+ getRegisteredServicePlugins: () => servicePluginsImplementations,
9
+ async parseJWT(jwt) {
10
+ if (!authStrategy.decodeJWT) {
11
+ throw new Error('decodeJWT is not supported by the authentication strategy');
12
+ }
13
+ const { decoded, valid } = await authStrategy.decodeJWT(jwt, true);
14
+ if (!valid) {
15
+ throw new Error('JWT is not valid');
16
+ }
17
+ if (typeof decoded.data !== 'object' ||
18
+ decoded.data === null ||
19
+ !('metadata' in decoded.data) ||
20
+ typeof decoded.data.metadata !== 'object' ||
21
+ decoded.data.metadata === null ||
22
+ !('appExtensionType' in decoded.data.metadata) ||
23
+ typeof decoded.data.metadata.appExtensionType !== 'string') {
24
+ throw new Error('Unexpected JWT data: expected object with metadata.appExtensionType string');
25
+ }
26
+ return decoded.data;
27
+ },
28
+ async process(request) {
29
+ const servicePluginRequest = await this.parseJWT(request.body);
30
+ return this.executeHandler(servicePluginRequest, request.url);
31
+ },
32
+ async parseRequest(request) {
33
+ const body = await request.text();
34
+ return this.parseJWT(body);
35
+ },
36
+ async processRequest(request) {
37
+ const url = request.url;
38
+ const body = await request.text();
39
+ const implMethodResult = await this.process({ url, body });
40
+ return Response.json(implMethodResult);
41
+ },
42
+ async executeHandler(servicePluginRequest, url) {
43
+ const componentType = servicePluginRequest.metadata.appExtensionType.toLowerCase();
44
+ const implementations = servicePluginsImplementations.get(componentType) ?? [];
45
+ if (implementations.length === 0) {
46
+ throw new Error(`No service plugin implementations found for component type ${componentType}`);
47
+ }
48
+ else if (implementations.length > 1) {
49
+ throw new Error(`Multiple service plugin implementations found for component type ${componentType}. This is currently not supported`);
50
+ }
51
+ const { implementation: impl, servicePluginDefinition } = implementations[0];
52
+ const method = servicePluginDefinition.methods.find((m) => url.endsWith(m.primaryHttpMappingPath));
53
+ if (!method) {
54
+ throw new Error('Unexpect request: request url did not match any method: ' + url);
55
+ }
56
+ const implMethod = impl[method.name];
57
+ if (!implMethod) {
58
+ throw new Error(`Got request for service plugin method ${method.name} but no implementation was provided. Available methods: ${Object.keys(impl).join(', ')}`);
59
+ }
60
+ return method.transformations.toREST(await implMethod(method.transformations.fromREST(servicePluginRequest)));
61
+ },
62
+ };
63
+ return {
64
+ initModule(servicePluginDefinition) {
65
+ return (implementation) => {
66
+ const implementations = servicePluginsImplementations.get(servicePluginDefinition.componentType.toLowerCase()) ?? [];
67
+ implementations.push({ servicePluginDefinition, implementation });
68
+ servicePluginsImplementations.set(servicePluginDefinition.componentType.toLowerCase(), implementations);
69
+ servicePluginsEmitter.emit('registered', servicePluginDefinition);
70
+ };
71
+ },
72
+ client,
5
73
  };
6
74
  }
@@ -1,6 +1,8 @@
1
- import { AuthenticationStrategy, BoundAuthenticationStrategy, BuildDescriptors, Descriptors, EventDefinition, EventIdentity, Host, HostModule, RESTFunctionDescriptor, ServicePluginContract, ServicePluginDefinition } from '@wix/sdk-types';
1
+ import { AuthenticationStrategy, BoundAuthenticationStrategy, BuildDescriptors, Descriptors, Host, HostModule, RESTFunctionDescriptor } from '@wix/sdk-types';
2
2
  import { EmptyObject } from 'type-fest/source/empty-object.js';
3
3
  import type { GraphQLFormattedError } from 'graphql';
4
+ import { EventHandlersClient } from './event-handlers-modules.js';
5
+ import { ServicePluginsClient } from './service-plugin-modules.js';
4
6
  export type ContextType = 'global' | 'module';
5
7
  type Headers = Record<string, string>;
6
8
  /**
@@ -42,55 +44,9 @@ export type WixClient<H extends Host<any> | undefined = undefined, Z extends Aut
42
44
  data: Result;
43
45
  errors?: GraphQLFormattedError[];
44
46
  }>;
45
- webhooks: {
46
- getRegisteredEvents(): string[];
47
- process<ExpectedEvents extends EventDefinition<any>[] = []>(jwt: string, opts?: {
48
- expectedEvents: ExpectedEvents;
49
- }): Promise<ProcessedEvent<ExpectedEvents>>;
50
- processRequest<ExpectedEvents extends EventDefinition<any>[] = []>(request: Request, opts?: {
51
- expectedEvents: ExpectedEvents;
52
- }): Promise<ProcessedEvent<ExpectedEvents>>;
53
- parseJWT(jwt: string): Promise<ProcessedEvent>;
54
- parseRequest(request: Request): Promise<ProcessedEvent>;
55
- executeHandlers(event: ProcessedEvent): Promise<void>;
56
- apps: {
57
- AppInstalled: EventDefinition<{
58
- appId: string;
59
- originInstanceId: string;
60
- }, 'AppInstalled'>;
61
- AppRemoved: EventDefinition<{
62
- appId: string;
63
- }, 'AppRemoved'>;
64
- };
65
- };
66
- servicePlugins: {
67
- getRegisteredServicePlugins(): Map<string, {
68
- servicePluginDefinition: ServicePluginDefinition<any>;
69
- implementation: ServicePluginContract;
70
- }[]>;
71
- process(request: {
72
- url: string;
73
- body: string;
74
- }): Promise<unknown>;
75
- processRequest(request: Request): Promise<Response>;
76
- parseJWT(jwt: string): Promise<unknown>;
77
- parseRequest(request: Request): Promise<unknown>;
78
- executeHandler(servicePluginRequest: ProcessedEvent): Promise<void>;
79
- };
47
+ webhooks: EventHandlersClient;
48
+ servicePlugins: ServicePluginsClient;
80
49
  } & BuildDescriptors<T, H>;
81
- type ResolvePossibleEvents<T extends EventDefinition<any>[]> = {
82
- [K in keyof T]: T[K] extends EventDefinition<any> ? {
83
- eventType: T[K]['type'];
84
- payload: T[K]['__payload'];
85
- } : never;
86
- } extends (infer U)[] ? U : never;
87
- export type ProcessedEvent<T extends EventDefinition<any>[] = []> = {
88
- instanceId: string;
89
- identity?: EventIdentity;
90
- } & (T['length'] extends 0 ? {
91
- eventType: string;
92
- payload: unknown;
93
- } : ResolvePossibleEvents<T>);
94
50
  export declare function createClient<H extends Host<any> | undefined = undefined, Z extends AuthenticationStrategy<H> = AuthenticationStrategy<H>, T extends Descriptors = EmptyObject>(config: {
95
51
  modules?: H extends Host<any> ? AssertHostMatches<T, H> : T;
96
52
  auth?: Z;
@@ -1,23 +1,23 @@
1
1
  import { wixContext } from '@wix/sdk-context';
2
- import { EventDefinition, SERVICE_PLUGIN_ERROR_TYPE, } from '@wix/sdk-types';
2
+ import { SERVICE_PLUGIN_ERROR_TYPE, } from '@wix/sdk-types';
3
3
  import { ambassadorModuleOptions, isAmbassadorModule, toHTTPModule, } from './ambassador-modules.js';
4
4
  import { API_URL, PUBLIC_METADATA_KEY } from './common.js';
5
5
  import { FetchErrorResponse } from './fetch-error.js';
6
6
  import { getDefaultContentHeader, isObject } from './helpers.js';
7
7
  import { buildHostModule, isHostModule } from './host-modules.js';
8
8
  import { buildRESTDescriptor } from './rest-modules.js';
9
- import { buildEventDefinition, isEventHandlerModule, runHandler, } from './event-handlers-modules.js';
10
- import { buildServicePluginDefinition, isServicePluginModule, } from './service-plugin-modules.js';
9
+ import { eventHandlersModules, isEventHandlerModule, } from './event-handlers-modules.js';
10
+ import { isServicePluginModule, servicePluginsModules, } from './service-plugin-modules.js';
11
11
  export function createClient(config) {
12
12
  const _headers = config.headers || { Authorization: '' };
13
- const eventHandlers = new Map();
14
- const servicePluginsImplementations = new Map();
15
13
  const authStrategy = config.auth ||
16
14
  {
17
15
  getAuthHeaders: (_) => Promise.resolve({ headers: {} }),
18
16
  };
19
17
  const boundGetAuthHeaders = authStrategy.getAuthHeaders.bind(undefined, config.host);
20
18
  authStrategy.getAuthHeaders = boundGetAuthHeaders;
19
+ const { client: servicePluginsClient, initModule: initServicePluginModule } = servicePluginsModules(authStrategy);
20
+ const { client: eventHandlersClient, initModule: initEventHandlerModule } = eventHandlersModules(authStrategy);
21
21
  const boundFetch = async (url, options) => {
22
22
  const authHeaders = await boundGetAuthHeaders();
23
23
  const defaultContentTypeHeader = getDefaultContentHeader(options);
@@ -36,18 +36,10 @@ export function createClient(config) {
36
36
  // excessively deep and possibly infinite.`
37
37
  const use = (modules, metadata) => {
38
38
  if (isEventHandlerModule(modules)) {
39
- return buildEventDefinition(modules, (eventDefinition, handler) => {
40
- const handlers = eventHandlers.get(eventDefinition.type) ?? [];
41
- handlers.push({ eventDefinition, handler });
42
- eventHandlers.set(eventDefinition.type, handlers);
43
- });
39
+ return initEventHandlerModule(modules);
44
40
  }
45
41
  else if (isServicePluginModule(modules)) {
46
- return buildServicePluginDefinition(modules, (servicePluginDefinition, implementation) => {
47
- const implementations = servicePluginsImplementations.get(servicePluginDefinition.componentType.toLowerCase()) ?? [];
48
- implementations.push({ servicePluginDefinition, implementation });
49
- servicePluginsImplementations.set(servicePluginDefinition.componentType.toLowerCase(), implementations);
50
- });
42
+ return initServicePluginModule(modules);
51
43
  }
52
44
  else if (isHostModule(modules) && config.host) {
53
45
  return buildHostModule(modules, config.host);
@@ -161,137 +153,7 @@ export function createClient(config) {
161
153
  const { data, errors } = await res.json();
162
154
  return { data: data ?? {}, errors };
163
155
  },
164
- webhooks: {
165
- getRegisteredEvents: () => Array.from(eventHandlers.keys()),
166
- async process(jwt, opts = {
167
- expectedEvents: [],
168
- }) {
169
- const { eventType, identity, instanceId, payload } = await this.parseJWT(jwt);
170
- const allExpectedEvents = [
171
- ...opts.expectedEvents,
172
- ...Array.from(eventHandlers.keys()).map((type) => ({ type })),
173
- ];
174
- if (allExpectedEvents.length > 0 &&
175
- !allExpectedEvents.some(({ type }) => type === eventType)) {
176
- throw new Error(`Unexpected event type: ${eventType}. Expected one of: ${allExpectedEvents
177
- .map((x) => x.type)
178
- .join(', ')}`);
179
- }
180
- const handlers = eventHandlers.get(eventType) ?? [];
181
- await Promise.all(handlers.map(({ eventDefinition, handler }) => runHandler(eventDefinition, handler, payload, {
182
- instanceId,
183
- identity,
184
- })));
185
- return {
186
- instanceId,
187
- eventType,
188
- payload,
189
- identity,
190
- };
191
- },
192
- async processRequest(request, opts) {
193
- const body = await request.text();
194
- return this.process(body, opts);
195
- },
196
- async parseJWT(jwt) {
197
- if (!authStrategy.decodeJWT) {
198
- throw new Error('decodeJWT is not supported by the authentication strategy');
199
- }
200
- const { decoded, valid } = await authStrategy.decodeJWT(jwt);
201
- if (!valid) {
202
- throw new Error('JWT is not valid');
203
- }
204
- if (typeof decoded.data !== 'string') {
205
- throw new Error(`Unexpected type of JWT data: expected string, got ${typeof decoded.data}`);
206
- }
207
- const parsedDecoded = JSON.parse(decoded.data);
208
- const eventType = parsedDecoded.eventType;
209
- const instanceId = parsedDecoded.instanceId;
210
- const identity = parsedDecoded.identity
211
- ? JSON.parse(parsedDecoded.identity)
212
- : undefined;
213
- const payload = JSON.parse(parsedDecoded.data);
214
- return {
215
- instanceId,
216
- eventType,
217
- payload,
218
- identity,
219
- };
220
- },
221
- async parseRequest(request) {
222
- const jwt = await request.text();
223
- return this.parseJWT(jwt);
224
- },
225
- async executeHandlers(event) {
226
- const allExpectedEvents = Array.from(eventHandlers.keys()).map((type) => ({ type }));
227
- if (allExpectedEvents.length > 0 &&
228
- !allExpectedEvents.some(({ type }) => type === event.eventType)) {
229
- throw new Error(`Unexpected event type: ${event.eventType}. Expected one of: ${allExpectedEvents
230
- .map((x) => x.type)
231
- .join(', ')}`);
232
- }
233
- const handlers = eventHandlers.get(event.eventType) ?? [];
234
- await Promise.all(handlers.map(({ eventDefinition, handler }) => runHandler(eventDefinition, handler, event.payload, {
235
- instanceId: event.instanceId,
236
- identity: event.identity,
237
- })));
238
- },
239
- apps: {
240
- AppInstalled: EventDefinition('AppInstalled')(),
241
- AppRemoved: EventDefinition('AppRemoved')(),
242
- },
243
- },
244
- servicePlugins: {
245
- getRegisteredServicePlugins: () => servicePluginsImplementations,
246
- async process(request) {
247
- const servicePluginRequest = await this.parseJWT(request.body);
248
- return this.executeHandler(servicePluginRequest, request.url);
249
- },
250
- async processRequest(request) {
251
- const url = request.url;
252
- const body = await request.text();
253
- const implMethodResult = await this.process({ url, body });
254
- return Response.json(implMethodResult);
255
- },
256
- async parseJWT(jwt) {
257
- if (!authStrategy.decodeJWT) {
258
- throw new Error('decodeJWT is not supported by the authentication strategy');
259
- }
260
- const { decoded, valid } = await authStrategy.decodeJWT(jwt, true);
261
- if (!valid) {
262
- throw new Error('JWT is not valid');
263
- }
264
- if (typeof decoded.data !== 'object' ||
265
- decoded.data === null ||
266
- !('metadata' in decoded.data) ||
267
- typeof decoded.data.metadata !== 'object' ||
268
- decoded.data.metadata === null ||
269
- !('appExtensionType' in decoded.data.metadata) ||
270
- typeof decoded.data.metadata.appExtensionType !== 'string') {
271
- throw new Error('Unexpected JWT data: expected object with metadata.appExtensionType string');
272
- }
273
- return decoded.data;
274
- },
275
- async executeHandler(servicePluginRequest, url) {
276
- const componentType = servicePluginRequest.metadata.appExtensionType.toLowerCase();
277
- const implementations = servicePluginsImplementations.get(componentType) ?? [];
278
- if (implementations.length === 0) {
279
- throw new Error(`No service plugin implementations found for component type ${componentType}`);
280
- }
281
- else if (implementations.length > 1) {
282
- throw new Error(`Multiple service plugin implementations found for component type ${componentType}. This is currently not supported`);
283
- }
284
- const { implementation: impl, servicePluginDefinition } = implementations[0];
285
- const method = servicePluginDefinition.methods.find((m) => url.endsWith(m.primaryHttpMappingPath));
286
- if (!method) {
287
- throw new Error('Unexpect request: request url did not match any method: ' + url);
288
- }
289
- const implMethod = impl[method.name];
290
- if (!implMethod) {
291
- throw new Error(`Got request for service plugin method ${method.name} but no implementation was provided. Available methods: ${Object.keys(impl).join(', ')}`);
292
- }
293
- return method.transformations.toREST(await implMethod(method.transformations.fromREST(servicePluginRequest)));
294
- },
295
- },
156
+ webhooks: eventHandlersClient,
157
+ servicePlugins: servicePluginsClient,
296
158
  };
297
159
  }
@@ -73,6 +73,7 @@ export declare function AppStrategy(opts: {
73
73
  appId: string;
74
74
  appSecret?: string;
75
75
  publicKey?: string;
76
+ authServerBaseUrl?: string;
76
77
  } & ({
77
78
  refreshToken?: string;
78
79
  } | {
@@ -66,6 +66,7 @@ const helpers_js_1 = require("../helpers.js");
66
66
  */
67
67
  // eslint-disable-next-line @typescript-eslint/no-redeclare
68
68
  function AppStrategy(opts) {
69
+ const authServerBaseUrl = opts.authServerBaseUrl ?? 'https://www.wixapis.com';
69
70
  let refreshToken = 'refreshToken' in opts ? opts.refreshToken : undefined;
70
71
  return {
71
72
  getInstallUrl({ redirectUrl, token, state }) {
@@ -94,7 +95,8 @@ function AppStrategy(opts) {
94
95
  if (!code || !instanceId) {
95
96
  throw new Error('Invalid OAuth callback URL. Make sure you pass the url including the code and instanceId query params.');
96
97
  }
97
- const tokensRes = await fetch('https://www.wixapis.com/oauth/access', {
98
+ const tokenUrl = new URL('/oauth/access', authServerBaseUrl);
99
+ const tokensRes = await fetch(tokenUrl.href, {
98
100
  method: 'POST',
99
101
  headers: {
100
102
  'Content-Type': 'application/json',
@@ -122,7 +124,8 @@ function AppStrategy(opts) {
122
124
  if (!opts.appSecret) {
123
125
  throw new Error('App secret is required for retrieveing app-level access tokens. Make sure to pass it to the AppStrategy');
124
126
  }
125
- const tokensRes = await fetch('https://www.wixapis.com/oauth/access', {
127
+ const tokenUrl = new URL('/oauth/access', authServerBaseUrl);
128
+ const tokensRes = await fetch(tokenUrl.href, {
126
129
  method: 'POST',
127
130
  headers: {
128
131
  'Content-Type': 'application/json',
@@ -149,7 +152,8 @@ function AppStrategy(opts) {
149
152
  if (!opts.appSecret) {
150
153
  throw new Error('App secret is required for retrieveing app-level access tokens. Make sure to pass it to the AppStrategy');
151
154
  }
152
- const tokensRes = await fetch('https://www.wixapis.com/oauth2/token', {
155
+ const tokenUrl = new URL('/oauth2/token', authServerBaseUrl);
156
+ const tokensRes = await fetch(tokenUrl.href, {
153
157
  method: 'POST',
154
158
  headers: {
155
159
  'Content-Type': 'application/json',
@@ -184,7 +188,7 @@ function AppStrategy(opts) {
184
188
  },
185
189
  async elevated() {
186
190
  if ('accessToken' in opts && opts.accessToken) {
187
- const tokenInfo = await getTokenInfo(opts.accessToken);
191
+ const tokenInfo = await getTokenInfo(opts.accessToken, authServerBaseUrl);
188
192
  if (tokenInfo.clientId !== opts.appId) {
189
193
  throw new Error(`Invalid access token. The token is not issued for the app with ID "${opts.appId}"`);
190
194
  }
@@ -196,6 +200,7 @@ function AppStrategy(opts) {
196
200
  appSecret: opts.appSecret,
197
201
  publicKey: opts.publicKey,
198
202
  instanceId: tokenInfo.instanceId,
203
+ authServerBaseUrl: opts.authServerBaseUrl,
199
204
  });
200
205
  }
201
206
  else {
@@ -226,13 +231,14 @@ function AppStrategy(opts) {
226
231
  if (!tokenToCheck) {
227
232
  throw new Error('Missing token to get info for. Either pass the token as an argument or provide it when initializing the AppStrategy');
228
233
  }
229
- return getTokenInfo(tokenToCheck);
234
+ return getTokenInfo(tokenToCheck, authServerBaseUrl);
230
235
  },
231
236
  };
232
237
  }
233
238
  exports.AppStrategy = AppStrategy;
234
- async function getTokenInfo(token) {
235
- const tokenInfoRes = await fetch('https://www.wixapis.com/oauth2/token-info', {
239
+ async function getTokenInfo(token, authServerBaseUrl) {
240
+ const tokenInfoUrl = new URL('/oauth2/token-info', authServerBaseUrl);
241
+ const tokenInfoRes = await fetch(tokenInfoUrl.href, {
236
242
  method: 'POST',
237
243
  headers: {
238
244
  'Content-Type': 'application/json',
@@ -1,4 +1,48 @@
1
- import { BaseEventMetadata, EventDefinition, EventHandler } from '@wix/sdk-types';
1
+ import { AuthenticationStrategy, EventDefinition, EventHandler, EventIdentity } from '@wix/sdk-types';
2
+ import { Emitter } from 'nanoevents';
2
3
  export declare const isEventHandlerModule: (val: any) => val is EventDefinition<unknown, string>;
3
4
  export declare function buildEventDefinition<T extends EventDefinition<any, string>>(eventDefinition: T, registerHandler: (eventDefinition: T, handler: EventHandler<T>) => void): (handler: EventHandler<T>) => void;
4
- export declare function runHandler<T extends EventDefinition>(eventDefinition: T, handler: EventHandler<T>, payload: unknown, baseEventMetadata: BaseEventMetadata): void | Promise<void>;
5
+ type ResolvePossibleEvents<T extends EventDefinition<any>[]> = {
6
+ [K in keyof T]: T[K] extends EventDefinition<any> ? {
7
+ eventType: T[K]['type'];
8
+ payload: T[K]['__payload'];
9
+ } : never;
10
+ } extends (infer U)[] ? U : never;
11
+ export type ProcessedEvent<T extends EventDefinition<any>[] = []> = {
12
+ instanceId: string;
13
+ identity?: EventIdentity;
14
+ } & (T['length'] extends 0 ? {
15
+ eventType: string;
16
+ payload: unknown;
17
+ } : ResolvePossibleEvents<T>);
18
+ export type EventHandlersClient = Emitter<{
19
+ registered: (event: EventDefinition<any>) => void;
20
+ }> & {
21
+ getRegisteredEvents(): Map<string, {
22
+ eventDefinition: EventDefinition;
23
+ handler: EventHandler<EventDefinition>;
24
+ }[]>;
25
+ process<ExpectedEvents extends EventDefinition<any>[] = []>(jwt: string, opts?: {
26
+ expectedEvents: ExpectedEvents;
27
+ }): Promise<ProcessedEvent<ExpectedEvents>>;
28
+ processRequest<ExpectedEvents extends EventDefinition<any>[] = []>(request: Request, opts?: {
29
+ expectedEvents: ExpectedEvents;
30
+ }): Promise<ProcessedEvent<ExpectedEvents>>;
31
+ parseJWT(jwt: string): Promise<ProcessedEvent>;
32
+ parseRequest(request: Request): Promise<ProcessedEvent>;
33
+ executeHandlers(event: ProcessedEvent): Promise<void>;
34
+ apps: {
35
+ AppInstalled: EventDefinition<{
36
+ appId: string;
37
+ originInstanceId: string;
38
+ }, 'AppInstalled'>;
39
+ AppRemoved: EventDefinition<{
40
+ appId: string;
41
+ }, 'AppRemoved'>;
42
+ };
43
+ };
44
+ export declare function eventHandlersModules(authStrategy: AuthenticationStrategy<any>): {
45
+ initModule(eventDefinition: EventDefinition<any, string>): (handler: EventHandler<EventDefinition<any, string>>) => void;
46
+ client: EventHandlersClient;
47
+ };
48
+ export {};
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.runHandler = exports.buildEventDefinition = exports.isEventHandlerModule = void 0;
3
+ exports.eventHandlersModules = exports.buildEventDefinition = exports.isEventHandlerModule = void 0;
4
+ const sdk_types_1 = require("@wix/sdk-types");
5
+ const nanoevents_1 = require("nanoevents");
4
6
  const isEventHandlerModule = (val) => val.__type === 'event-definition';
5
7
  exports.isEventHandlerModule = isEventHandlerModule;
6
8
  function buildEventDefinition(eventDefinition, registerHandler) {
@@ -51,4 +53,100 @@ function runHandler(eventDefinition, handler, payload, baseEventMetadata) {
51
53
  const transformFromRESTFn = eventDefinition.transformations ?? ((x) => x);
52
54
  return handler(transformFromRESTFn(envelope));
53
55
  }
54
- exports.runHandler = runHandler;
56
+ function eventHandlersModules(authStrategy) {
57
+ const eventHandlers = new Map();
58
+ const webhooksEmitter = (0, nanoevents_1.createNanoEvents)();
59
+ const client = {
60
+ ...webhooksEmitter,
61
+ getRegisteredEvents: () => eventHandlers,
62
+ async process(jwt, opts = {
63
+ expectedEvents: [],
64
+ }) {
65
+ const { eventType, identity, instanceId, payload } = await this.parseJWT(jwt);
66
+ const allExpectedEvents = [
67
+ ...opts.expectedEvents,
68
+ ...Array.from(eventHandlers.keys()).map((type) => ({ type })),
69
+ ];
70
+ if (allExpectedEvents.length > 0 &&
71
+ !allExpectedEvents.some(({ type }) => type === eventType)) {
72
+ throw new Error(`Unexpected event type: ${eventType}. Expected one of: ${allExpectedEvents
73
+ .map((x) => x.type)
74
+ .join(', ')}`);
75
+ }
76
+ const handlers = eventHandlers.get(eventType) ?? [];
77
+ await Promise.all(handlers.map(({ eventDefinition, handler }) => runHandler(eventDefinition, handler, payload, {
78
+ instanceId,
79
+ identity,
80
+ })));
81
+ return {
82
+ instanceId,
83
+ eventType,
84
+ payload,
85
+ identity,
86
+ };
87
+ },
88
+ async processRequest(request, opts) {
89
+ const body = await request.text();
90
+ return this.process(body, opts);
91
+ },
92
+ async parseJWT(jwt) {
93
+ if (!authStrategy.decodeJWT) {
94
+ throw new Error('decodeJWT is not supported by the authentication strategy');
95
+ }
96
+ const { decoded, valid } = await authStrategy.decodeJWT(jwt);
97
+ if (!valid) {
98
+ throw new Error('JWT is not valid');
99
+ }
100
+ if (typeof decoded.data !== 'string') {
101
+ throw new Error(`Unexpected type of JWT data: expected string, got ${typeof decoded.data}`);
102
+ }
103
+ const parsedDecoded = JSON.parse(decoded.data);
104
+ const eventType = parsedDecoded.eventType;
105
+ const instanceId = parsedDecoded.instanceId;
106
+ const identity = parsedDecoded.identity
107
+ ? JSON.parse(parsedDecoded.identity)
108
+ : undefined;
109
+ const payload = JSON.parse(parsedDecoded.data);
110
+ return {
111
+ instanceId,
112
+ eventType,
113
+ payload,
114
+ identity,
115
+ };
116
+ },
117
+ async parseRequest(request) {
118
+ const jwt = await request.text();
119
+ return this.parseJWT(jwt);
120
+ },
121
+ async executeHandlers(event) {
122
+ const allExpectedEvents = Array.from(eventHandlers.keys()).map((type) => ({ type }));
123
+ if (allExpectedEvents.length > 0 &&
124
+ !allExpectedEvents.some(({ type }) => type === event.eventType)) {
125
+ throw new Error(`Unexpected event type: ${event.eventType}. Expected one of: ${allExpectedEvents
126
+ .map((x) => x.type)
127
+ .join(', ')}`);
128
+ }
129
+ const handlers = eventHandlers.get(event.eventType) ?? [];
130
+ await Promise.all(handlers.map(({ eventDefinition, handler }) => runHandler(eventDefinition, handler, event.payload, {
131
+ instanceId: event.instanceId,
132
+ identity: event.identity,
133
+ })));
134
+ },
135
+ apps: {
136
+ AppInstalled: (0, sdk_types_1.EventDefinition)('AppInstalled')(),
137
+ AppRemoved: (0, sdk_types_1.EventDefinition)('AppRemoved')(),
138
+ },
139
+ };
140
+ return {
141
+ initModule(eventDefinition) {
142
+ return (handler) => {
143
+ const handlers = eventHandlers.get(eventDefinition.type) ?? [];
144
+ handlers.push({ eventDefinition, handler });
145
+ eventHandlers.set(eventDefinition.type, handlers);
146
+ webhooksEmitter.emit('registered', eventDefinition);
147
+ };
148
+ },
149
+ client,
150
+ };
151
+ }
152
+ exports.eventHandlersModules = eventHandlersModules;
@@ -1,8 +1,33 @@
1
- import { ServicePluginContract, ServicePluginDefinition } from '@wix/sdk-types';
1
+ import { AuthenticationStrategy, ServicePluginContract, ServicePluginDefinition } from '@wix/sdk-types';
2
+ import { Emitter } from 'nanoevents';
2
3
  export declare const isServicePluginModule: (val: any) => val is ServicePluginDefinition<ServicePluginContract>;
3
- export declare function buildServicePluginDefinition<T extends ServicePluginDefinition<any>>(servicePluginDefinition: T, registrServicePluginImplementation: (servicePluginDefinition: T, implementation: T['__contract']) => void, decodeJWT?: (token: string, verifyCallerClaims?: boolean) => Promise<{
4
- decoded: {
5
- data: string;
6
- };
7
- valid: boolean;
8
- }>): (implementation: T['__contract']) => void;
4
+ export type UnknownServicePluginResponse = unknown;
5
+ type ServicePluginRequestMetadata = {
6
+ instanceId: string;
7
+ appExtensionType: string;
8
+ };
9
+ type ServicePluginRequest = {
10
+ metadata: ServicePluginRequestMetadata;
11
+ request: unknown;
12
+ };
13
+ export type ServicePluginsClient = Emitter<{
14
+ registered: (servicePluginDefinition: ServicePluginDefinition<ServicePluginContract>) => void;
15
+ }> & {
16
+ getRegisteredServicePlugins(): Map<string, {
17
+ servicePluginDefinition: ServicePluginDefinition<any>;
18
+ implementation: ServicePluginContract;
19
+ }[]>;
20
+ process(request: {
21
+ url: string;
22
+ body: string;
23
+ }): Promise<unknown>;
24
+ processRequest(request: Request): Promise<Response>;
25
+ parseJWT(jwt: string): Promise<ServicePluginRequest>;
26
+ parseRequest(request: Request): Promise<ServicePluginRequest>;
27
+ executeHandler(servicePluginRequest: ServicePluginRequest, url: string): Promise<UnknownServicePluginResponse>;
28
+ };
29
+ export declare function servicePluginsModules(authStrategy: AuthenticationStrategy<any>): {
30
+ initModule<T extends ServicePluginDefinition<any>>(servicePluginDefinition: T): (implementation: T["__contract"]) => void;
31
+ client: ServicePluginsClient;
32
+ };
33
+ export {};
@@ -1,11 +1,79 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.buildServicePluginDefinition = exports.isServicePluginModule = void 0;
3
+ exports.servicePluginsModules = exports.isServicePluginModule = void 0;
4
+ const nanoevents_1 = require("nanoevents");
4
5
  const isServicePluginModule = (val) => val.__type === 'service-plugin-definition';
5
6
  exports.isServicePluginModule = isServicePluginModule;
6
- function buildServicePluginDefinition(servicePluginDefinition, registrServicePluginImplementation, decodeJWT) {
7
- return (implementation) => {
8
- registrServicePluginImplementation(servicePluginDefinition, implementation);
7
+ function servicePluginsModules(authStrategy) {
8
+ const servicePluginsImplementations = new Map();
9
+ const servicePluginsEmitter = (0, nanoevents_1.createNanoEvents)();
10
+ const client = {
11
+ ...servicePluginsEmitter,
12
+ getRegisteredServicePlugins: () => servicePluginsImplementations,
13
+ async parseJWT(jwt) {
14
+ if (!authStrategy.decodeJWT) {
15
+ throw new Error('decodeJWT is not supported by the authentication strategy');
16
+ }
17
+ const { decoded, valid } = await authStrategy.decodeJWT(jwt, true);
18
+ if (!valid) {
19
+ throw new Error('JWT is not valid');
20
+ }
21
+ if (typeof decoded.data !== 'object' ||
22
+ decoded.data === null ||
23
+ !('metadata' in decoded.data) ||
24
+ typeof decoded.data.metadata !== 'object' ||
25
+ decoded.data.metadata === null ||
26
+ !('appExtensionType' in decoded.data.metadata) ||
27
+ typeof decoded.data.metadata.appExtensionType !== 'string') {
28
+ throw new Error('Unexpected JWT data: expected object with metadata.appExtensionType string');
29
+ }
30
+ return decoded.data;
31
+ },
32
+ async process(request) {
33
+ const servicePluginRequest = await this.parseJWT(request.body);
34
+ return this.executeHandler(servicePluginRequest, request.url);
35
+ },
36
+ async parseRequest(request) {
37
+ const body = await request.text();
38
+ return this.parseJWT(body);
39
+ },
40
+ async processRequest(request) {
41
+ const url = request.url;
42
+ const body = await request.text();
43
+ const implMethodResult = await this.process({ url, body });
44
+ return Response.json(implMethodResult);
45
+ },
46
+ async executeHandler(servicePluginRequest, url) {
47
+ const componentType = servicePluginRequest.metadata.appExtensionType.toLowerCase();
48
+ const implementations = servicePluginsImplementations.get(componentType) ?? [];
49
+ if (implementations.length === 0) {
50
+ throw new Error(`No service plugin implementations found for component type ${componentType}`);
51
+ }
52
+ else if (implementations.length > 1) {
53
+ throw new Error(`Multiple service plugin implementations found for component type ${componentType}. This is currently not supported`);
54
+ }
55
+ const { implementation: impl, servicePluginDefinition } = implementations[0];
56
+ const method = servicePluginDefinition.methods.find((m) => url.endsWith(m.primaryHttpMappingPath));
57
+ if (!method) {
58
+ throw new Error('Unexpect request: request url did not match any method: ' + url);
59
+ }
60
+ const implMethod = impl[method.name];
61
+ if (!implMethod) {
62
+ throw new Error(`Got request for service plugin method ${method.name} but no implementation was provided. Available methods: ${Object.keys(impl).join(', ')}`);
63
+ }
64
+ return method.transformations.toREST(await implMethod(method.transformations.fromREST(servicePluginRequest)));
65
+ },
66
+ };
67
+ return {
68
+ initModule(servicePluginDefinition) {
69
+ return (implementation) => {
70
+ const implementations = servicePluginsImplementations.get(servicePluginDefinition.componentType.toLowerCase()) ?? [];
71
+ implementations.push({ servicePluginDefinition, implementation });
72
+ servicePluginsImplementations.set(servicePluginDefinition.componentType.toLowerCase(), implementations);
73
+ servicePluginsEmitter.emit('registered', servicePluginDefinition);
74
+ };
75
+ },
76
+ client,
9
77
  };
10
78
  }
11
- exports.buildServicePluginDefinition = buildServicePluginDefinition;
79
+ exports.servicePluginsModules = servicePluginsModules;
@@ -1,6 +1,8 @@
1
- import { AuthenticationStrategy, BoundAuthenticationStrategy, BuildDescriptors, Descriptors, EventDefinition, EventIdentity, Host, HostModule, RESTFunctionDescriptor, ServicePluginContract, ServicePluginDefinition } from '@wix/sdk-types';
1
+ import { AuthenticationStrategy, BoundAuthenticationStrategy, BuildDescriptors, Descriptors, Host, HostModule, RESTFunctionDescriptor } from '@wix/sdk-types';
2
2
  import { EmptyObject } from 'type-fest/source/empty-object.js';
3
3
  import type { GraphQLFormattedError } from 'graphql';
4
+ import { EventHandlersClient } from './event-handlers-modules.js';
5
+ import { ServicePluginsClient } from './service-plugin-modules.js';
4
6
  export type ContextType = 'global' | 'module';
5
7
  type Headers = Record<string, string>;
6
8
  /**
@@ -42,55 +44,9 @@ export type WixClient<H extends Host<any> | undefined = undefined, Z extends Aut
42
44
  data: Result;
43
45
  errors?: GraphQLFormattedError[];
44
46
  }>;
45
- webhooks: {
46
- getRegisteredEvents(): string[];
47
- process<ExpectedEvents extends EventDefinition<any>[] = []>(jwt: string, opts?: {
48
- expectedEvents: ExpectedEvents;
49
- }): Promise<ProcessedEvent<ExpectedEvents>>;
50
- processRequest<ExpectedEvents extends EventDefinition<any>[] = []>(request: Request, opts?: {
51
- expectedEvents: ExpectedEvents;
52
- }): Promise<ProcessedEvent<ExpectedEvents>>;
53
- parseJWT(jwt: string): Promise<ProcessedEvent>;
54
- parseRequest(request: Request): Promise<ProcessedEvent>;
55
- executeHandlers(event: ProcessedEvent): Promise<void>;
56
- apps: {
57
- AppInstalled: EventDefinition<{
58
- appId: string;
59
- originInstanceId: string;
60
- }, 'AppInstalled'>;
61
- AppRemoved: EventDefinition<{
62
- appId: string;
63
- }, 'AppRemoved'>;
64
- };
65
- };
66
- servicePlugins: {
67
- getRegisteredServicePlugins(): Map<string, {
68
- servicePluginDefinition: ServicePluginDefinition<any>;
69
- implementation: ServicePluginContract;
70
- }[]>;
71
- process(request: {
72
- url: string;
73
- body: string;
74
- }): Promise<unknown>;
75
- processRequest(request: Request): Promise<Response>;
76
- parseJWT(jwt: string): Promise<unknown>;
77
- parseRequest(request: Request): Promise<unknown>;
78
- executeHandler(servicePluginRequest: ProcessedEvent): Promise<void>;
79
- };
47
+ webhooks: EventHandlersClient;
48
+ servicePlugins: ServicePluginsClient;
80
49
  } & BuildDescriptors<T, H>;
81
- type ResolvePossibleEvents<T extends EventDefinition<any>[]> = {
82
- [K in keyof T]: T[K] extends EventDefinition<any> ? {
83
- eventType: T[K]['type'];
84
- payload: T[K]['__payload'];
85
- } : never;
86
- } extends (infer U)[] ? U : never;
87
- export type ProcessedEvent<T extends EventDefinition<any>[] = []> = {
88
- instanceId: string;
89
- identity?: EventIdentity;
90
- } & (T['length'] extends 0 ? {
91
- eventType: string;
92
- payload: unknown;
93
- } : ResolvePossibleEvents<T>);
94
50
  export declare function createClient<H extends Host<any> | undefined = undefined, Z extends AuthenticationStrategy<H> = AuthenticationStrategy<H>, T extends Descriptors = EmptyObject>(config: {
95
51
  modules?: H extends Host<any> ? AssertHostMatches<T, H> : T;
96
52
  auth?: Z;
@@ -13,14 +13,14 @@ 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
  function createClient(config) {
15
15
  const _headers = config.headers || { Authorization: '' };
16
- const eventHandlers = new Map();
17
- const servicePluginsImplementations = new Map();
18
16
  const authStrategy = config.auth ||
19
17
  {
20
18
  getAuthHeaders: (_) => Promise.resolve({ headers: {} }),
21
19
  };
22
20
  const boundGetAuthHeaders = authStrategy.getAuthHeaders.bind(undefined, config.host);
23
21
  authStrategy.getAuthHeaders = boundGetAuthHeaders;
22
+ const { client: servicePluginsClient, initModule: initServicePluginModule } = (0, service_plugin_modules_js_1.servicePluginsModules)(authStrategy);
23
+ const { client: eventHandlersClient, initModule: initEventHandlerModule } = (0, event_handlers_modules_js_1.eventHandlersModules)(authStrategy);
24
24
  const boundFetch = async (url, options) => {
25
25
  const authHeaders = await boundGetAuthHeaders();
26
26
  const defaultContentTypeHeader = (0, helpers_js_1.getDefaultContentHeader)(options);
@@ -39,18 +39,10 @@ function createClient(config) {
39
39
  // excessively deep and possibly infinite.`
40
40
  const use = (modules, metadata) => {
41
41
  if ((0, event_handlers_modules_js_1.isEventHandlerModule)(modules)) {
42
- return (0, event_handlers_modules_js_1.buildEventDefinition)(modules, (eventDefinition, handler) => {
43
- const handlers = eventHandlers.get(eventDefinition.type) ?? [];
44
- handlers.push({ eventDefinition, handler });
45
- eventHandlers.set(eventDefinition.type, handlers);
46
- });
42
+ return initEventHandlerModule(modules);
47
43
  }
48
44
  else if ((0, service_plugin_modules_js_1.isServicePluginModule)(modules)) {
49
- return (0, service_plugin_modules_js_1.buildServicePluginDefinition)(modules, (servicePluginDefinition, implementation) => {
50
- const implementations = servicePluginsImplementations.get(servicePluginDefinition.componentType.toLowerCase()) ?? [];
51
- implementations.push({ servicePluginDefinition, implementation });
52
- servicePluginsImplementations.set(servicePluginDefinition.componentType.toLowerCase(), implementations);
53
- });
45
+ return initServicePluginModule(modules);
54
46
  }
55
47
  else if ((0, host_modules_js_1.isHostModule)(modules) && config.host) {
56
48
  return (0, host_modules_js_1.buildHostModule)(modules, config.host);
@@ -164,138 +156,8 @@ function createClient(config) {
164
156
  const { data, errors } = await res.json();
165
157
  return { data: data ?? {}, errors };
166
158
  },
167
- webhooks: {
168
- getRegisteredEvents: () => Array.from(eventHandlers.keys()),
169
- async process(jwt, opts = {
170
- expectedEvents: [],
171
- }) {
172
- const { eventType, identity, instanceId, payload } = await this.parseJWT(jwt);
173
- const allExpectedEvents = [
174
- ...opts.expectedEvents,
175
- ...Array.from(eventHandlers.keys()).map((type) => ({ type })),
176
- ];
177
- if (allExpectedEvents.length > 0 &&
178
- !allExpectedEvents.some(({ type }) => type === eventType)) {
179
- throw new Error(`Unexpected event type: ${eventType}. Expected one of: ${allExpectedEvents
180
- .map((x) => x.type)
181
- .join(', ')}`);
182
- }
183
- const handlers = eventHandlers.get(eventType) ?? [];
184
- await Promise.all(handlers.map(({ eventDefinition, handler }) => (0, event_handlers_modules_js_1.runHandler)(eventDefinition, handler, payload, {
185
- instanceId,
186
- identity,
187
- })));
188
- return {
189
- instanceId,
190
- eventType,
191
- payload,
192
- identity,
193
- };
194
- },
195
- async processRequest(request, opts) {
196
- const body = await request.text();
197
- return this.process(body, opts);
198
- },
199
- async parseJWT(jwt) {
200
- if (!authStrategy.decodeJWT) {
201
- throw new Error('decodeJWT is not supported by the authentication strategy');
202
- }
203
- const { decoded, valid } = await authStrategy.decodeJWT(jwt);
204
- if (!valid) {
205
- throw new Error('JWT is not valid');
206
- }
207
- if (typeof decoded.data !== 'string') {
208
- throw new Error(`Unexpected type of JWT data: expected string, got ${typeof decoded.data}`);
209
- }
210
- const parsedDecoded = JSON.parse(decoded.data);
211
- const eventType = parsedDecoded.eventType;
212
- const instanceId = parsedDecoded.instanceId;
213
- const identity = parsedDecoded.identity
214
- ? JSON.parse(parsedDecoded.identity)
215
- : undefined;
216
- const payload = JSON.parse(parsedDecoded.data);
217
- return {
218
- instanceId,
219
- eventType,
220
- payload,
221
- identity,
222
- };
223
- },
224
- async parseRequest(request) {
225
- const jwt = await request.text();
226
- return this.parseJWT(jwt);
227
- },
228
- async executeHandlers(event) {
229
- const allExpectedEvents = Array.from(eventHandlers.keys()).map((type) => ({ type }));
230
- if (allExpectedEvents.length > 0 &&
231
- !allExpectedEvents.some(({ type }) => type === event.eventType)) {
232
- throw new Error(`Unexpected event type: ${event.eventType}. Expected one of: ${allExpectedEvents
233
- .map((x) => x.type)
234
- .join(', ')}`);
235
- }
236
- const handlers = eventHandlers.get(event.eventType) ?? [];
237
- await Promise.all(handlers.map(({ eventDefinition, handler }) => (0, event_handlers_modules_js_1.runHandler)(eventDefinition, handler, event.payload, {
238
- instanceId: event.instanceId,
239
- identity: event.identity,
240
- })));
241
- },
242
- apps: {
243
- AppInstalled: (0, sdk_types_1.EventDefinition)('AppInstalled')(),
244
- AppRemoved: (0, sdk_types_1.EventDefinition)('AppRemoved')(),
245
- },
246
- },
247
- servicePlugins: {
248
- getRegisteredServicePlugins: () => servicePluginsImplementations,
249
- async process(request) {
250
- const servicePluginRequest = await this.parseJWT(request.body);
251
- return this.executeHandler(servicePluginRequest, request.url);
252
- },
253
- async processRequest(request) {
254
- const url = request.url;
255
- const body = await request.text();
256
- const implMethodResult = await this.process({ url, body });
257
- return Response.json(implMethodResult);
258
- },
259
- async parseJWT(jwt) {
260
- if (!authStrategy.decodeJWT) {
261
- throw new Error('decodeJWT is not supported by the authentication strategy');
262
- }
263
- const { decoded, valid } = await authStrategy.decodeJWT(jwt, true);
264
- if (!valid) {
265
- throw new Error('JWT is not valid');
266
- }
267
- if (typeof decoded.data !== 'object' ||
268
- decoded.data === null ||
269
- !('metadata' in decoded.data) ||
270
- typeof decoded.data.metadata !== 'object' ||
271
- decoded.data.metadata === null ||
272
- !('appExtensionType' in decoded.data.metadata) ||
273
- typeof decoded.data.metadata.appExtensionType !== 'string') {
274
- throw new Error('Unexpected JWT data: expected object with metadata.appExtensionType string');
275
- }
276
- return decoded.data;
277
- },
278
- async executeHandler(servicePluginRequest, url) {
279
- const componentType = servicePluginRequest.metadata.appExtensionType.toLowerCase();
280
- const implementations = servicePluginsImplementations.get(componentType) ?? [];
281
- if (implementations.length === 0) {
282
- throw new Error(`No service plugin implementations found for component type ${componentType}`);
283
- }
284
- else if (implementations.length > 1) {
285
- throw new Error(`Multiple service plugin implementations found for component type ${componentType}. This is currently not supported`);
286
- }
287
- const { implementation: impl, servicePluginDefinition } = implementations[0];
288
- const method = servicePluginDefinition.methods.find((m) => url.endsWith(m.primaryHttpMappingPath));
289
- if (!method) {
290
- throw new Error('Unexpect request: request url did not match any method: ' + url);
291
- }
292
- const implMethod = impl[method.name];
293
- if (!implMethod) {
294
- throw new Error(`Got request for service plugin method ${method.name} but no implementation was provided. Available methods: ${Object.keys(impl).join(', ')}`);
295
- }
296
- return method.transformations.toREST(await implMethod(method.transformations.fromREST(servicePluginRequest)));
297
- },
298
- },
159
+ webhooks: eventHandlersClient,
160
+ servicePlugins: servicePluginsClient,
299
161
  };
300
162
  }
301
163
  exports.createClient = createClient;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/sdk",
3
- "version": "1.12.7",
3
+ "version": "1.12.9",
4
4
  "license": "UNLICENSED",
5
5
  "author": {
6
6
  "name": "Ronny Ringel",
@@ -65,13 +65,14 @@
65
65
  "dependencies": {
66
66
  "@babel/runtime": "^7.23.2",
67
67
  "@wix/identity": "^1.0.78",
68
- "@wix/image-kit": "^1.73.0",
68
+ "@wix/image-kit": "^1.77.0",
69
69
  "@wix/redirects": "^1.0.41",
70
70
  "@wix/sdk-context": "^0.0.1",
71
- "@wix/sdk-runtime": "0.3.11",
71
+ "@wix/sdk-runtime": "0.3.14",
72
72
  "@wix/sdk-types": "^1.9.2",
73
73
  "crypto-js": "^4.2.0",
74
74
  "jose": "^5.2.1",
75
+ "nanoevents": "^9.0.0",
75
76
  "pkce-challenge": "^3.1.0",
76
77
  "querystring": "^0.2.1",
77
78
  "type-fest": "^4.9.0"
@@ -88,7 +89,7 @@
88
89
  "@wix/events": "^1.0.179",
89
90
  "@wix/metro": "^1.0.73",
90
91
  "@wix/metro-runtime": "^1.1677.0",
91
- "@wix/sdk-runtime": "0.3.11",
92
+ "@wix/sdk-runtime": "0.3.14",
92
93
  "eslint": "^8.56.0",
93
94
  "eslint-config-sdk": "0.0.0",
94
95
  "graphql": "^16.8.0",
@@ -122,5 +123,5 @@
122
123
  "wallaby": {
123
124
  "autoDetect": true
124
125
  },
125
- "falconPackageHash": "225bbf7de21c21bb40a3f13f6b1b7013827df311a9c450869d201177"
126
+ "falconPackageHash": "fac4a20ec702fb1a9e081a3fe2eb9dc105d284baeb49281de73dbf0e"
126
127
  }