@croct/sdk 0.10.0 → 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 (231) 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 +33 -36
  100. package/base64Url.js +1 -0
  101. package/cache/cache.js +1 -0
  102. package/cache/fallbackCache.js +16 -32
  103. package/cache/inMemoryCache.js +10 -10
  104. package/cache/index.js +2 -1
  105. package/cache/localStorageCache.js +25 -25
  106. package/channel/beaconSocketChannel.d.ts +1 -1
  107. package/channel/beaconSocketChannel.js +50 -79
  108. package/channel/channel.d.ts +1 -1
  109. package/channel/channel.js +1 -0
  110. package/channel/encodedChannel.js +9 -10
  111. package/channel/guaranteedChannel.d.ts +4 -4
  112. package/channel/guaranteedChannel.js +42 -43
  113. package/channel/index.js +2 -1
  114. package/channel/queuedChannel.js +36 -64
  115. package/channel/retryChannel.d.ts +1 -1
  116. package/channel/retryChannel.js +45 -77
  117. package/channel/sandboxChannel.js +18 -18
  118. package/channel/socketChannel.d.ts +4 -4
  119. package/channel/socketChannel.js +78 -79
  120. package/cid/assigner.js +1 -0
  121. package/cid/cachedAssigner.js +16 -27
  122. package/cid/fixedAssigner.js +6 -6
  123. package/cid/index.js +2 -1
  124. package/cid/remoteAssigner.js +24 -36
  125. package/constants.d.ts +6 -5
  126. package/constants.js +7 -5
  127. package/container.d.ts +13 -6
  128. package/container.js +153 -168
  129. package/contentFetcher.d.ts +59 -0
  130. package/contentFetcher.js +130 -0
  131. package/context.d.ts +3 -3
  132. package/context.js +37 -38
  133. package/error.js +3 -2
  134. package/evaluator.d.ts +33 -24
  135. package/evaluator.js +127 -117
  136. package/eventManager.d.ts +1 -1
  137. package/eventManager.js +15 -15
  138. package/facade/contentFetcherFacade.d.ts +27 -0
  139. package/facade/contentFetcherFacade.js +41 -0
  140. package/facade/evaluatorFacade.d.ts +13 -3
  141. package/facade/evaluatorFacade.js +58 -72
  142. package/facade/index.js +1 -0
  143. package/facade/sdkFacade.d.ts +10 -3
  144. package/facade/sdkFacade.js +130 -141
  145. package/facade/sessionFacade.js +7 -7
  146. package/facade/sessionPatch.js +10 -13
  147. package/facade/trackerFacade.js +33 -38
  148. package/facade/userFacade.js +11 -11
  149. package/facade/userPatch.js +10 -13
  150. package/index.js +3 -2
  151. package/logging/consoleLogger.js +19 -35
  152. package/logging/index.js +2 -1
  153. package/logging/logger.js +1 -0
  154. package/logging/namespacedLogger.js +15 -15
  155. package/logging/nullLogger.js +11 -13
  156. package/namespacedStorage.js +31 -47
  157. package/package.json +13 -16
  158. package/patch.d.ts +1 -1
  159. package/patch.js +1 -0
  160. package/queue/capacityRestrictedQueue.js +18 -18
  161. package/queue/inMemoryQueue.js +23 -28
  162. package/queue/index.js +2 -1
  163. package/queue/monitoredQueue.d.ts +2 -2
  164. package/queue/monitoredQueue.js +40 -40
  165. package/queue/persistentQueue.js +34 -38
  166. package/queue/queue.js +1 -0
  167. package/retry/arbitraryPolicy.js +9 -10
  168. package/retry/backoffPolicy.d.ts +1 -1
  169. package/retry/backoffPolicy.js +12 -13
  170. package/retry/index.js +2 -1
  171. package/retry/maxAttemptsPolicy.js +8 -8
  172. package/retry/neverPolicy.js +7 -9
  173. package/retry/policy.js +1 -0
  174. package/schema/attributeSchema.js +2 -1
  175. package/schema/contentFetcherSchemas.d.ts +2 -0
  176. package/schema/contentFetcherSchemas.js +23 -0
  177. package/schema/contentSchemas.js +2 -1
  178. package/schema/contextSchemas.js +2 -1
  179. package/schema/ecommerceSchemas.js +2 -1
  180. package/schema/evaluatorSchemas.d.ts +2 -0
  181. package/schema/{evaluationSchemas.js → evaluatorSchemas.js} +4 -3
  182. package/schema/eventSchemas.js +6 -7
  183. package/schema/index.d.ts +2 -1
  184. package/schema/index.js +4 -2
  185. package/schema/loggerSchema.js +2 -1
  186. package/schema/operationSchemas.js +9 -8
  187. package/schema/sdkFacadeSchemas.js +10 -6
  188. package/schema/sdkSchemas.js +9 -5
  189. package/schema/tokenSchema.js +6 -4
  190. package/schema/userSchema.js +3 -2
  191. package/sdk.d.ts +9 -3
  192. package/sdk.js +82 -127
  193. package/sdkEvents.d.ts +3 -3
  194. package/sdkEvents.js +1 -0
  195. package/sourceLocation.d.ts +3 -3
  196. package/sourceLocation.js +14 -14
  197. package/tab.d.ts +5 -5
  198. package/tab.js +51 -80
  199. package/token/cachedTokenStore.js +10 -10
  200. package/token/inMemoryTokenStore.js +8 -8
  201. package/token/index.js +2 -1
  202. package/token/replicatedTokenStore.js +8 -8
  203. package/token/token.d.ts +9 -5
  204. package/token/token.js +64 -57
  205. package/tracker.d.ts +4 -4
  206. package/tracker.js +146 -122
  207. package/trackingEvents.d.ts +36 -36
  208. package/trackingEvents.js +13 -6
  209. package/transformer.js +2 -1
  210. package/utilityTypes.d.ts +2 -2
  211. package/utilityTypes.js +1 -0
  212. package/uuid.js +10 -7
  213. package/validation/arrayType.d.ts +2 -2
  214. package/validation/arrayType.js +30 -27
  215. package/validation/booleanType.js +12 -15
  216. package/validation/functionType.js +12 -15
  217. package/validation/index.js +2 -1
  218. package/validation/jsonType.d.ts +2 -2
  219. package/validation/jsonType.js +62 -80
  220. package/validation/mixedSchema.js +5 -7
  221. package/validation/nullType.js +12 -15
  222. package/validation/numberType.d.ts +1 -1
  223. package/validation/numberType.js +24 -22
  224. package/validation/objectType.d.ts +1 -1
  225. package/validation/objectType.js +62 -72
  226. package/validation/schema.js +7 -10
  227. package/validation/stringType.d.ts +1 -1
  228. package/validation/stringType.js +37 -47
  229. package/validation/unionType.js +28 -77
  230. package/validation/violation.js +2 -2
  231. package/schema/evaluationSchemas.d.ts +0 -2
