@croct/plug 0.16.5 → 0.17.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/api/evaluate.cjs +45 -0
  2. package/api/evaluate.d.cts +23 -0
  3. package/api/evaluate.d.ts +9 -7
  4. package/api/evaluate.js +20 -22
  5. package/api/fetchContent.cjs +55 -0
  6. package/api/fetchContent.d.cts +45 -0
  7. package/api/fetchContent.d.ts +31 -11
  8. package/api/fetchContent.js +31 -21
  9. package/api/index.cjs +23 -0
  10. package/api/index.d.cts +27 -0
  11. package/api/index.d.ts +27 -2
  12. package/api/index.js +2 -6
  13. package/component.cjs +15 -0
  14. package/component.d.cts +19 -0
  15. package/component.d.ts +12 -9
  16. package/component.js +0 -3
  17. package/constants.cjs +39 -0
  18. package/constants.d.cts +7 -0
  19. package/constants.d.ts +7 -5
  20. package/constants.js +12 -9
  21. package/index.cjs +24 -0
  22. package/index.d.cts +27 -0
  23. package/index.d.ts +26 -3
  24. package/index.js +5 -6
  25. package/package.json +34 -7
  26. package/playground.cjs +166 -0
  27. package/playground.d.cts +64 -0
  28. package/playground.d.ts +18 -8
  29. package/playground.js +130 -138
  30. package/plug.cjs +279 -0
  31. package/plug.d.cts +86 -0
  32. package/plug.d.ts +34 -17
  33. package/plug.js +239 -244
  34. package/plugin.cjs +15 -0
  35. package/plugin.d.cts +41 -0
  36. package/plugin.d.ts +18 -8
  37. package/plugin.js +0 -3
  38. package/preview.cjs +180 -0
  39. package/preview.d.cts +35 -0
  40. package/preview.d.ts +18 -6
  41. package/preview.js +143 -152
  42. package/sdk/apiKey.cjs +21 -0
  43. package/sdk/apiKey.js +1 -5
  44. package/sdk/evaluation.cjs +34 -0
  45. package/sdk/evaluation.d.cts +2 -0
  46. package/sdk/evaluation.d.ts +1 -1
  47. package/sdk/evaluation.js +8 -10
  48. package/sdk/index.cjs +33 -0
  49. package/sdk/index.d.cts +15 -0
  50. package/sdk/index.d.ts +9 -6
  51. package/sdk/index.js +8 -10
  52. package/sdk/json.cjs +15 -0
  53. package/sdk/json.d.cts +6 -0
  54. package/sdk/json.d.ts +4 -1
  55. package/sdk/json.js +0 -5
  56. package/sdk/sdkEvents.cjs +15 -0
  57. package/sdk/sdkEvents.d.cts +1 -0
  58. package/sdk/sdkEvents.js +0 -3
  59. package/sdk/token.cjs +27 -0
  60. package/sdk/token.d.cts +7 -0
  61. package/sdk/token.d.ts +7 -1
  62. package/sdk/token.js +4 -6
  63. package/sdk/tracking.cjs +27 -0
  64. package/sdk/tracking.d.cts +9 -0
  65. package/sdk/tracking.d.ts +8 -5
  66. package/sdk/tracking.js +4 -6
  67. package/sdk/validation.cjs +23 -0
  68. package/sdk/validation.js +2 -6
  69. package/slot.cjs +15 -0
  70. package/slot.d.cts +42 -0
  71. package/slot.d.ts +14 -12
  72. package/slot.js +0 -3
  73. package/versioning.cjs +15 -0
  74. package/versioning.d.cts +26 -0
  75. package/versioning.d.ts +8 -6
  76. package/versioning.js +0 -3
  77. package/api/evaluate.js.map +0 -1
  78. package/api/fetchContent.js.map +0 -1
  79. package/api/index.js.map +0 -1
  80. package/component.js.map +0 -1
  81. package/constants.js.map +0 -1
  82. package/index.js.map +0 -1
  83. package/playground.js.map +0 -1
  84. package/plug.js.map +0 -1
  85. package/plugin.js.map +0 -1
  86. package/preview.js.map +0 -1
  87. package/sdk/apiKey.js.map +0 -1
  88. package/sdk/evaluation.js.map +0 -1
  89. package/sdk/index.js.map +0 -1
  90. package/sdk/json.js.map +0 -1
  91. package/sdk/sdkEvents.js.map +0 -1
  92. package/sdk/token.js.map +0 -1
  93. package/sdk/tracking.js.map +0 -1
  94. package/sdk/validation.js.map +0 -1
  95. package/slot.js.map +0 -1
  96. package/src/api/evaluate.ts +0 -50
  97. package/src/api/fetchContent.ts +0 -70
  98. package/src/api/index.ts +0 -2
  99. package/src/component.ts +0 -19
  100. package/src/constants.ts +0 -5
  101. package/src/index.ts +0 -6
  102. package/src/playground.ts +0 -247
  103. package/src/plug.ts +0 -429
  104. package/src/plugin.ts +0 -39
  105. package/src/preview.ts +0 -226
  106. package/src/sdk/evaluation.ts +0 -2
  107. package/src/sdk/index.ts +0 -14
  108. package/src/sdk/json.ts +0 -4
  109. package/src/sdk/sdkEvents.ts +0 -1
  110. package/src/sdk/token.ts +0 -1
  111. package/src/sdk/tracking.ts +0 -14
  112. package/src/slot.ts +0 -48
  113. package/src/versioning.ts +0 -38
  114. package/versioning.js.map +0 -1
  115. /package/{src/sdk/apiKey.ts → sdk/apiKey.d.cts} +0 -0
  116. /package/{src/sdk/validation.ts → sdk/validation.d.cts} +0 -0
