@croct/sdk 0.11.0-alpha → 0.11.0-alpha.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 (198) hide show
  1. package/.src/activeRecord.ts +150 -0
  2. package/.src/base64Url.ts +18 -0
  3. package/.src/cache/cache.ts +15 -0
  4. package/.src/cache/fallbackCache.ts +29 -0
  5. package/.src/cache/inMemoryCache.ts +21 -0
  6. package/.src/cache/index.ts +4 -0
  7. package/.src/cache/localStorageCache.ts +85 -0
  8. package/.src/channel/beaconSocketChannel.ts +153 -0
  9. package/.src/channel/channel.ts +20 -0
  10. package/.src/channel/encodedChannel.ts +21 -0
  11. package/.src/channel/guaranteedChannel.ts +131 -0
  12. package/.src/channel/index.ts +8 -0
  13. package/.src/channel/queuedChannel.ts +112 -0
  14. package/.src/channel/retryChannel.ts +90 -0
  15. package/.src/channel/sandboxChannel.ts +43 -0
  16. package/.src/channel/socketChannel.ts +217 -0
  17. package/.src/cid/assigner.ts +3 -0
  18. package/.src/cid/cachedAssigner.ts +35 -0
  19. package/.src/cid/fixedAssigner.ts +13 -0
  20. package/.src/cid/index.ts +4 -0
  21. package/.src/cid/remoteAssigner.ts +47 -0
  22. package/.src/constants.ts +6 -0
  23. package/.src/container.ts +388 -0
  24. package/.src/contentFetcher.ts +226 -0
  25. package/.src/context.ts +137 -0
  26. package/.src/error.ts +31 -0
  27. package/.src/evaluator.ts +251 -0
  28. package/.src/eventManager.ts +53 -0
  29. package/.src/facade/contentFetcherFacade.ts +69 -0
  30. package/.src/facade/evaluatorFacade.ts +152 -0
  31. package/.src/facade/index.ts +7 -0
  32. package/.src/facade/sdkFacade.ts +291 -0
  33. package/.src/facade/sessionFacade.ts +14 -0
  34. package/.src/facade/sessionPatch.ts +32 -0
  35. package/.src/facade/trackerFacade.ts +98 -0
  36. package/.src/facade/userFacade.ts +26 -0
  37. package/.src/facade/userPatch.ts +32 -0
  38. package/.src/index.ts +4 -0
  39. package/.src/logging/consoleLogger.ts +37 -0
  40. package/.src/logging/index.ts +4 -0
  41. package/.src/logging/logger.ts +13 -0
  42. package/.src/logging/namespacedLogger.ts +32 -0
  43. package/.src/logging/nullLogger.ts +19 -0
  44. package/.src/namespacedStorage.ts +69 -0
  45. package/.src/patch.ts +64 -0
  46. package/.src/queue/capacityRestrictedQueue.ts +44 -0
  47. package/.src/queue/inMemoryQueue.ts +43 -0
  48. package/.src/queue/index.ts +5 -0
  49. package/.src/queue/monitoredQueue.ts +168 -0
  50. package/.src/queue/persistentQueue.ts +84 -0
  51. package/.src/queue/queue.ts +15 -0
  52. package/.src/retry/arbitraryPolicy.ts +21 -0
  53. package/.src/retry/backoffPolicy.ts +84 -0
  54. package/.src/retry/index.ts +5 -0
  55. package/.src/retry/maxAttemptsPolicy.ts +28 -0
  56. package/.src/retry/neverPolicy.ts +11 -0
  57. package/.src/retry/policy.ts +5 -0
  58. package/.src/schema/attributeSchema.ts +6 -0
  59. package/.src/schema/contentFetcherSchemas.ts +23 -0
  60. package/.src/schema/contentSchemas.ts +44 -0
  61. package/.src/schema/contextSchemas.ts +5 -0
  62. package/.src/schema/ecommerceSchemas.ts +179 -0
  63. package/.src/schema/evaluatorSchemas.ts +11 -0
  64. package/.src/schema/eventSchemas.ts +150 -0
  65. package/.src/schema/index.ts +11 -0
  66. package/.src/schema/loggerSchema.ts +12 -0
  67. package/.src/schema/operationSchemas.ts +102 -0
  68. package/.src/schema/sdkFacadeSchemas.ts +44 -0
  69. package/.src/schema/sdkSchemas.ts +49 -0
  70. package/.src/schema/tokenSchema.ts +42 -0
  71. package/.src/schema/userSchema.ts +184 -0
  72. package/.src/sdk.ts +174 -0
  73. package/.src/sdkEvents.ts +15 -0
  74. package/.src/sourceLocation.ts +85 -0
  75. package/.src/tab.ts +148 -0
  76. package/.src/token/cachedTokenStore.ts +34 -0
  77. package/.src/token/inMemoryTokenStore.ts +13 -0
  78. package/.src/token/index.ts +4 -0
  79. package/.src/token/replicatedTokenStore.ts +21 -0
  80. package/.src/token/token.ts +164 -0
  81. package/.src/tracker.ts +460 -0
  82. package/.src/trackingEvents.ts +456 -0
  83. package/.src/transformer.ts +7 -0
  84. package/.src/utilityTypes.ts +3 -0
  85. package/.src/uuid.ts +43 -0
  86. package/.src/validation/arrayType.ts +71 -0
  87. package/.src/validation/booleanType.ts +22 -0
  88. package/.src/validation/functionType.ts +22 -0
  89. package/.src/validation/index.ts +12 -0
  90. package/.src/validation/jsonType.ts +157 -0
  91. package/.src/validation/mixedSchema.ts +7 -0
  92. package/.src/validation/nullType.ts +22 -0
  93. package/.src/validation/numberType.ts +59 -0
  94. package/.src/validation/objectType.ts +138 -0
  95. package/.src/validation/schema.ts +21 -0
  96. package/.src/validation/stringType.ts +118 -0
  97. package/.src/validation/unionType.ts +53 -0
  98. package/.src/validation/violation.ts +23 -0
  99. package/activeRecord.js +1 -0
  100. package/base64Url.js +1 -0
  101. package/cache/cache.js +1 -0
  102. package/cache/fallbackCache.js +1 -0
  103. package/cache/inMemoryCache.js +1 -0
  104. package/cache/index.js +1 -0
  105. package/cache/localStorageCache.js +1 -0
  106. package/channel/beaconSocketChannel.js +1 -0
  107. package/channel/channel.js +1 -0
  108. package/channel/encodedChannel.js +1 -0
  109. package/channel/guaranteedChannel.js +1 -0
  110. package/channel/index.js +1 -0
  111. package/channel/queuedChannel.js +1 -0
  112. package/channel/retryChannel.js +1 -0
  113. package/channel/sandboxChannel.js +1 -0
  114. package/channel/socketChannel.js +1 -0
  115. package/cid/assigner.js +1 -0
  116. package/cid/cachedAssigner.js +1 -0
  117. package/cid/fixedAssigner.js +1 -0
  118. package/cid/index.js +1 -0
  119. package/cid/remoteAssigner.js +1 -0
  120. package/constants.d.ts +5 -5
  121. package/constants.js +2 -1
  122. package/container.js +1 -0
  123. package/contentFetcher.js +1 -0
  124. package/context.js +1 -0
  125. package/error.js +1 -0
  126. package/evaluator.js +1 -0
  127. package/eventManager.js +1 -0
  128. package/facade/contentFetcherFacade.js +1 -0
  129. package/facade/evaluatorFacade.js +1 -0
  130. package/facade/index.js +1 -0
  131. package/facade/sdkFacade.js +1 -0
  132. package/facade/sessionFacade.js +1 -0
  133. package/facade/sessionPatch.js +1 -0
  134. package/facade/trackerFacade.js +1 -0
  135. package/facade/userFacade.js +1 -0
  136. package/facade/userPatch.js +1 -0
  137. package/index.js +1 -0
  138. package/logging/consoleLogger.js +1 -0
  139. package/logging/index.js +1 -0
  140. package/logging/logger.js +1 -0
  141. package/logging/namespacedLogger.js +1 -0
  142. package/logging/nullLogger.js +1 -0
  143. package/namespacedStorage.js +1 -0
  144. package/package.json +1 -1
  145. package/patch.js +1 -0
  146. package/queue/capacityRestrictedQueue.js +1 -0
  147. package/queue/inMemoryQueue.js +1 -0
  148. package/queue/index.js +1 -0
  149. package/queue/monitoredQueue.js +1 -0
  150. package/queue/persistentQueue.js +1 -0
  151. package/queue/queue.js +1 -0
  152. package/retry/arbitraryPolicy.js +1 -0
  153. package/retry/backoffPolicy.js +1 -0
  154. package/retry/index.js +1 -0
  155. package/retry/maxAttemptsPolicy.js +1 -0
  156. package/retry/neverPolicy.js +1 -0
  157. package/retry/policy.js +1 -0
  158. package/schema/attributeSchema.js +1 -0
  159. package/schema/contentFetcherSchemas.js +1 -0
  160. package/schema/contentSchemas.js +1 -0
  161. package/schema/contextSchemas.js +1 -0
  162. package/schema/ecommerceSchemas.js +1 -0
  163. package/schema/evaluatorSchemas.js +1 -0
  164. package/schema/eventSchemas.js +1 -0
  165. package/schema/index.js +1 -0
  166. package/schema/loggerSchema.js +1 -0
  167. package/schema/operationSchemas.js +1 -0
  168. package/schema/sdkFacadeSchemas.js +1 -0
  169. package/schema/sdkSchemas.js +1 -0
  170. package/schema/tokenSchema.js +1 -0
  171. package/schema/userSchema.js +1 -0
  172. package/sdk.js +1 -0
  173. package/sdkEvents.js +1 -0
  174. package/sourceLocation.js +1 -0
  175. package/tab.js +1 -0
  176. package/token/cachedTokenStore.js +1 -0
  177. package/token/inMemoryTokenStore.js +1 -0
  178. package/token/index.js +1 -0
  179. package/token/replicatedTokenStore.js +1 -0
  180. package/token/token.js +1 -0
  181. package/tracker.js +1 -0
  182. package/trackingEvents.js +1 -0
  183. package/transformer.js +1 -0
  184. package/utilityTypes.js +1 -0
  185. package/uuid.js +1 -0
  186. package/validation/arrayType.js +1 -0
  187. package/validation/booleanType.js +1 -0
  188. package/validation/functionType.js +1 -0
  189. package/validation/index.js +1 -0
  190. package/validation/jsonType.js +1 -0
  191. package/validation/mixedSchema.js +1 -0
  192. package/validation/nullType.js +1 -0
  193. package/validation/numberType.js +1 -0
  194. package/validation/objectType.js +1 -0
  195. package/validation/schema.js +1 -0
  196. package/validation/stringType.js +1 -0
  197. package/validation/unionType.js +1 -0
  198. package/validation/violation.js +1 -0