@@ -0,0 +1,112 @@
1
+ import {OutputChannel} from './channel';
2
+ import {Queue} from '../queue';
3
+ import {Logger, NullLogger} from '../logging';
4
+
5
+ export class QueuedChannel<T> implements OutputChannel<T> {
6
+ private readonly channel: OutputChannel<T>;
7
+
8
+ private readonly queue: Queue<T>;
9
+
10
+ private readonly logger: Logger;
11
+
12
+ private pending?: Promise<void>;
13
+
14
+ private closed = false;
15
+
16
+ public constructor(channel: OutputChannel<T>, queue: Queue<T>, logger?: Logger) {
17
+ this.channel = channel;
18
+ this.queue = queue;
19
+ this.logger = logger ?? new NullLogger();
20
+ }
21
+
22
+ public flush(): Promise<void> {
23
+ if (this.pending === undefined) {
24
+ return this.requeue();
25
+ }
26
+
27
+ return this.pending.catch(this.requeue.bind(this));
28
+ }
29
+
30
+ public publish(message: T): Promise<void> {
31
+ if (this.closed) {
32
+ return Promise.reject(new Error('Channel is closed.'));
33
+ }
34
+
35
+ if (this.queue.length() >= this.queue.getCapacity()) {
36
+ this.logger.warn('The queue is full, message rejected.');
37
+
38
+ return Promise.reject(new Error('The queue is full.'));
39
+ }
40
+
41
+ if (this.pending === undefined) {
42
+ this.pending = this.queue.isEmpty()
43
+ ? Promise.resolve()
44
+ : Promise.reject(new Error('The queue must be flushed.'));
45
+ }
46
+
47
+ this.enqueue(message);
48
+
49
+ this.pending = this.pending.then(
50
+ () => this.channel
51
+ .publish(message)
52
+ .then(this.dequeue.bind(this)),
53
+ );
54
+
55
+ return this.pending;
56
+ }
57
+
58
+ private enqueue(message: T): void {
59
+ this.logger.debug('Enqueueing message...');
60
+ this.logger.debug(`Queue length: ${this.queue.length() + 1}`);
61
+
62
+ this.queue.push(message);
63
+ }
64
+
65
+ private dequeue(): void {
66
+ this.logger.debug('Dequeuing message...');
67
+ this.logger.debug(`Queue length: ${Math.max(0, this.queue.length() - 1)}`);
68
+
69
+ this.queue.shift();
70
+ }
71
+
72
+ private requeue(): Promise<void> {
73
+ if (this.closed) {
74
+ return Promise.reject(new Error('Channel is closed.'));
75
+ }
76
+
77
+ this.pending = Promise.resolve();
78
+
79
+ if (this.queue.isEmpty()) {
80
+ return this.pending;
81
+ }
82
+
83
+ const length = this.queue.length();
84
+
85
+ this.logger.debug('Requeuing messages...');
86
+ this.logger.debug(`Queue length: ${length}`);
87
+
88
+ for (const message of this.queue.all()) {
89
+ this.pending = this.pending.then(
90
+ () => this.channel
91
+ .publish(message)
92
+ .then(this.dequeue.bind(this)),
93
+ );
94
+ }
95
+
96
+ return this.pending;
97
+ }
98
+
99
+ public async close(): Promise<void> {
100
+ this.closed = true;
101
+
102
+ await this.channel.close();
103
+
104
+ if (this.pending !== undefined) {
105
+ try {
106
+ await this.pending;
107
+ } catch {
108
+ // suppress errors
109
+ }
110
+ }
111
+ }
112
+ }
@@ -0,0 +1,90 @@
1
+ import {OutputChannel} from './channel';
2
+ import {Logger, NullLogger} from '../logging';
3
+ import {RetryPolicy} from '../retry';
4
+
5
+ type Configuration<T> = {
6
+ channel: OutputChannel<T>,
7
+ retryPolicy: RetryPolicy<T>,
8
+ logger?: Logger,
9
+ };
10
+
11
+ export class RetryChannel<T> implements OutputChannel<T> {
12
+ private readonly channel: OutputChannel<T>;
13
+
14
+ private readonly retryPolicy: RetryPolicy<T>;
15
+
16
+ private readonly logger: Logger;
17
+
18
+ private closed = false;
19
+
20
+ public constructor({channel, retryPolicy, logger}: Configuration<T>) {
21
+ this.channel = channel;
22
+ this.retryPolicy = retryPolicy;
23
+ this.logger = logger ?? new NullLogger();
24
+ }
25
+
26
+ public publish(message: T): Promise<void> {
27
+ if (this.closed) {
28
+ return Promise.reject(new Error('The channel is closed.'));
29
+ }
30
+
31
+ return this.channel
32
+ .publish(message)
33
+ .catch(error => this.retry(message, error));
34
+ }
35
+
36
+ public async retry(message: T, error: unknown): Promise<void> {
37
+ let attempt = 0;
38
+
39
+ while (this.retryPolicy.shouldRetry(attempt, message, error)) {
40
+ if (this.closed) {
41
+ throw new Error('Connection deliberately closed.');
42
+ }
43
+
44
+ const delay = this.retryPolicy.getDelay(attempt);
45
+
46
+ this.logger.debug(`Retry attempt ${attempt + 1}`);
47
+
48
+ if (delay > 0) {
49
+ this.logger.debug(`Retry attempt delayed in ${delay}ms`);
50
+
51
+ await new Promise<void>((resolve, reject): void => {
52
+ const closeWatcher = window.setInterval(
53
+ () => {
54
+ if (this.closed) {
55
+ // Cancel delay immediately when the channel is closed
56
+ window.clearInterval(closeWatcher);
57
+
58
+ reject(new Error('Connection deliberately closed.'));
59
+ }
60
+ },
61
+ 0,
62
+ );
63
+
64
+ window.setTimeout(
65
+ (): void => {
66
+ window.clearInterval(closeWatcher);
67
+
68
+ resolve();
69
+ },
70
+ delay,
71
+ );
72
+ });
73
+ }
74
+
75
+ try {
76
+ return await this.channel.publish(message);
77
+ } catch {
78
+ attempt += 1;
79
+ }
80
+ }
81
+
82
+ throw new Error('Maximum retry attempts reached.');
83
+ }
84
+
85
+ public close(): Promise<void> {
86
+ this.closed = true;
87
+
88
+ return this.channel.close();
89
+ }
90
+ }
@@ -0,0 +1,43 @@
1
+ import {ChannelListener, DuplexChannel} from './channel';
2
+
3
+ export class SandboxChannel<I, O> implements DuplexChannel<I, O> {
4
+ private readonly listeners: Array<ChannelListener<I>> = [];
5
+
6
+ public readonly messages: O[] = [];
7
+
8
+ private closed = false;
9
+
10
+ public publish(message: O): Promise<void> {
11
+ this.messages.push(message);
12
+
13
+ return Promise.resolve();
14
+ }
15
+
16
+ public notify(message: I): void {
17
+ this.listeners.forEach(dispatch => dispatch(message));
18
+ }
19
+
20
+ public subscribe(listener: ChannelListener<I>): void {
21
+ if (!this.listeners.includes(listener)) {
22
+ this.listeners.push(listener);
23
+ }
24
+ }
25
+
26
+ public unsubscribe(listener: ChannelListener<I>): void {
27
+ const index = this.listeners.indexOf(listener);
28
+
29
+ if (index >= 0) {
30
+ this.listeners.splice(index, 1);
31
+ }
32
+ }
33
+
34
+ public close(): Promise<void> {
35
+ this.closed = true;
36
+
37
+ return Promise.resolve();
38
+ }
39
+
40
+ public isClosed(): boolean {
41
+ return this.closed;
42
+ }
43
+ }
@@ -0,0 +1,217 @@
1
+ import {Logger, NullLogger} from '../logging';
2
+ import {ChannelListener, DuplexChannel} from './channel';
3
+ import {formatCause} from '../error';
4
+
5
+ type Input = string | ArrayBufferLike | Blob | ArrayBufferView;
6
+ type Output = string | ArrayBuffer | Blob;
7
+
8
+ type Options = {
9
+ closeTimeout: number,
10
+ connectionTimeout: number,
11
+ protocols: string | string[],
12
+ binaryType?: BinaryType,
13
+ };
14
+
15
+ export type Configuration = Partial<Options> & {
16
+ url: string,
17
+ logger?: Logger,
18
+ };
19
+
20
+ export class SocketChannel<I extends Input, O extends Output> implements DuplexChannel<I, O> {
21
+ private readonly url: string;
22
+
23
+ private readonly logger: Logger;
24
+
25
+ private readonly options: Options;
26
+
27
+ private readonly listeners: Array<ChannelListener<I>> = [];
28
+
29
+ private connection?: Promise<WebSocket>;
30
+
31
+ private closed = false;
32
+
33
+ public constructor({url, logger, ...options}: Configuration) {
34
+ this.url = url;
35
+ this.logger = logger ?? new NullLogger();
36
+ this.options = {
37
+ ...options,
38
+ closeTimeout: options.closeTimeout ?? 5000,
39
+ connectionTimeout: options.connectionTimeout ?? 10000,
40
+ protocols: options.protocols ?? [],
41
+ };
42
+ }
43
+
44
+ public get connected(): Promise<boolean> {
45
+ if (this.connection === undefined) {
46
+ return Promise.resolve(false);
47
+ }
48
+
49
+ return this.connection.then(() => true, () => false);
50
+ }
51
+
52
+ public publish(message: O): Promise<void> {
53
+ return this.connect().then(socket => {
54
+ socket.send(message);
55
+
56
+ this.logger.debug('Message sent.');
57
+ });
58
+ }
59
+
60
+ public subscribe(listener: ChannelListener<I>): void {
61
+ if (!this.listeners.includes(listener)) {
62
+ this.listeners.push(listener);
63
+ }
64
+ }
65
+
66
+ public unsubscribe(listener: ChannelListener<I>): void {
67
+ const index = this.listeners.indexOf(listener);
68
+
69
+ if (index >= 0) {
70
+ this.listeners.splice(index, 1);
71
+ }
72
+ }
73
+
74
+ private notify(message: I): void {
75
+ this.listeners.forEach(dispatch => dispatch(message));
76
+ }
77
+
78
+ private connect(): Promise<WebSocket> {
79
+ if (this.closed) {
80
+ return Promise.reject(new Error('Channel has been closed.'));
81
+ }
82
+
83
+ if (this.connection !== undefined) {
84
+ return this.connection
85
+ .then(connection => {
86
+ const state = connection.readyState;
87
+
88
+ if (state === WebSocket.OPEN) {
89
+ return connection;
90
+ }
91
+
92
+ throw new Error('Connection lost.');
93
+ })
94
+ .catch(() => {
95
+ // Reconnect
96
+ delete this.connection;
97
+
98
+ return this.connect();
99
+ });
100
+ }
101
+
102
+ this.connection = new Promise((resolve, reject): void => {
103
+ this.logger.debug('Connecting...');
104
+
105
+ const connection = new window.WebSocket(this.url, this.options.protocols);
106
+
107
+ if (this.options.binaryType !== undefined) {
108
+ connection.binaryType = this.options.binaryType;
109
+ }
110
+
111
+ const abortListener = (): void => {
112
+ const reason = 'Maximum connection timeout reached.';
113
+
114
+ this.logger.error(reason);
115
+
116
+ reject(new Error(reason));
117
+
118
+ connection.close(1000, reason);
119
+ };
120
+
121
+ const abortTimer: number = window.setTimeout(abortListener, this.options.connectionTimeout);
122
+
123
+ const openListener = (): void => {
124
+ window.clearTimeout(abortTimer);
125
+
126
+ this.logger.info('Connection established.');
127
+
128
+ connection.removeEventListener('open', openListener);
129
+
130
+ resolve(connection);
131
+ };
132
+
133
+ const errorListener = (): void => {
134
+ if (!this.closed) {
135
+ this.logger.error('Connection error.');
136
+ }
137
+ };
138
+
139
+ const messageListener = (event: MessageEvent): void => {
140
+ this.logger.debug('Message received.');
141
+
142
+ this.notify(event.data);
143
+ };
144
+
145
+ const closeListener = (event: CloseEvent): void => {
146
+ window.clearTimeout(abortTimer);
147
+
148
+ const reason = `${formatCause(event.reason ?? 'unknown')} (code ${event.code})`;
149
+ const message = `Connection has been closed, reason: ${reason}`;
150
+
151
+ if (!this.closed) {
152
+ this.logger.info(message);
153
+ }
154
+
155
+ connection.removeEventListener('open', openListener);
156
+ connection.removeEventListener('error', errorListener);
157
+ connection.removeEventListener('close', closeListener);
158
+ connection.removeEventListener('message', messageListener);
159
+
160
+ reject(new Error(message));
161
+ };
162
+
163
+ connection.addEventListener('open', openListener, {once: true});
164
+ connection.addEventListener('close', closeListener, {once: true});
165
+ connection.addEventListener('error', errorListener);
166
+ connection.addEventListener('message', messageListener);
167
+ });
168
+
169
+ return this.connection;
170
+ }
171
+
172
+ public close(): Promise<void> {
173
+ this.logger.debug('Closing connection...');
174
+
175
+ return new Promise((resolve, reject): void => {
176
+ this.closed = true;
177
+
178
+ if (this.connection === undefined) {
179
+ this.logger.debug('Connection is not open.');
180
+
181
+ resolve();
182
+
183
+ return;
184
+ }
185
+
186
+ this.connection.then(
187
+ (connection): void => {
188
+ let abortTimer: number | undefined;
189
+
190
+ const abort = (): void => {
191
+ this.logger.warn('Connection could not be closed within the timeout period.');
192
+
193
+ reject(new Error('Maximum close timeout reached.'));
194
+ };
195
+
196
+ const close = (): void => {
197
+ window.clearTimeout(abortTimer);
198
+
199
+ this.logger.info('Connection gracefully closed.');
200
+
201
+ resolve();
202
+ };
203
+
204
+ connection.addEventListener('close', close, {once: true});
205
+ connection.close(1000, 'Deliberate disconnection.');
206
+
207
+ abortTimer = window.setTimeout(abort, this.options.closeTimeout);
208
+ },
209
+ () => {
210
+ this.logger.info('Connection closed.');
211
+
212
+ resolve();
213
+ },
214
+ );
215
+ });
216
+ }
217
+ }
@@ -0,0 +1,3 @@
1
+ export interface CidAssigner {
2
+ assignCid(): Promise<string>;
3
+ }
@@ -0,0 +1,35 @@
1
+ import {Logger, NullLogger} from '../logging';
2
+ import {Cache} from '../cache';
3
+ import {CidAssigner} from './assigner';
4
+
5
+ export class CachedAssigner implements CidAssigner {
6
+ private readonly assigner: CidAssigner;
7
+
8
+ private readonly cache: Cache;
9
+
10
+ private readonly logger: Logger;
11
+
12
+ public constructor(assigner: CidAssigner, cache: Cache, logger?: Logger) {
13
+ this.assigner = assigner;
14
+ this.cache = cache;
15
+ this.logger = logger ?? new NullLogger();
16
+ }
17
+
18
+ public async assignCid(): Promise<string> {
19
+ const cid = this.cache.get();
20
+
21
+ if (cid !== null) {
22
+ this.logger.debug('Previous CID loaded from cache');
23
+
24
+ return cid;
25
+ }
26
+
27
+ const newCid = await this.assigner.assignCid();
28
+
29
+ this.cache.put(newCid);
30
+
31
+ this.logger.debug('New CID stored into cache');
32
+
33
+ return newCid;
34
+ }
35
+ }
@@ -0,0 +1,13 @@
1
+ import {CidAssigner} from './assigner';
2
+
3
+ export class FixedAssigner implements CidAssigner {
4
+ private readonly cid: string;
5
+
6
+ public constructor(cid: string) {
7
+ this.cid = cid;
8
+ }
9
+
10
+ public assignCid(): Promise<string> {
11
+ return Promise.resolve(this.cid);
12
+ }
13
+ }
@@ -0,0 +1,4 @@
1
+ export * from './assigner';
2
+ export {CachedAssigner} from './cachedAssigner';
3
+ export {FixedAssigner} from './fixedAssigner';
4
+ export {RemoteAssigner} from './remoteAssigner';
@@ -0,0 +1,47 @@
1
+ import {Logger, NullLogger} from '../logging';
2
+ import {formatCause} from '../error';
3
+ import {CidAssigner} from './assigner';
4
+
5
+ export class RemoteAssigner implements CidAssigner {
6
+ private readonly logger: Logger;
7
+
8
+ private readonly endpoint: string;
9
+
10
+ private pending?: Promise<string>;
11
+
12
+ public constructor(endpoint: string, logger?: Logger) {
13
+ this.endpoint = endpoint;
14
+ this.logger = logger ?? new NullLogger();
15
+ }
16
+
17
+ public assignCid(): Promise<string> {
18
+ if (this.pending === undefined) {
19
+ this.pending = this.fetchCid().finally(() => {
20
+ this.pending = undefined;
21
+ });
22
+ }
23
+
24
+ return this.pending;
25
+ }
26
+
27
+ private async fetchCid(): Promise<string> {
28
+ const options: RequestInit = {
29
+ method: 'GET',
30
+ credentials: 'include',
31
+ };
32
+
33
+ const response = await window.fetch(this.endpoint, options);
34
+
35
+ if (!response.ok) {
36
+ const error = new Error(`Failed to assign CID: ${formatCause(response.statusText)}`);
37
+
38
+ this.logger.error(error.message);
39
+
40
+ throw error;
41
+ }
42
+
43
+ this.logger.debug('New CID successfully assigned');
44
+
45
+ return response.text();
46
+ }
47
+ }
@@ -0,0 +1,6 @@
1
+ export const CID_ASSIGNER_ENDPOINT_URL = '<@cidAssignerEndpointUrl@>';
2
+ export const TRACKER_ENDPOINT_URL = '<@trackerEndpointUrl@>';
3
+ export const EVALUATION_ENDPOINT_URL = '<@evaluationEndpointUrl@>';
4
+ export const CONTENT_ENDPOINT_URL = '<@contentEndpointUrl@>';
5
+ export const MAX_QUERY_LENGTH = parseInt('<@maxQueryLength@>', 10);
6
+ export const VERSION = '<@version@>';