package/src/plug.ts DELETED
@@ -1,429 +0,0 @@
1
- import {Logger} from '@croct/sdk/logging';
2
- import {SessionFacade} from '@croct/sdk/facade/sessionFacade';
3
- import {UserFacade} from '@croct/sdk/facade/userFacade';
4
- import {TrackerFacade} from '@croct/sdk/facade/trackerFacade';
5
- import {EvaluationOptions, EvaluatorFacade} from '@croct/sdk/facade/evaluatorFacade';
6
- import {Configuration as SdkFacadeConfiguration, SdkFacade} from '@croct/sdk/facade/sdkFacade';
7
- import {formatCause} from '@croct/sdk/error';
8
- import {describe} from '@croct/sdk/validation';
9
- import {Optional} from '@croct/sdk/utilityTypes';
10
- import {Token} from '@croct/sdk/token';
11
- import {
12
- ExternalTrackingEvent as ExternalEvent,
13
- ExternalTrackingEventPayload as ExternalEventPayload,
14
- ExternalTrackingEventType as ExternalEventType,
15
- } from '@croct/sdk/trackingEvents';
16
- import {VERSION} from '@croct/sdk';
17
- import {FetchOptions as BaseFetchOptions} from '@croct/sdk/facade/contentFetcherFacade';
18
- import {Plugin, PluginArguments, PluginFactory} from './plugin';
19
- import {CDN_URL} from './constants';
20
- import {factory as playgroundPluginFactory} from './playground';
21
- import {factory as previewPluginFactory} from './preview';
22
- import {VersionedSlotId, SlotContent} from './slot';
23
- import {JsonValue, JsonObject} from './sdk/json';
24
-
25
- export interface PluginConfigurations {
26
- [key: string]: any;
27
- }
28
-
29
- export type Configuration = Optional<SdkFacadeConfiguration, 'appId'> & {
30
- plugins?: PluginConfigurations,
31
- };
32
-
33
- export type FetchOptions = Omit<BaseFetchOptions, 'version'>;
34
-
35
- export type FetchResponse<I extends VersionedSlotId, C extends JsonObject = JsonObject> = {
36
- content: SlotContent<I, C>,
37
- };
38
-
39
- export interface Plug {
40
- readonly tracker: TrackerFacade;
41
- readonly user: UserFacade;
42
- readonly session: SessionFacade;
43
- readonly initialized: boolean;
44
- readonly flushed: Promise<this>;
45
- readonly plugged: Promise<this>;
46
-
47
- plug(configuration: Configuration): void;
48
-
49
- isAnonymous(): boolean;
50
-
51
- getUserId(): string | null;
52
-
53
- identify(userId: string): void;
54
-
55
- anonymize(): void;
56
-
57
- setToken(token: string): void;
58
-
59
- unsetToken(): void;
60
-
61
- track<T extends ExternalEventType>(type: T, payload: ExternalEventPayload<T>): Promise<ExternalEvent<T>>;
62
-
63
- evaluate<T extends JsonValue>(expression: string, options?: EvaluationOptions): Promise<T>;
64
-
65
- fetch<P extends JsonObject, I extends VersionedSlotId>(
66
- slotId: I,
67
- options?: FetchOptions
68
- ): Promise<FetchResponse<I, P>>;
69
-
70
- unplug(): Promise<void>;
71
- }
72
-
73
- const PLUGIN_NAMESPACE = 'Plugin';
74
-
75
- function detectAppId(): string | null {
76
- const script = window.document.querySelector(`script[src^='${CDN_URL}']`);
77
-
78
- if (!(script instanceof HTMLScriptElement)) {
79
- return null;
80
- }
81
-
82
- return (new URL(script.src)).searchParams.get('appId');
83
- }
84
-
85
- export class GlobalPlug implements Plug {
86
- private pluginFactories: {[key: string]: PluginFactory} = {
87
- playground: playgroundPluginFactory,
88
- preview: previewPluginFactory,
89
- };
90
-
91
- private instance?: SdkFacade;
92
-
93
- private plugins: {[key: string]: Plugin} = {};
94
-
95
- private initialize: {(): void};
96
-
97
- private ready: Promise<void>;
98
-
99
- public constructor() {
100
- this.ready = new Promise(resolve => {
101
- this.initialize = resolve;
102
- });
103
- }
104
-
105
- public extend(name: string, plugin: PluginFactory): void {
106
- if (this.pluginFactories[name] !== undefined) {
107
- throw new Error(`Another plugin is already registered with name "${name}".`);
108
- }
109
-
110
- this.pluginFactories[name] = plugin;
111
- }
112
-
113
- public plug(configuration: Configuration = {}): void {
114
- if (this.instance !== undefined) {
115
- const logger = this.instance.getLogger();
116
-
117
- logger.info('Croct is already plugged in.');
118
-
119
- return;
120
- }
121
-
122
- const detectedAppId = detectAppId();
123
- const configuredAppId = configuration.appId ?? null;
124
-
125
- if (detectedAppId !== null && configuredAppId !== null && detectedAppId !== configuredAppId) {
126
- throw new Error(
127
- 'The specified app ID and the auto-detected app ID are conflicting. '
128
- + 'There is no need to specify an app ID when using an application-specific tag. '
129
- + 'Please try again omitting the "appId" option. '
130
- + 'For help, see https://croct.help/sdk/javascript/conflicting-app-id',
131
-
132
- );
133
- }
134
-
135
- const appId = detectedAppId ?? configuredAppId;
136
-
137
- if (appId === null) {
138
- throw new Error(
139
- 'The app ID must be specified when it cannot be auto-detected. '
140
- + 'Please try again specifying the "appId" option.'
141
- + 'For help, see https://croct.help/sdk/javascript/missing-app-id',
142
- );
143
- }
144
-
145
- const {plugins, test, ...sdkConfiguration} = configuration;
146
-
147
- const sdk = SdkFacade.init({
148
- ...sdkConfiguration,
149
- appId: appId,
150
- test: test ?? (typeof process === 'object' && (
151
- process.env?.CROCT_TEST_MODE !== undefined
152
- ? process.env.CROCT_TEST_MODE === 'true'
153
- : process.env?.NODE_ENV === 'test'
154
- )),
155
- });
156
-
157
- this.instance = sdk;
158
-
159
- const logger = this.instance.getLogger();
160
-
161
- if (detectedAppId === configuredAppId) {
162
- logger.warn(
163
- 'It is strongly recommended omitting the "appId" option when using '
164
- + 'the application-specific tag as it is detected automatically.',
165
- );
166
- }
167
-
168
- const pending: Array<Promise<void>> = [];
169
-
170
- const defaultEnabledPlugins = Object.fromEntries(
171
- Object.keys(this.pluginFactories)
172
- .map(name => [name, true]),
173
- );
174
-
175
- for (const [name, options] of Object.entries({...defaultEnabledPlugins, ...plugins})) {
176
- logger.debug(`Initializing plugin "${name}"...`);
177
-
178
- const factory = this.pluginFactories[name];
179
-
180
- if (factory === undefined) {
181
- logger.error(`Plugin "${name}" is not registered.`);
182
-
183
- continue;
184
- }
185
-
186
- if (typeof options !== 'boolean' && (options === null || typeof options !== 'object')) {
187
- logger.error(
188
- `Invalid options for plugin "${name}", `
189
- + `expected either boolean or object but got ${describe(options)}`,
190
- );
191
-
192
- continue;
193
- }
194
-
195
- if (options === false) {
196
- logger.warn(`Plugin "${name}" is declared but not enabled`);
197
-
198
- continue;
199
- }
200
-
201
- const args: PluginArguments = {
202
- options: options === true ? {} : options,
203
- sdk: {
204
- version: VERSION,
205
- appId: appId,
206
- tracker: sdk.tracker,
207
- evaluator: sdk.evaluator,
208
- user: sdk.user,
209
- session: sdk.session,
210
- tab: sdk.context.getTab(),
211
- userTokenStore: {
212
- getToken: sdk.getToken.bind(sdk),
213
- setToken: sdk.setToken.bind(sdk),
214
- },
215
- previewTokenStore: sdk.previewTokenStore,
216
- cidAssigner: sdk.cidAssigner,
217
- eventManager: sdk.eventManager,
218
- getLogger: (...namespace: string[]): Logger => sdk.getLogger(PLUGIN_NAMESPACE, name, ...namespace),
219
- getTabStorage: (...namespace: string[]): Storage => (
220
- sdk.getTabStorage(PLUGIN_NAMESPACE, name, ...namespace)
221
- ),
222
- getBrowserStorage: (...namespace: string[]): Storage => (
223
- sdk.getBrowserStorage(PLUGIN_NAMESPACE, name, ...namespace)
224
- ),
225
- },
226
- };
227
-
228
- let plugin;
229
-
230
- try {
231
- plugin = factory(args);
232
- } catch (error) {
233
- logger.error(`Failed to initialize plugin "${name}": ${formatCause(error)}`);
234
-
235
- continue;
236
- }
237
-
238
- logger.debug(`Plugin "${name}" initialized`);
239
-
240
- if (typeof plugin !== 'object') {
241
- continue;
242
- }
243
-
244
- this.plugins[name] = plugin;
245
-
246
- const promise = plugin.enable();
247
-
248
- if (!(promise instanceof Promise)) {
249
- logger.debug(`Plugin "${name}" enabled`);
250
-
251
- continue;
252
- }
253
-
254
- pending.push(
255
- promise.then(() => logger.debug(`Plugin "${name}" enabled`))
256
- .catch(error => logger.error(`Failed to enable plugin "${name}": ${formatCause(error)}`)),
257
- );
258
- }
259
-
260
- Promise.all(pending)
261
- .then(() => {
262
- this.initialize();
263
-
264
- logger.debug('Initialization complete');
265
- });
266
- }
267
-
268
- public get initialized(): boolean {
269
- return this.instance !== undefined;
270
- }
271
-
272
- public get plugged(): Promise<this> {
273
- return this.ready.then(() => this);
274
- }
275
-
276
- public get flushed(): Promise<this> {
277
- return this.tracker
278
- .flushed
279
- .then(() => this);
280
- }
281
-
282
- private get sdk(): SdkFacade {
283
- if (this.instance === undefined) {
284
- throw new Error('Croct is not plugged in. For help, see https://croct.help/sdk/javascript/not-plugged-in');
285
- }
286
-
287
- return this.instance;
288
- }
289
-
290
- public get tracker(): TrackerFacade {
291
- return this.sdk.tracker;
292
- }
293
-
294
- public get evaluator(): EvaluatorFacade {
295
- return this.sdk.evaluator;
296
- }
297
-
298
- public get user(): UserFacade {
299
- return this.sdk.user;
300
- }
301
-
302
- public get session(): SessionFacade {
303
- return this.sdk.session;
304
- }
305
-
306
- public isAnonymous(): boolean {
307
- return this.sdk
308
- .context
309
- .isAnonymous();
310
- }
311
-
312
- public getUserId(): string | null {
313
- return this.sdk
314
- .context
315
- .getUser();
316
- }
317
-
318
- public identify(userId: string): void {
319
- if (typeof userId !== 'string') {
320
- throw new Error(
321
- 'The user ID must be a string. For help, see https://croct.help/sdk/javascript/invalid-user-id',
322
- );
323
- }
324
-
325
- this.sdk.identify(userId);
326
- }
327
-
328
- public anonymize(): void {
329
- this.sdk.anonymize();
330
- }
331
-
332
- public setToken(token: string): void {
333
- this.sdk.setToken(Token.parse(token));
334
- }
335
-
336
- public unsetToken(): void {
337
- this.sdk.unsetToken();
338
- }
339
-
340
- public track<T extends ExternalEventType>(type: T, payload: ExternalEventPayload<T>): Promise<ExternalEvent<T>> {
341
- return this.sdk
342
- .tracker
343
- .track(type, payload);
344
- }
345
-
346
- public evaluate<T extends JsonValue>(query: string, options: EvaluationOptions = {}): Promise<T> {
347
- return this.sdk
348
- .evaluator
349
- .evaluate(query, options)
350
- .catch(error => {
351
- const logger = this.sdk.getLogger();
352
- const reference = query.length > 20 ? `${query.slice(0, 20)}...` : query;
353
-
354
- logger.error(`Failed to evaluate query "${reference}": ${formatCause(error)}`);
355
-
356
- throw error;
357
- }) as Promise<T>;
358
- }
359
-
360
- public test(expression: string, options: EvaluationOptions = {}): Promise<boolean> {
361
- return this.evaluate(expression, options)
362
- .then(result => result === true);
363
- }
364
-
365
- public fetch<C extends JsonObject, I extends VersionedSlotId = VersionedSlotId>(
366
- slotId: I,
367
- options: FetchOptions = {},
368
- ): Promise<FetchResponse<I, C>> {
369
- const [id, version = 'latest'] = slotId.split('@') as [string, `${number}` | 'latest' | undefined];
370
- const logger = this.sdk.getLogger();
371
-
372
- return this.sdk
373
- .contentFetcher
374
- .fetch<SlotContent<I, C>>(id, version === 'latest' ? options : {...options, version: version})
375
- .catch(error => {
376
- logger.error(`Failed to fetch content for slot "${id}@${version}": ${formatCause(error)}`);
377
-
378
- throw error;
379
- });
380
- }
381
-
382
- public async unplug(): Promise<void> {
383
- if (this.instance === undefined) {
384
- return;
385
- }
386
-
387
- const {instance, plugins} = this;
388
-
389
- const logger = this.sdk.getLogger();
390
- const pending: Array<Promise<void>> = [];
391
-
392
- for (const [pluginName, controller] of Object.entries(plugins)) {
393
- if (typeof controller.disable !== 'function') {
394
- continue;
395
- }
396
-
397
- logger.debug(`Disabling plugin "${pluginName}"...`);
398
-
399
- const promise = controller.disable();
400
-
401
- if (!(promise instanceof Promise)) {
402
- logger.debug(`Plugin "${pluginName}" disabled`);
403
-
404
- continue;
405
- }
406
-
407
- pending.push(
408
- promise.then(() => logger.debug(`Plugin "${pluginName}" disabled`))
409
- .catch(error => logger.error(`Failed to disable "${pluginName}": ${formatCause(error)}`)),
410
- );
411
- }
412
-
413
- // Reset
414
- delete this.instance;
415
-
416
- this.plugins = {};
417
- this.ready = new Promise(resolve => {
418
- this.initialize = resolve;
419
- });
420
-
421
- await Promise.all(pending);
422
-
423
- try {
424
- await instance.close();
425
- } finally {
426
- logger.info('🔌 Croct has been unplugged.');
427
- }
428
- }
429
- }
package/src/plugin.ts DELETED
@@ -1,39 +0,0 @@
1
- import {TokenStore} from './sdk/token';
2
- import {EvaluatorFacade} from './sdk/evaluation';
3
- import {TrackerFacade} from './sdk/tracking';
4
- import {Tab, Logger, SdkEventManager, SessionFacade, UserFacade, CidAssigner} from './sdk';
5
-
6
- export interface PluginSdk {
7
- readonly version: string;
8
- readonly appId: string;
9
- readonly tracker: TrackerFacade;
10
- readonly evaluator: EvaluatorFacade;
11
- readonly user: UserFacade;
12
- readonly session: SessionFacade;
13
- readonly tab: Tab;
14
- readonly userTokenStore: TokenStore;
15
- readonly previewTokenStore: TokenStore;
16
- readonly cidAssigner: CidAssigner;
17
- readonly eventManager: SdkEventManager;
18
-
19
- getLogger(...namespace: string[]): Logger;
20
-
21
- getTabStorage(...namespace: string[]): Storage;
22
-
23
- getBrowserStorage(...namespace: string[]): Storage;
24
- }
25
-
26
- export interface PluginArguments<T = any> {
27
- options: T;
28
- sdk: PluginSdk;
29
- }
30
-
31
- export interface PluginFactory<T = any> {
32
- (args: PluginArguments<T>): Plugin;
33
- }
34
-
35
- export interface Plugin {
36
- enable(): Promise<void>|void;
37
-
38
- disable?(): Promise<void>|void;
39
- }
package/src/preview.ts DELETED
@@ -1,226 +0,0 @@
1
- import {formatCause} from '@croct/sdk/error';
2
- import {Logger} from './sdk';
3
- import {Plugin, PluginFactory} from './plugin';
4
- import {Token, TokenStore} from './sdk/token';
5
- import {PREVIEW_WIDGET_ORIGIN, PREVIEW_WIDGET_URL} from './constants';
6
-
7
- const PREVIEW_PARAMETER = 'croct-preview';
8
- const PREVIEW_EXIT = 'exit';
9
-
10
- export type Configuration = {
11
- tokenStore: TokenStore,
12
- logger: Logger,
13
- };
14
-
15
- export class PreviewPlugin implements Plugin {
16
- private static readonly PREVIEW_PARAMS = {
17
- previewMode: 'previewMode',
18
- experienceName: 'experience',
19
- experimentName: 'experiment',
20
- audienceName: 'audience',
21
- variantName: 'variant',
22
- locale: 'locale',
23
- };
24
-
25
- private readonly tokenStore: TokenStore;
26
-
27
- private readonly logger: Logger;
28
-
29
- private cleanUp: Array<() => void> = [];
30
-
31
- public constructor(configuration: Configuration) {
32
- this.tokenStore = configuration.tokenStore;
33
- this.logger = configuration.logger;
34
- }
35
-
36
- public enable(): void {
37
- const url = new URL(window.location.href);
38
- const previewData = url.searchParams.get(PREVIEW_PARAMETER);
39
-
40
- if (previewData !== null) {
41
- this.updateToken(previewData);
42
-
43
- this.updateUrl();
44
-
45
- // Some frameworks (e.g. Next) may revert the URL change
46
- // after the page is loaded, so ensure the token is removed
47
- // from the URL after a short delay.
48
- setTimeout(this.updateUrl, 500);
49
- }
50
-
51
- const token = this.tokenStore.getToken();
52
-
53
- if (token !== null) {
54
- this.insertWidget(this.getWidgetUrl(token));
55
- }
56
- }
57
-
58
- public disable(): void {
59
- const callbacks = this.cleanUp.splice(0);
60
-
61
- callbacks.forEach(cleanUp => cleanUp());
62
- }
63
-
64
- private updateToken(data: string): void {
65
- if (data === PREVIEW_EXIT) {
66
- this.logger.debug('Exiting preview mode.');
67
-
68
- this.tokenStore.setToken(null);
69
-
70
- return;
71
- }
72
-
73
- try {
74
- let token: Token | null = Token.parse(data);
75
- const {exp} = token.getPayload();
76
-
77
- if (exp !== undefined && exp <= Date.now() / 1000) {
78
- this.logger.debug('Preview token expired.');
79
-
80
- token = null;
81
- }
82
-
83
- this.logger.debug('Preview token updated.');
84
-
85
- this.tokenStore.setToken(token);
86
- } catch (error) {
87
- this.tokenStore.setToken(null);
88
-
89
- this.logger.warn(`Invalid preview token: ${formatCause(error)}`);
90
- }
91
- }
92
-
93
- private getWidgetUrl(token: Token): string {
94
- const params = this.getWidgetParams(token);
95
- let queryString = params.toString();
96
-
97
- if (queryString !== '') {
98
- queryString = `?${queryString}`;
99
- }
100
-
101
- return `${PREVIEW_WIDGET_URL}${queryString}`;
102
- }
103
-
104
- private getWidgetParams(token: Token): URLSearchParams {
105
- const {metadata = {}} = token.getPayload();
106
- const params = new URLSearchParams();
107
-
108
- if (metadata === null || typeof metadata !== 'object' || Array.isArray(metadata)) {
109
- return params;
110
- }
111
-
112
- for (const [key, param] of Object.entries(PreviewPlugin.PREVIEW_PARAMS)) {
113
- const value = metadata[key];
114
-
115
- if (typeof value === 'string') {
116
- params.set(param, value);
117
- }
118
- }
119
-
120
- return params;
121
- }
122
-
123
- private insertWidget(url: string): void {
124
- const widget = this.createWidget(url);
125
-
126
- const onMessage = (event: MessageEvent): void => {
127
- if (event.origin !== PREVIEW_WIDGET_ORIGIN) {
128
- return;
129
- }
130
-
131
- switch (event.data.type) {
132
- case 'croct:preview:leave': {
133
- this.tokenStore.setToken(null);
134
-
135
- const exitUrl = new URL(window.location.href);
136
-
137
- exitUrl.searchParams.set(PREVIEW_PARAMETER, PREVIEW_EXIT);
138
-
139
- window.location.replace(exitUrl.toString());
140
-
141
- break;
142
- }
143
-
144
- case 'croct:preview:resize':
145
- widget.style.width = `${event.data.width}px`;
146
- widget.style.height = `${event.data.height}px`;
147
-
148
- break;
149
- }
150
- };
151
-
152
- window.addEventListener('message', onMessage);
153
-
154
- this.cleanUp.push(() => window.removeEventListener('message', onMessage));
155
-
156
- const insert = (): void => {
157
- const container = document.body;
158
-
159
- container.prepend(widget);
160
-
161
- this.cleanUp.push(() => container.removeChild(widget));
162
-
163
- this.logger.debug('Preview widget mounted.');
164
-
165
- // Reinsert the widget if it is removed from the DOM (e.g. by a framework)
166
- const observer = new MutationObserver(() => {
167
- if (!container.contains(widget)) {
168
- container.prepend(widget);
169
-
170
- this.logger.debug('Preview widget removed from DOM, reinserting.');
171
- }
172
- });
173
-
174
- observer.observe(container, {childList: true});
175
-
176
- // Make sure the observer is disconnected before the widget is removed.
177
- // Otherwise, the observer may reinsert the widget after it is removed.
178
- this.cleanUp.unshift(() => observer.disconnect());
179
- };
180
-
181
- if (document.readyState !== 'loading') {
182
- insert();
183
- } else {
184
- this.logger.debug('Waiting for document to be ready.');
185
- this.cleanUp.push(() => window.removeEventListener('DOMContentLoaded', insert));
186
- window.addEventListener('DOMContentLoaded', insert);
187
- }
188
- }
189
-
190
- private updateUrl(): void {
191
- const url = new URL(window.location.href);
192
-
193
- if (url.searchParams.has(PREVIEW_PARAMETER)) {
194
- url.searchParams.delete(PREVIEW_PARAMETER);
195
-
196
- window.history.replaceState({}, '', url.toString());
197
- }
198
- }
199
-
200
- private createWidget(url: string): HTMLIFrameElement {
201
- const widget = document.createElement('iframe');
202
-
203
- widget.setAttribute('src', url);
204
- widget.setAttribute('sandbox', 'allow-scripts allow-same-origin');
205
-
206
- widget.style.position = 'fixed';
207
- widget.style.width = '0px';
208
- widget.style.height = '0px';
209
- widget.style.right = '0';
210
- widget.style.bottom = '0';
211
- widget.style.border = '0';
212
- widget.style.overflow = 'hidden';
213
- widget.style.zIndex = '2147483647';
214
-
215
- return widget;
216
- }
217
- }
218
-
219
- export const factory: PluginFactory = (props): PreviewPlugin => {
220
- const {sdk} = props;
221
-
222
- return new PreviewPlugin({
223
- tokenStore: props.sdk.previewTokenStore,
224
- logger: sdk.getLogger(),
225
- });
226
- };
@@ -1,2 +0,0 @@
1
- export {EvaluatorFacade, EvaluationOptions} from '@croct/sdk/facade/evaluatorFacade';
2
- export {EvaluationError, EvaluationErrorType, QueryError} from '@croct/sdk/evaluator';