@@ -0,0 +1,291 @@
1
+ import {EvaluatorFacade, TabContextFactory} from './evaluatorFacade';
2
+ import {TrackerFacade} from './trackerFacade';
3
+ import {Context, TokenScope} from '../context';
4
+ import {UserFacade} from './userFacade';
5
+ import {Token, TokenStore} from '../token';
6
+ import {formatCause} from '../error';
7
+ import {sdkFacadeConfigurationSchema} from '../schema';
8
+ import {Sdk} from '../sdk';
9
+ import {SessionFacade} from './sessionFacade';
10
+ import {Logger} from '../logging';
11
+ import {SdkEventMap} from '../sdkEvents';
12
+ import {EventManager} from '../eventManager';
13
+ import {CidAssigner} from '../cid';
14
+ import {PartialTrackingEvent} from '../trackingEvents';
15
+ import {UrlSanitizer} from '../tab';
16
+ import {ContentFetcherFacade} from './contentFetcherFacade';
17
+
18
+ export type Configuration = {
19
+ appId: string,
20
+ tokenScope?: TokenScope,
21
+ debug?: boolean,
22
+ test?: boolean,
23
+ track?: boolean,
24
+ token?: string | null,
25
+ userId?: string,
26
+ clientId?: string,
27
+ eventMetadata?: {[key: string]: string},
28
+ logger?: Logger,
29
+ urlSanitizer?: UrlSanitizer,
30
+ trackerEndpointUrl?: string,
31
+ evaluationEndpointUrl?: string,
32
+ contentEndpointUrl?: string,
33
+ cidAssignerEndpointUrl?: string,
34
+ };
35
+
36
+ function validateConfiguration(configuration: unknown): asserts configuration is Configuration {
37
+ try {
38
+ sdkFacadeConfigurationSchema.validate(configuration);
39
+ } catch (violation) {
40
+ throw new Error(`Invalid configuration: ${formatCause(violation)}`);
41
+ }
42
+ }
43
+
44
+ export class SdkFacade {
45
+ private readonly sdk: Sdk;
46
+
47
+ private trackerFacade?: TrackerFacade;
48
+
49
+ private userFacade?: UserFacade;
50
+
51
+ private sessionFacade?: SessionFacade;
52
+
53
+ private evaluatorFacade?: EvaluatorFacade;
54
+
55
+ private contentFetcherFacade?: ContentFetcherFacade;
56
+
57
+ private constructor(sdk: Sdk) {
58
+ this.sdk = sdk;
59
+ }
60
+
61
+ public static init(configuration: Configuration): SdkFacade {
62
+ validateConfiguration(configuration);
63
+
64
+ const {track = true, userId, token, ...containerConfiguration} = configuration;
65
+
66
+ if (userId !== undefined && token !== undefined) {
67
+ throw new Error('Either the user ID or token can be specified, but not both.');
68
+ }
69
+
70
+ const sdk = new SdkFacade(
71
+ Sdk.init({
72
+ ...containerConfiguration,
73
+ tokenScope: containerConfiguration.tokenScope ?? 'global',
74
+ debug: containerConfiguration.debug ?? false,
75
+ test: containerConfiguration.test ?? false,
76
+ }),
77
+ );
78
+
79
+ if (userId !== undefined) {
80
+ sdk.identify(userId);
81
+ } else if (token !== undefined) {
82
+ if (token === null) {
83
+ sdk.unsetToken();
84
+ } else {
85
+ sdk.setToken(Token.parse(token));
86
+ }
87
+ }
88
+
89
+ if (track) {
90
+ sdk.tracker.enable();
91
+ }
92
+
93
+ return sdk;
94
+ }
95
+
96
+ public get context(): Context {
97
+ return this.sdk.context;
98
+ }
99
+
100
+ public get cidAssigner(): CidAssigner {
101
+ return this.sdk.cidAssigner;
102
+ }
103
+
104
+ public get previewTokenStore(): TokenStore {
105
+ return this.sdk.previewTokenStore;
106
+ }
107
+
108
+ public get userTokenStore(): TokenStore {
109
+ return this.sdk.userTokenStore;
110
+ }
111
+
112
+ public get tracker(): TrackerFacade {
113
+ if (this.trackerFacade === undefined) {
114
+ this.trackerFacade = new TrackerFacade(this.sdk.tracker);
115
+ }
116
+
117
+ return this.trackerFacade;
118
+ }
119
+
120
+ public get user(): UserFacade {
121
+ if (this.userFacade === undefined) {
122
+ this.userFacade = new UserFacade(this.context, this.sdk.tracker);
123
+ }
124
+
125
+ return this.userFacade;
126
+ }
127
+
128
+ public get session(): SessionFacade {
129
+ if (this.sessionFacade === undefined) {
130
+ this.sessionFacade = new SessionFacade(this.sdk.tracker);
131
+ }
132
+
133
+ return this.sessionFacade;
134
+ }
135
+
136
+ public get evaluator(): EvaluatorFacade {
137
+ if (this.evaluatorFacade === undefined) {
138
+ this.evaluatorFacade = new EvaluatorFacade({
139
+ evaluator: this.sdk.evaluator,
140
+ contextFactory: new TabContextFactory(this.sdk
141
+ .context
142
+ .getTab()),
143
+ cidAssigner: this.sdk.cidAssigner,
144
+ userTokenProvider: this.sdk.userTokenStore,
145
+ });
146
+ }
147
+
148
+ return this.evaluatorFacade;
149
+ }
150
+
151
+ public get contentFetcher(): ContentFetcherFacade {
152
+ if (this.contentFetcherFacade === undefined) {
153
+ this.contentFetcherFacade = new ContentFetcherFacade({
154
+ contentFetcher: this.sdk.contentFetcher,
155
+ contextFactory: new TabContextFactory(this.sdk
156
+ .context
157
+ .getTab()),
158
+ cidAssigner: this.sdk.cidAssigner,
159
+ previewTokenProvider: this.sdk.previewTokenStore,
160
+ userTokenProvider: this.sdk.userTokenStore,
161
+ });
162
+ }
163
+
164
+ return this.contentFetcherFacade;
165
+ }
166
+
167
+ public get eventManager(): EventManager<Record<string, Record<string, unknown>>, SdkEventMap> {
168
+ const {eventManager} = this.sdk;
169
+
170
+ return {
171
+ addListener: eventManager.addListener.bind(eventManager),
172
+ removeListener: eventManager.removeListener.bind(eventManager),
173
+ dispatch: (eventName: string, event: Record<string, unknown>): void => {
174
+ if (!/[a-z][a-z_]+\.[a-z][a-z_]+/i.test(eventName)) {
175
+ throw new Error(
176
+ 'The event name must be in the form of "namespaced.eventName", where '
177
+ + 'both the namespace and event name must start with a letter, followed by '
178
+ + 'any series of letters and underscores.',
179
+ );
180
+ }
181
+
182
+ eventManager.dispatch(eventName, event);
183
+ },
184
+ };
185
+ }
186
+
187
+ public identify(userId: string): void {
188
+ this.setToken(Token.issue(this.sdk.appId, userId));
189
+ }
190
+
191
+ public anonymize(): void {
192
+ if (!this.context.isAnonymous()) {
193
+ this.unsetToken();
194
+ }
195
+ }
196
+
197
+ public getToken(): Token|null {
198
+ return this.context.getToken();
199
+ }
200
+
201
+ public setToken(token: Token): void {
202
+ const currentToken = this.getToken();
203
+
204
+ if (currentToken !== null && currentToken.toString() === token.toString()) {
205
+ return;
206
+ }
207
+
208
+ const currentSubject = currentToken?.getSubject() ?? null;
209
+ const subject = token.getSubject();
210
+ const logger = this.getLogger();
211
+
212
+ if (subject === currentSubject) {
213
+ this.context.setToken(token);
214
+
215
+ logger.debug('Token refreshed');
216
+
217
+ return;
218
+ }
219
+
220
+ if (currentSubject !== null) {
221
+ this.trackInternalEvent({
222
+ type: 'userSignedOut',
223
+ userId: currentSubject,
224
+ });
225
+
226
+ logger.info('User signed out');
227
+ }
228
+
229
+ this.context.setToken(token);
230
+
231
+ if (subject !== null) {
232
+ this.trackInternalEvent({
233
+ type: 'userSignedIn',
234
+ userId: subject,
235
+ });
236
+
237
+ logger.info(`User signed in as ${subject}`);
238
+ }
239
+
240
+ logger.debug('New token saved, ');
241
+ }
242
+
243
+ public unsetToken(): void {
244
+ const token = this.getToken();
245
+
246
+ if (token === null) {
247
+ return;
248
+ }
249
+
250
+ const logger = this.getLogger();
251
+ const subject = token.getSubject();
252
+
253
+ if (subject !== null) {
254
+ this.trackInternalEvent({
255
+ type: 'userSignedOut',
256
+ userId: subject,
257
+ });
258
+
259
+ logger.info('User signed out');
260
+ }
261
+
262
+ this.context.setToken(null);
263
+
264
+ logger.debug('Token removed');
265
+ }
266
+
267
+ private trackInternalEvent(event: PartialTrackingEvent): void {
268
+ this.sdk
269
+ .tracker
270
+ .track(event)
271
+ .catch(() => {
272
+ // suppress error as it is already logged by the tracker
273
+ });
274
+ }
275
+
276
+ public getLogger(...namespace: string[]): Logger {
277
+ return this.sdk.getLogger(...namespace);
278
+ }
279
+
280
+ public getTabStorage(namespace: string, ...subnamespace: string[]): Storage {
281
+ return this.sdk.getTabStorage(namespace, ...subnamespace);
282
+ }
283
+
284
+ public getBrowserStorage(namespace: string, ...subnamespace: string[]): Storage {
285
+ return this.sdk.getBrowserStorage(namespace, ...subnamespace);
286
+ }
287
+
288
+ public close(): Promise<void> {
289
+ return this.sdk.close();
290
+ }
291
+ }
@@ -0,0 +1,14 @@
1
+ import {Tracker} from '../tracker';
2
+ import {SessionPatch} from './sessionPatch';
3
+
4
+ export class SessionFacade {
5
+ private readonly tracker: Tracker;
6
+
7
+ public constructor(tracker: Tracker) {
8
+ this.tracker = tracker;
9
+ }
10
+
11
+ public edit(): SessionPatch {
12
+ return new SessionPatch(this.tracker);
13
+ }
14
+ }
@@ -0,0 +1,32 @@
1
+ import {ActiveRecord} from '../activeRecord';
2
+ import {Tracker} from '../tracker';
3
+ import {SessionAttributesChanged} from '../trackingEvents';
4
+
5
+ export class SessionPatch extends ActiveRecord<SessionAttributesChanged> {
6
+ private readonly tracker: Tracker;
7
+
8
+ public constructor(tracker: Tracker) {
9
+ super();
10
+
11
+ this.tracker = tracker;
12
+ }
13
+
14
+ public save(): Promise<SessionAttributesChanged> {
15
+ if (!this.isDirty()) {
16
+ // Empty patch
17
+ return Promise.resolve({
18
+ type: 'sessionAttributesChanged',
19
+ patch: {operations: []},
20
+ });
21
+ }
22
+
23
+ const promise = this.tracker.track({
24
+ type: 'sessionAttributesChanged',
25
+ patch: this.buildPatch(),
26
+ });
27
+
28
+ this.reset();
29
+
30
+ return promise;
31
+ }
32
+ }
@@ -0,0 +1,98 @@
1
+ import {
2
+ ExternalTrackingEvent as ExternalEvent,
3
+ ExternalTrackingEventPayload as ExternalEventPayload,
4
+ ExternalTrackingEventType as ExternalEventType,
5
+ } from '../trackingEvents';
6
+ import {formatCause} from '../error';
7
+ import {Tracker, EventListener} from '../tracker';
8
+ import {
9
+ cartModified,
10
+ cartViewed,
11
+ checkoutStarted,
12
+ eventOccurred,
13
+ goalCompleted,
14
+ interestShown,
15
+ postViewed,
16
+ orderPlaced,
17
+ productViewed,
18
+ userSignedUp,
19
+ linkOpened,
20
+ } from '../schema';
21
+
22
+ const eventSchemas = {
23
+ cartViewed: cartViewed,
24
+ cartModified: cartModified,
25
+ checkoutStarted: checkoutStarted,
26
+ orderPlaced: orderPlaced,
27
+ productViewed: productViewed,
28
+ userSignedUp: userSignedUp,
29
+ eventOccurred: eventOccurred,
30
+ interestShown: interestShown,
31
+ postViewed: postViewed,
32
+ goalCompleted: goalCompleted,
33
+ linkOpened: linkOpened,
34
+ };
35
+
36
+ type UnknownEvent<T extends ExternalEventType> = Pick<ExternalEvent<T>, 'type'>;
37
+
38
+ function validateEvent<T extends ExternalEventType>(event: UnknownEvent<T>): asserts event is ExternalEvent<T> {
39
+ const {type, ...payload} = event;
40
+
41
+ if (!(type in eventSchemas)) {
42
+ throw new Error(`Unknown event type '${type}'.`);
43
+ }
44
+
45
+ try {
46
+ eventSchemas[type].validate(payload);
47
+ } catch (violation) {
48
+ throw new Error(`Invalid event payload: ${formatCause(violation)}`);
49
+ }
50
+ }
51
+
52
+ function createEvent<T extends ExternalEventType>(type: T, payload: unknown): ExternalEvent<T> {
53
+ if (typeof type !== 'string') {
54
+ throw new Error('The event type must of type string.');
55
+ }
56
+
57
+ if (typeof payload !== 'object' || payload == null) {
58
+ throw new Error('The event payload must of type object.');
59
+ }
60
+
61
+ const event: UnknownEvent<T> = {type: type, ...payload};
62
+
63
+ validateEvent(event);
64
+
65
+ return event;
66
+ }
67
+
68
+ export class TrackerFacade {
69
+ private readonly tracker: Tracker;
70
+
71
+ public constructor(tracker: Tracker) {
72
+ this.tracker = tracker;
73
+ }
74
+
75
+ public get flushed(): Promise<void> {
76
+ return this.tracker.flushed;
77
+ }
78
+
79
+ public enable(): void {
80
+ this.tracker.enable();
81
+ }
82
+
83
+ public disable(): void {
84
+ this.tracker.disable();
85
+ }
86
+
87
+ public addListener(listener: EventListener): void {
88
+ this.tracker.addListener(listener);
89
+ }
90
+
91
+ public removeListener(listener: EventListener): void {
92
+ this.tracker.removeListener(listener);
93
+ }
94
+
95
+ public track<T extends ExternalEventType>(type: T, payload: ExternalEventPayload<T>): Promise<ExternalEvent<T>> {
96
+ return this.tracker.track(createEvent(type, payload));
97
+ }
98
+ }
@@ -0,0 +1,26 @@
1
+ import {Tracker} from '../tracker';
2
+ import {UserPatch} from './userPatch';
3
+ import {Context} from '../context';
4
+
5
+ export class UserFacade {
6
+ private readonly context: Context;
7
+
8
+ private readonly tracker: Tracker;
9
+
10
+ public constructor(context: Context, tracker: Tracker) {
11
+ this.context = context;
12
+ this.tracker = tracker;
13
+ }
14
+
15
+ public isIdentified(): boolean {
16
+ return !this.isAnonymous();
17
+ }
18
+
19
+ public isAnonymous(): boolean {
20
+ return this.context.isAnonymous();
21
+ }
22
+
23
+ public edit(): UserPatch {
24
+ return new UserPatch(this.tracker);
25
+ }
26
+ }
@@ -0,0 +1,32 @@
1
+ import {ActiveRecord} from '../activeRecord';
2
+ import {Tracker} from '../tracker';
3
+ import {UserProfileChanged} from '../trackingEvents';
4
+
5
+ export class UserPatch extends ActiveRecord<UserProfileChanged> {
6
+ private readonly tracker: Tracker;
7
+
8
+ public constructor(tracker: Tracker) {
9
+ super();
10
+
11
+ this.tracker = tracker;
12
+ }
13
+
14
+ public save(): Promise<UserProfileChanged> {
15
+ if (!this.isDirty()) {
16
+ // Empty patch
17
+ return Promise.resolve({
18
+ type: 'userProfileChanged',
19
+ patch: {operations: []},
20
+ });
21
+ }
22
+
23
+ const promise = this.tracker.track({
24
+ type: 'userProfileChanged',
25
+ patch: this.buildPatch(),
26
+ });
27
+
28
+ this.reset();
29
+
30
+ return promise;
31
+ }
32
+ }
package/.src/index.ts ADDED
@@ -0,0 +1,4 @@
1
+ import {VERSION} from './constants';
2
+ import {Sdk, Configuration} from './sdk';
3
+
4
+ export {Sdk, Configuration, VERSION};
@@ -0,0 +1,37 @@
1
+ import {Logger} from './logger';
2
+
3
+ type ConsoleMethod = {
4
+ (message?: any, ...optionalParams: any[]): void,
5
+ };
6
+
7
+ export class ConsoleLogger implements Logger {
8
+ private readonly namespace?: string;
9
+
10
+ public constructor(namespace?: string) {
11
+ this.namespace = namespace;
12
+ }
13
+
14
+ public get debug(): (message: string) => void {
15
+ return this.bind(window.console.debug);
16
+ }
17
+
18
+ public get info(): (message: string) => void {
19
+ return this.bind(window.console.info);
20
+ }
21
+
22
+ public get warn(): (message: string) => void {
23
+ return this.bind(window.console.warn);
24
+ }
25
+
26
+ public get error(): (message: string) => void {
27
+ return this.bind(window.console.error);
28
+ }
29
+
30
+ private bind(method: ConsoleMethod): ConsoleMethod {
31
+ if (this.namespace !== undefined) {
32
+ return method.bind(window.console, `[${this.namespace}]`);
33
+ }
34
+
35
+ return method.bind(window.console);
36
+ }
37
+ }
@@ -0,0 +1,4 @@
1
+ export * from './logger';
2
+ export {ConsoleLogger} from './consoleLogger';
3
+ export {NamespacedLogger} from './namespacedLogger';
4
+ export {NullLogger} from './nullLogger';
@@ -0,0 +1,13 @@
1
+ export interface Logger {
2
+ debug(message: string): void;
3
+
4
+ info(message: string): void;
5
+
6
+ warn(message: string): void;
7
+
8
+ error(message: string): void;
9
+ }
10
+
11
+ export interface LoggerFactory {
12
+ (namespace?: string): Logger;
13
+ }
@@ -0,0 +1,32 @@
1
+ import {Logger} from './logger';
2
+
3
+ export class NamespacedLogger implements Logger {
4
+ private readonly logger: Logger;
5
+
6
+ private readonly namespace: string;
7
+
8
+ public constructor(logger: Logger, namespace: string) {
9
+ this.logger = logger;
10
+ this.namespace = namespace;
11
+ }
12
+
13
+ public debug(message: string): void {
14
+ this.logger.debug(this.format(message));
15
+ }
16
+
17
+ public info(message: string): void {
18
+ this.logger.info(this.format(message));
19
+ }
20
+
21
+ public warn(message: string): void {
22
+ this.logger.warn(this.format(message));
23
+ }
24
+
25
+ public error(message: string): void {
26
+ this.logger.error(this.format(message));
27
+ }
28
+
29
+ private format(message: string): string {
30
+ return `[${this.namespace}] ${message}`;
31
+ }
32
+ }
@@ -0,0 +1,19 @@
1
+ import {Logger} from './logger';
2
+
3
+ export class NullLogger implements Logger {
4
+ public debug(): void {
5
+ // suppress debug logs
6
+ }
7
+
8
+ public info(): void {
9
+ // suppress info logs
10
+ }
11
+
12
+ public warn(): void {
13
+ // suppress warning logs
14
+ }
15
+
16
+ public error(): void {
17
+ // suppress error logs
18
+ }
19
+ }
@@ -0,0 +1,69 @@
1
+ export class NamespacedStorage implements Storage {
2
+ private readonly storage: Storage;
3
+
4
+ private readonly namespace: string;
5
+
6
+ public constructor(storage: Storage, namespace: string) {
7
+ if (namespace === '') {
8
+ throw new Error('The namespace cannot be empty.');
9
+ }
10
+
11
+ this.storage = storage;
12
+ this.namespace = namespace;
13
+ }
14
+
15
+ public get length(): number {
16
+ return this.getKeys().length;
17
+ }
18
+
19
+ public clear(): void {
20
+ for (const key of this.getKeys()) {
21
+ this.storage.removeItem(key);
22
+ }
23
+ }
24
+
25
+ public getItem(key: string): string | null {
26
+ return this.storage.getItem(this.getPrefixedKey(key));
27
+ }
28
+
29
+ public key(index: number): string | null {
30
+ const keys = this.getKeys();
31
+
32
+ if (index >= keys.length) {
33
+ return null;
34
+ }
35
+
36
+ return keys[index].substring(this.namespace.length + 1);
37
+ }
38
+
39
+ public removeItem(key: string): void {
40
+ this.storage.removeItem(this.getPrefixedKey(key));
41
+ }
42
+
43
+ public setItem(key: string, value: string): void {
44
+ this.storage.setItem(this.getPrefixedKey(key), value);
45
+ }
46
+
47
+ private getKeys(): string[] {
48
+ const keys = [];
49
+ const prefix = this.getPrefix();
50
+
51
+ for (let index = 0; index < this.storage.length; index++) {
52
+ const key = this.storage.key(index);
53
+
54
+ if (key !== null && key.indexOf(prefix) === 0) {
55
+ keys.push(key);
56
+ }
57
+ }
58
+
59
+ return keys;
60
+ }
61
+
62
+ private getPrefixedKey(key: string): string {
63
+ return this.getPrefix() + key;
64
+ }
65
+
66
+ private getPrefix(): string {
67
+ return `${this.namespace}.`;
68
+ }
69
+ }