@configdirector/react-native-sdk 1.5.1 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,1307 +1,8 @@
1
- import { sha256 } from "@noble/hashes/sha2.js";
1
+ import { a as CLIENT_BASE_URL, d as _defineProperty, i as fetchWithTimeout, n as readInstalledTestClient, o as createDefaultLogger, r as DefaultConfigDirectorClient, s as isFetchErrorFatal } from "./installedTestClient-CxGEAMOE.mjs";
2
2
  import React, { Component, useContext as useContext$1 } from "react";
3
- import { AppState } from "react-native";
3
+ import { AppState, NativeModules } from "react-native";
4
+ import { sha256 } from "@noble/hashes/sha2.js";
4
5
  import { jsx } from "react/jsx-runtime";
5
- //#region \0@oxc-project+runtime@0.122.0/helpers/typeof.js
6
- function _typeof(o) {
7
- "@babel/helpers - typeof";
8
- return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o) {
9
- return typeof o;
10
- } : function(o) {
11
- return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
12
- }, _typeof(o);
13
- }
14
- //#endregion
15
- //#region \0@oxc-project+runtime@0.122.0/helpers/toPrimitive.js
16
- function toPrimitive(t, r) {
17
- if ("object" != _typeof(t) || !t) return t;
18
- var e = t[Symbol.toPrimitive];
19
- if (void 0 !== e) {
20
- var i = e.call(t, r || "default");
21
- if ("object" != _typeof(i)) return i;
22
- throw new TypeError("@@toPrimitive must return a primitive value.");
23
- }
24
- return ("string" === r ? String : Number)(t);
25
- }
26
- //#endregion
27
- //#region \0@oxc-project+runtime@0.122.0/helpers/toPropertyKey.js
28
- function toPropertyKey(t) {
29
- var i = toPrimitive(t, "string");
30
- return "symbol" == _typeof(i) ? i : i + "";
31
- }
32
- //#endregion
33
- //#region \0@oxc-project+runtime@0.122.0/helpers/defineProperty.js
34
- function _defineProperty(e, r, t) {
35
- return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
36
- value: t,
37
- enumerable: !0,
38
- configurable: !0,
39
- writable: !0
40
- }) : e[r] = t, e;
41
- }
42
- //#endregion
43
- //#region ../js-client-core/src/Emitter.ts
44
- var Emitter = class {
45
- constructor() {
46
- _defineProperty(this, "handlerMap", /* @__PURE__ */ new Map());
47
- }
48
- on(name, handler) {
49
- const handlers = this.handlerMap.get(name);
50
- if (handlers) handlers.push(handler);
51
- else this.handlerMap.set(name, [handler]);
52
- }
53
- once(name, handler) {
54
- const self = this;
55
- function onceHandler(payload) {
56
- self.off(name, onceHandler);
57
- handler.apply(self, payload);
58
- }
59
- this.on(name, onceHandler);
60
- }
61
- off(name, handler) {
62
- const handlers = this.handlerMap.get(name);
63
- if (!handlers) return;
64
- if (handler) {
65
- const listenerIndex = handlers.indexOf(handler);
66
- if (listenerIndex >= 0) handlers.splice(listenerIndex, 1);
67
- } else this.handlerMap.set(name, []);
68
- }
69
- clear() {
70
- this.handlerMap.clear();
71
- }
72
- emit(name, payload) {
73
- const handlers = this.handlerMap.get(name);
74
- if (!handlers) return;
75
- handlers.slice().map((h) => h(payload));
76
- }
77
- };
78
- //#endregion
79
- //#region ../shared/src/errors.ts
80
- var ConfigDirectorConnectionError = class ConfigDirectorConnectionError extends Error {
81
- constructor(message, status) {
82
- super(message);
83
- _defineProperty(this, "name", "ConfigDirectorConnectionError");
84
- _defineProperty(this, "status", void 0);
85
- this.status = status;
86
- Object.setPrototypeOf(this, ConfigDirectorConnectionError.prototype);
87
- }
88
- };
89
- var ConfigDirectorValidationError = class ConfigDirectorValidationError extends Error {
90
- constructor(message) {
91
- super(message);
92
- _defineProperty(this, "name", "ConfigDirectorValidationError");
93
- Object.setPrototypeOf(this, ConfigDirectorValidationError.prototype);
94
- }
95
- };
96
- //#endregion
97
- //#region ../js-client-core/src/errors.ts
98
- const isFetchErrorFatal = (fetchError) => {
99
- if ((fetchError === null || fetchError === void 0 ? void 0 : fetchError.name) === "NotAllowedError") return true;
100
- else if (fetchError instanceof TypeError) return true;
101
- return false;
102
- };
103
- //#endregion
104
- //#region ../eventsource/src/errors.ts
105
- var StreamClosedError = class StreamClosedError extends Error {
106
- constructor(message) {
107
- super(message);
108
- _defineProperty(this, "name", "StreamClosedError");
109
- Object.setPrototypeOf(this, StreamClosedError.prototype);
110
- }
111
- };
112
- var MissingResponseBodyError = class MissingResponseBodyError extends Error {
113
- constructor(message) {
114
- super(message);
115
- _defineProperty(this, "name", "MissingResponseBodyError");
116
- Object.setPrototypeOf(this, MissingResponseBodyError.prototype);
117
- }
118
- };
119
- var InvalidOptionError = class InvalidOptionError extends Error {
120
- constructor(message) {
121
- super(message);
122
- _defineProperty(this, "name", "InvalidOptionError");
123
- Object.setPrototypeOf(this, InvalidOptionError.prototype);
124
- }
125
- };
126
- var ValueOutOfRangeError = class ValueOutOfRangeError extends Error {
127
- constructor(message) {
128
- super(message);
129
- _defineProperty(this, "name", "ValueOutOfRangeError");
130
- Object.setPrototypeOf(this, ValueOutOfRangeError.prototype);
131
- }
132
- };
133
- //#endregion
134
- //#region ../eventsource/src/types.ts
135
- let ReadyState = /* @__PURE__ */ function(ReadyState) {
136
- ReadyState["OPEN"] = "open";
137
- ReadyState["CLOSED"] = "closed";
138
- ReadyState["CONNECTING"] = "connecting";
139
- return ReadyState;
140
- }({});
141
- //#endregion
142
- //#region ../eventsource/src/EventSourceParser.ts
143
- var EventSourceParser = class {
144
- constructor(options) {
145
- var _options$onEvent;
146
- _defineProperty(this, "isFirstChunk", true);
147
- _defineProperty(this, "bufferedInput", "");
148
- _defineProperty(this, "currentEvent", { data: "" });
149
- _defineProperty(this, "lastEventId", void 0);
150
- _defineProperty(this, "onEvent", void 0);
151
- _defineProperty(this, "onRetry", void 0);
152
- _defineProperty(this, "onComment", void 0);
153
- this.currentEvent = { data: "" };
154
- this.onEvent = (_options$onEvent = options.onEvent) !== null && _options$onEvent !== void 0 ? _options$onEvent : (() => {});
155
- this.onRetry = options.onRetry;
156
- this.onComment = options.onComment;
157
- }
158
- parse(chunk) {
159
- let input = chunk;
160
- if (this.isFirstChunk) {
161
- this.isFirstChunk = false;
162
- if (input.startsWith("")) input = input.slice(1);
163
- else if (input.startsWith("")) input = input.slice(3);
164
- }
165
- const lines = this.scanLines(this.bufferedInput + input);
166
- for (const line of lines) this.dispatchLine(line);
167
- }
168
- finish() {
169
- this.bufferedInput = "";
170
- }
171
- /**
172
- * Scans `text` character by character to extract complete lines, recognizing
173
- * CR, LF, and CRLF as line terminators per the SSE spec. Any unterminated
174
- * trailing text is buffered and prepended to the next chunk.
175
- */
176
- scanLines(text) {
177
- const lines = [];
178
- let lineStart = 0;
179
- let i = 0;
180
- while (i < text.length) {
181
- const ch = text[i];
182
- if (ch === "\r" || ch === "\n") {
183
- lines.push(text.slice(lineStart, i));
184
- if (ch === "\r" && text[i + 1] === "\n") i++;
185
- i++;
186
- lineStart = i;
187
- } else i++;
188
- }
189
- this.bufferedInput = text.slice(lineStart);
190
- return lines;
191
- }
192
- dispatchLine(line) {
193
- if (line.startsWith(":")) {
194
- var _this$onComment;
195
- (_this$onComment = this.onComment) === null || _this$onComment === void 0 || _this$onComment.call(this, this.extractValue(line, 1));
196
- return;
197
- }
198
- if (line === "") {
199
- this.emitEvent();
200
- return;
201
- }
202
- this.applyField(this.parseField(line));
203
- }
204
- emitEvent() {
205
- const data = this.currentEvent.data.endsWith("\n") ? this.currentEvent.data.slice(0, -1) : this.currentEvent.data;
206
- if (data !== "") this.onEvent({
207
- ...this.currentEvent,
208
- id: this.lastEventId,
209
- data
210
- });
211
- this.currentEvent = { data: "" };
212
- }
213
- applyField(parsed) {
214
- switch (parsed.field) {
215
- case "event":
216
- this.currentEvent.type = parsed.value;
217
- break;
218
- case "data":
219
- this.currentEvent.data += parsed.value + "\n";
220
- break;
221
- case "id":
222
- if (!parsed.value.includes("\0")) this.lastEventId = parsed.value;
223
- break;
224
- case "retry":
225
- if (/^\d+$/.test(parsed.value)) {
226
- var _this$onRetry;
227
- (_this$onRetry = this.onRetry) === null || _this$onRetry === void 0 || _this$onRetry.call(this, Number.parseInt(parsed.value, 10));
228
- }
229
- break;
230
- default: break;
231
- }
232
- }
233
- parseField(line) {
234
- const colon = line.indexOf(":");
235
- if (colon === -1) return {
236
- field: line,
237
- value: ""
238
- };
239
- return {
240
- field: line.slice(0, colon),
241
- value: this.extractValue(line, colon + 1)
242
- };
243
- }
244
- /** Returns the value portion of a line, stripping a single leading space per spec. */
245
- extractValue(line, from) {
246
- return line[from] === " " ? line.slice(from + 1) : line.slice(from);
247
- }
248
- };
249
- //#endregion
250
- //#region ../eventsource/src/EventSourceClient.ts
251
- var EventSourceClient = class {
252
- constructor(options) {
253
- var _options$method, _options$fetch;
254
- _defineProperty(this, "DEFAULT_SHOULD_RECONNECT", () => true);
255
- _defineProperty(this, "DEFAULT_CALCULATE_RECONNECT_DELAY", () => this.serverReconnectionTime);
256
- _defineProperty(this, "url", void 0);
257
- _defineProperty(this, "headers", void 0);
258
- _defineProperty(this, "body", void 0);
259
- _defineProperty(this, "requestOptions", void 0);
260
- _defineProperty(this, "lastEventId", void 0);
261
- _defineProperty(this, "reconnectAttempt", 0);
262
- _defineProperty(this, "abortController", new AbortController());
263
- _defineProperty(this, "reconnectTimeout", void 0);
264
- _defineProperty(this, "_readyState", ReadyState.CLOSED);
265
- _defineProperty(this, "serverReconnectionTime", 2e3);
266
- _defineProperty(this, "_fetch", void 0);
267
- _defineProperty(this, "_onError", void 0);
268
- _defineProperty(this, "_onMessage", void 0);
269
- _defineProperty(this, "_onComment", void 0);
270
- _defineProperty(this, "_onConnect", void 0);
271
- _defineProperty(this, "_onDisconnect", void 0);
272
- _defineProperty(this, "_shouldReconnect", void 0);
273
- _defineProperty(this, "_calculateReconnectDelay", void 0);
274
- this.url = options.url.toString();
275
- this.lastEventId = options.lastEventId;
276
- this.headers = {
277
- Accept: "text/event-stream",
278
- ...options.headers
279
- };
280
- const body = options.body;
281
- this.body = typeof body === "function" ? body : () => body;
282
- this.requestOptions = {
283
- method: (_options$method = options.method) !== null && _options$method !== void 0 ? _options$method : "GET",
284
- mode: options.mode,
285
- credentials: options.credentials,
286
- redirect: options.redirect,
287
- referrer: options.referrer,
288
- referrerPolicy: options.referrerPolicy
289
- };
290
- this._fetch = (_options$fetch = options.fetch) !== null && _options$fetch !== void 0 ? _options$fetch : ((url, init) => fetch(url, init));
291
- this._onError = this.resolveCallback(options.onError, () => {}, "onError");
292
- this._onConnect = this.resolveCallback(options.onConnect, () => {}, "onConnect");
293
- this._onDisconnect = this.resolveCallback(options.onDisconnect, () => {}, "onDisconnect");
294
- this._onMessage = this.resolveCallback(options.onMessage, () => {}, "onMessage");
295
- this._onComment = this.resolveCallback(options.onComment, () => {}, "onComment");
296
- this._shouldReconnect = this.resolveCallback(options.shouldReconnect, this.DEFAULT_SHOULD_RECONNECT, "shouldReconnect");
297
- this._calculateReconnectDelay = this.resolveCallback(options.calculateReconnectDelay, this.DEFAULT_CALCULATE_RECONNECT_DELAY, "calculateReconnectDelay");
298
- }
299
- set onError(value) {
300
- this._onError = this.resolveCallback(value, () => {}, "onError");
301
- }
302
- set onMessage(value) {
303
- this._onMessage = this.resolveCallback(value, () => {}, "onMessage");
304
- }
305
- set onComment(value) {
306
- this._onComment = this.resolveCallback(value, () => {}, "onComment");
307
- }
308
- set onConnect(value) {
309
- this._onConnect = this.resolveCallback(value, () => {}, "onConnect");
310
- }
311
- set onDisconnect(value) {
312
- this._onDisconnect = this.resolveCallback(value, () => {}, "onDisconnect");
313
- }
314
- set shouldReconnect(value) {
315
- this._shouldReconnect = this.resolveCallback(value, this.DEFAULT_SHOULD_RECONNECT, "shouldReconnect");
316
- }
317
- set calculateReconnectDelay(value) {
318
- this._calculateReconnectDelay = this.resolveCallback(value, this.DEFAULT_CALCULATE_RECONNECT_DELAY, "shouldReconnect");
319
- }
320
- get readyState() {
321
- return this._readyState;
322
- }
323
- connect() {
324
- if (this._readyState !== ReadyState.CLOSED) return;
325
- this.reconnectAttempt = 0;
326
- this.initiateConnection();
327
- }
328
- close() {
329
- clearTimeout(this.reconnectTimeout);
330
- this._readyState = ReadyState.CLOSED;
331
- this.abortController.abort();
332
- }
333
- initiateConnection() {
334
- this.abortController = new AbortController();
335
- this._readyState = ReadyState.CONNECTING;
336
- this._fetch(this.url, {
337
- ...this.requestOptions,
338
- body: this.body(),
339
- headers: this.buildRequestHeaders(),
340
- signal: this.abortController.signal,
341
- cache: "no-store"
342
- }).then((response) => this.handleFetchResponse(response)).catch((error) => {
343
- if (error.name === "AbortError" || this.abortController.signal.aborted) {
344
- this.close();
345
- return;
346
- }
347
- this._onError(error);
348
- this.scheduleReconnect({ error });
349
- });
350
- }
351
- async handleFetchResponse(response) {
352
- const { body, status } = response;
353
- if (status === 204) {
354
- this.disconnected();
355
- return;
356
- }
357
- if (status >= 400) {
358
- this.scheduleReconnect({ status });
359
- return;
360
- }
361
- if (!body) throw new MissingResponseBodyError("The server response did not have a body");
362
- this._readyState = ReadyState.OPEN;
363
- this._onConnect();
364
- this.reconnectAttempt = 0;
365
- const parser = new EventSourceParser({
366
- onEvent: (event) => {
367
- if (typeof event.id === "string") this.lastEventId = event.id;
368
- this._onMessage(event);
369
- },
370
- onComment: (comment) => this._onComment(comment),
371
- onRetry: (retryDelay) => this.serverReconnectionTime = retryDelay
372
- });
373
- await this.readResponseStream(body, parser);
374
- this.scheduleReconnect({
375
- status,
376
- error: new StreamClosedError("The server response stream was closed")
377
- });
378
- }
379
- async readResponseStream(body, parser) {
380
- const reader = body.getReader();
381
- const decoder = new TextDecoder();
382
- let done = false;
383
- while (!done) {
384
- const result = await reader.read();
385
- if (result.value) parser.parse(decoder.decode(result.value, { stream: true }));
386
- done = result.done;
387
- }
388
- parser.finish();
389
- }
390
- disconnected() {
391
- clearTimeout(this.reconnectTimeout);
392
- this._readyState = ReadyState.CLOSED;
393
- this._onDisconnect();
394
- }
395
- buildRequestHeaders() {
396
- const lastEventIdHeader = this.lastEventId ? { "Last-Event-ID": this.lastEventId } : void 0;
397
- return {
398
- ...this.headers,
399
- ...lastEventIdHeader
400
- };
401
- }
402
- scheduleReconnect(state) {
403
- if (this.abortController.signal.aborted) return;
404
- clearTimeout(this.reconnectTimeout);
405
- this.reconnectAttempt += 1;
406
- const reconnectionState = {
407
- attempt: this.reconnectAttempt,
408
- status: state.status,
409
- error: state.error,
410
- serverReconnectionTime: this.serverReconnectionTime
411
- };
412
- if (!this._shouldReconnect(reconnectionState)) {
413
- this.disconnected();
414
- return;
415
- }
416
- this._readyState = ReadyState.CONNECTING;
417
- let delay = this._calculateReconnectDelay(reconnectionState);
418
- if (!Number.isFinite(delay) || delay < 1 || delay > 1e3 * 60 * 60) {
419
- this._onError(new ValueOutOfRangeError(`The calculated reconnect delay is out of range: ${delay}. Defaulting to ${this.serverReconnectionTime}`));
420
- delay = this.serverReconnectionTime;
421
- }
422
- this.reconnectTimeout = setTimeout(() => this.initiateConnection(), delay);
423
- }
424
- resolveCallback(provided, fallback, optionName) {
425
- if (provided == null) return fallback;
426
- if (typeof provided !== "function") throw new InvalidOptionError(`${optionName} must be a function`);
427
- return provided;
428
- }
429
- };
430
- //#endregion
431
- //#region ../eventsource/src/index.ts
432
- const createEventSourceClient = (options) => {
433
- return new EventSourceClient(options);
434
- };
435
- //#endregion
436
- //#region ../js-client-core/src/StreamingTransport.ts
437
- var StreamingTransport = class {
438
- constructor(options) {
439
- this.options = options;
440
- _defineProperty(this, "logger", void 0);
441
- _defineProperty(this, "eventSource", void 0);
442
- _defineProperty(this, "eventEmitter", new Emitter());
443
- _defineProperty(this, "url", void 0);
444
- _defineProperty(this, "timeoutTimer", void 0);
445
- this.options = options;
446
- this.logger = options.logger;
447
- this.url = options.resolveUrl("client/sse/v1", options.baseUrl);
448
- }
449
- async connect(context, timeout) {
450
- if (this.eventSource) this.close();
451
- const eventSourcePromise = new Promise((resolve, reject) => {
452
- this.eventSource = createEventSourceClient({
453
- url: this.url,
454
- method: "POST",
455
- headers: { "Content-Type": "application/json" },
456
- fetch: this.options.fetch,
457
- body: JSON.stringify({
458
- givenContext: context,
459
- metaContext: this.options.metaContext,
460
- clientSdkKey: this.options.clientSdkKey,
461
- instanceId: this.options.instanceId
462
- }),
463
- onMessage: ({ data }) => {
464
- this.dispatchMessage(data);
465
- },
466
- onConnect: () => {
467
- this.logger.debug("[StreamingTransport] Connected");
468
- resolve(this);
469
- },
470
- shouldReconnect: (state) => {
471
- const reconnect = !this.isStatusFatal(state.status);
472
- if (!reconnect) {
473
- const error = this.prepareFatalError(state.status, state.error);
474
- this.eventEmitter.emit("connectionError", error);
475
- reject(error);
476
- }
477
- return reconnect;
478
- },
479
- calculateReconnectDelay: (state) => {
480
- const delay = this.options.connectionRetryDelay(state.attempt);
481
- const logMessage = `[StreamingTransport] Scheduling reconnect attempt #${state.attempt} in ${delay}ms.`;
482
- if (state.attempt <= 5) this.logger.info(logMessage);
483
- else this.logger.warn(logMessage);
484
- return delay;
485
- },
486
- onError: (error) => {
487
- this.logger.debug("[StreamingTransport] Error: ", error);
488
- },
489
- onDisconnect: () => {
490
- this.logger.debug("[StreamingTransport] Disconnected");
491
- }
492
- });
493
- this.eventSource.connect();
494
- });
495
- clearTimeout(this.timeoutTimer);
496
- this.timeoutTimer = void 0;
497
- return Promise.race([eventSourcePromise, new Promise((resolve) => {
498
- this.timeoutTimer = setTimeout(() => resolve(this), timeout);
499
- })]).finally(() => clearTimeout(this.timeoutTimer));
500
- }
501
- dispatchMessage(data) {
502
- try {
503
- const json = JSON.parse(data);
504
- this.eventEmitter.emit("configSetReceived", json);
505
- } catch (error) {
506
- this.logger.error("[StreamingTransport] Error parsing and dispatching config data update: ", error);
507
- }
508
- }
509
- prepareFatalError(responseStatus, error) {
510
- const status = responseStatus !== null && responseStatus !== void 0 ? responseStatus : 0;
511
- return new ConfigDirectorConnectionError(`${`Connection failed with status: ${responseStatus !== null && responseStatus !== void 0 ? responseStatus : "unknown"}.`}${error ? ` Error: ${error}` : ""}. This is an unrecoverable error, will not attempt to reconnect.`, status);
512
- }
513
- isStatusFatal(status) {
514
- return !!status && status >= 400 && status < 500 && status != 429;
515
- }
516
- on(name, handler) {
517
- this.eventEmitter.on(name, handler);
518
- }
519
- off(name, handler) {
520
- this.eventEmitter.off(name, handler);
521
- }
522
- clear() {
523
- this.eventEmitter.clear();
524
- }
525
- close() {
526
- var _this$eventSource;
527
- (_this$eventSource = this.eventSource) === null || _this$eventSource === void 0 || _this$eventSource.close();
528
- clearTimeout(this.timeoutTimer);
529
- }
530
- dispose() {
531
- this.close();
532
- this.clear();
533
- }
534
- };
535
- //#endregion
536
- //#region ../shared/src/value-parser.ts
537
- const getRequestedType = (defaultValue) => {
538
- const baseType = typeof defaultValue;
539
- if (baseType === "object") try {
540
- var _constructor$name, _constructor;
541
- return (_constructor$name = (_constructor = defaultValue.constructor) === null || _constructor === void 0 ? void 0 : _constructor.name) !== null && _constructor$name !== void 0 ? _constructor$name : baseType;
542
- } catch {
543
- return baseType;
544
- }
545
- else if (baseType === "function") try {
546
- const functionName = defaultValue.name;
547
- return functionName ? `function: ${functionName}` : baseType;
548
- } catch {
549
- return baseType;
550
- }
551
- return baseType;
552
- };
553
- const isNumericNativeType = (requestedType) => {
554
- return requestedType === "number" || requestedType === "bigint";
555
- };
556
- const configTypesNotReadableAsString = new Set([
557
- "boolean",
558
- "integer",
559
- "float"
560
- ]);
561
- const parseConfigValue = (configState, defaultValue) => {
562
- const value = configState.value;
563
- const requestedType = getRequestedType(defaultValue);
564
- if (value == null) return {
565
- parsedValue: defaultValue,
566
- parsedValueId: void 0,
567
- requestedType,
568
- usedDefault: true,
569
- reason: "value-missing"
570
- };
571
- if (configState.type === "json") return parseJson(value, defaultValue, requestedType, configState.valueId);
572
- if (typeof defaultValue === "string") {
573
- if (configTypesNotReadableAsString.has(configState.type)) return {
574
- parsedValue: defaultValue,
575
- parsedValueId: void 0,
576
- requestedType,
577
- usedDefault: true,
578
- reason: "type-mismatch"
579
- };
580
- return {
581
- parsedValue: value,
582
- parsedValueId: configState.valueId,
583
- requestedType,
584
- usedDefault: false,
585
- reason: "found-match"
586
- };
587
- }
588
- if (typeof defaultValue === "boolean" && (configState.type === "boolean" || configState.type === "string" || configState.type === "enum")) {
589
- const boolValue = parseConfigBoolean(value);
590
- const hasBoolean = typeof boolValue === "boolean";
591
- return {
592
- parsedValue: hasBoolean ? boolValue : defaultValue,
593
- parsedValueId: hasBoolean ? configState.valueId : void 0,
594
- requestedType,
595
- usedDefault: !hasBoolean,
596
- reason: hasBoolean ? "found-match" : "invalid-boolean"
597
- };
598
- }
599
- if (isNumericNativeType(typeof defaultValue) && configState.type === "integer") {
600
- const numValue = parseConfigInteger(value);
601
- const hasNumber = typeof numValue === "number";
602
- return {
603
- parsedValue: hasNumber ? numValue : defaultValue,
604
- parsedValueId: hasNumber ? configState.valueId : void 0,
605
- requestedType,
606
- usedDefault: !hasNumber,
607
- reason: hasNumber ? "found-match" : "invalid-number"
608
- };
609
- }
610
- if (isNumericNativeType(typeof defaultValue) && (configState.type === "float" || configState.type === "enum" || configState.type === "string")) {
611
- const numValue = parseConfigFloat(value);
612
- const hasNumber = typeof numValue === "number";
613
- return {
614
- parsedValue: hasNumber ? numValue : defaultValue,
615
- parsedValueId: hasNumber ? configState.valueId : void 0,
616
- requestedType,
617
- usedDefault: !hasNumber,
618
- reason: hasNumber ? "found-match" : "invalid-number"
619
- };
620
- }
621
- return {
622
- parsedValue: defaultValue,
623
- parsedValueId: void 0,
624
- requestedType,
625
- usedDefault: true,
626
- reason: "type-mismatch"
627
- };
628
- };
629
- const parseConfigBoolean = (value) => {
630
- if (!value) return;
631
- const lowerValue = value.toLowerCase();
632
- if (lowerValue != "true" && lowerValue != "false") return;
633
- return lowerValue === "true";
634
- };
635
- const parseConfigInteger = (value) => {
636
- const num = parseStrictNumber(value);
637
- return num == null ? void 0 : Math.trunc(num);
638
- };
639
- const parseConfigFloat = (value) => {
640
- return parseStrictNumber(value);
641
- };
642
- const parseStrictNumber = (value) => {
643
- if (!value || !isDecimalCharacters(value)) return;
644
- const num = Number(value);
645
- if (isNaN(num) || !isFinite(num)) return;
646
- return num;
647
- };
648
- const isDecimalCharacters = (value) => {
649
- for (const character of value) if (!(character >= "0" && character <= "9") && !"+-.eE".includes(character)) return false;
650
- return true;
651
- };
652
- const parseJson = (jsonString, defaultValue, requestedType, valueId) => {
653
- if (typeof defaultValue === "string") return {
654
- parsedValue: jsonString,
655
- parsedValueId: valueId,
656
- requestedType,
657
- usedDefault: false,
658
- reason: "found-match"
659
- };
660
- try {
661
- const parsed = JSON.parse(jsonString);
662
- if (!isJsonTypeCompatible(parsed, defaultValue)) return {
663
- parsedValue: defaultValue,
664
- parsedValueId: void 0,
665
- requestedType,
666
- usedDefault: true,
667
- reason: "type-mismatch"
668
- };
669
- return {
670
- parsedValue: parsed,
671
- parsedValueId: valueId,
672
- requestedType,
673
- usedDefault: false,
674
- reason: "found-match"
675
- };
676
- } catch {
677
- return {
678
- parsedValue: defaultValue,
679
- parsedValueId: void 0,
680
- requestedType,
681
- usedDefault: true,
682
- reason: "invalid-json"
683
- };
684
- }
685
- };
686
- const isJsonTypeCompatible = (parsed, defaultValue) => {
687
- if (parsed === null) return false;
688
- const defaultType = typeof defaultValue;
689
- if (defaultType === "bigint") return typeof parsed === "number";
690
- return typeof parsed === defaultType;
691
- };
692
- //#endregion
693
- //#region ../shared/src/logger.ts
694
- const LEVELS = {
695
- off: -1,
696
- error: 0,
697
- warn: 1,
698
- info: 2,
699
- debug: 3
700
- };
701
- const buildDateFormatter = () => {
702
- const baseOptions = {
703
- year: "numeric",
704
- month: "2-digit",
705
- day: "2-digit",
706
- hour: "2-digit",
707
- minute: "2-digit",
708
- second: "2-digit",
709
- timeZoneName: "short"
710
- };
711
- try {
712
- return new Intl.DateTimeFormat(void 0, {
713
- ...baseOptions,
714
- fractionalSecondDigits: 3
715
- });
716
- } catch {
717
- return new Intl.DateTimeFormat(void 0, baseOptions);
718
- }
719
- };
720
- var DefaultConsoleLogger = class {
721
- constructor(level, decorator) {
722
- this.level = level;
723
- this.decorator = decorator;
724
- _defineProperty(this, "dateFormatter", void 0);
725
- this.level = level;
726
- this.decorator = decorator;
727
- this.dateFormatter = buildDateFormatter();
728
- }
729
- debug(message, ...args) {
730
- this.log(console.debug, "debug", message, ...args);
731
- }
732
- info(message, ...args) {
733
- this.log(console.info, "info", message, ...args);
734
- }
735
- warn(message, ...args) {
736
- this.log(console.warn, "warn", message, ...args);
737
- }
738
- error(message, ...args) {
739
- this.log(console.error, "error", message, ...args);
740
- }
741
- log(loggerFunction, level, message, ...args) {
742
- if (LEVELS[this.level] >= LEVELS[level]) {
743
- var _this$decorator;
744
- loggerFunction(`[${this.dateFormatter.format(/* @__PURE__ */ new Date())}] ${(_this$decorator = this.decorator) === null || _this$decorator === void 0 ? void 0 : _this$decorator.decorateMessage(message)}`, ...args);
745
- }
746
- }
747
- };
748
- //#endregion
749
- //#region ../js-client-core/src/logger.ts
750
- var LogMessageDecorator = class {
751
- decorateMessage(message) {
752
- return `[ConfigDirector:js-client-sdk] ${message}`;
753
- }
754
- };
755
- const createDefaultLogger = (level, messageDecorator) => {
756
- return new DefaultConsoleLogger(level !== null && level !== void 0 ? level : "warn", messageDecorator !== null && messageDecorator !== void 0 ? messageDecorator : new LogMessageDecorator());
757
- };
758
- //#endregion
759
- //#region ../shared/src/url.ts
760
- const defaultUrlFactory = (input, base) => new URL(input, base === null || base === void 0 ? void 0 : base.toString());
761
- //#endregion
762
- //#region ../shared/src/constants.ts
763
- const CLIENT_BASE_URL = "https://client-sdk-api.configdirector.com";
764
- //#endregion
765
- //#region ../shared/src/instance-id.ts
766
- const generateInstanceId = () => {
767
- if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") return crypto.randomUUID();
768
- return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (placeholder) => {
769
- const random = Math.random() * 16 | 0;
770
- return (placeholder === "x" ? random : random & 3 | 8).toString(16);
771
- });
772
- };
773
- //#endregion
774
- //#region ../js-client-core/src/instance-id.ts
775
- const STORAGE_KEY = "configdirector-sdk:instance-id";
776
- const RECORD_VERSION = 1;
777
- const MAX_AGE_MS = 360 * 60 * 1e3;
778
- const CHECKSUM_SALT = "configdirector.instanceId.v1";
779
- const UUID_V4_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
780
- const fnv1a = (input, offsetBasis) => {
781
- let hash = offsetBasis;
782
- for (let i = 0; i < input.length; i++) {
783
- hash ^= input.charCodeAt(i);
784
- hash = Math.imul(hash, 16777619);
785
- }
786
- return hash >>> 0;
787
- };
788
- const checksumFor = (id, createdAt) => {
789
- const payload = `${CHECKSUM_SALT}|${id}|${createdAt}`;
790
- const low = fnv1a(payload, 2166136261);
791
- return fnv1a(`${low}|${payload}`, 16777619).toString(16).padStart(8, "0") + low.toString(16).padStart(8, "0");
792
- };
793
- const getLocalStorage = () => {
794
- try {
795
- var _localStorage;
796
- return (_localStorage = globalThis.localStorage) !== null && _localStorage !== void 0 ? _localStorage : void 0;
797
- } catch {
798
- return;
799
- }
800
- };
801
- const isUsableRecord = (record) => {
802
- if (!record || typeof record !== "object") return false;
803
- const { v, id, createdAt, checksum } = record;
804
- if (v !== RECORD_VERSION || typeof id !== "string" || typeof checksum !== "string") return false;
805
- if (typeof createdAt !== "number" || !Number.isInteger(createdAt) || createdAt <= 0) return false;
806
- if (!UUID_V4_PATTERN.test(id)) return false;
807
- return checksum === checksumFor(id, createdAt);
808
- };
809
- const readStoredInstanceId = (storage, logger) => {
810
- let raw;
811
- try {
812
- raw = storage.getItem(STORAGE_KEY);
813
- } catch (error) {
814
- logger === null || logger === void 0 || logger.debug("[ConfigDirectorClient] Could not read the stored instance id, generating a new one", error);
815
- return;
816
- }
817
- if (!raw) return;
818
- let record;
819
- try {
820
- record = JSON.parse(raw);
821
- } catch {
822
- logger === null || logger === void 0 || logger.debug("[ConfigDirectorClient] The stored instance id is not valid JSON, generating a new one");
823
- return;
824
- }
825
- if (!isUsableRecord(record)) {
826
- logger === null || logger === void 0 || logger.debug("[ConfigDirectorClient] The stored instance id failed validation, generating a new one");
827
- return;
828
- }
829
- const age = Date.now() - record.createdAt;
830
- if (age < 0 || age >= MAX_AGE_MS) {
831
- logger === null || logger === void 0 || logger.debug("[ConfigDirectorClient] The stored instance id has expired, generating a new one");
832
- return;
833
- }
834
- return record.id;
835
- };
836
- const storeInstanceId = (storage, id, logger) => {
837
- const createdAt = Date.now();
838
- const record = {
839
- v: RECORD_VERSION,
840
- id,
841
- createdAt,
842
- checksum: checksumFor(id, createdAt)
843
- };
844
- try {
845
- storage.setItem(STORAGE_KEY, JSON.stringify(record));
846
- } catch (error) {
847
- logger === null || logger === void 0 || logger.debug("[ConfigDirectorClient] Could not persist the instance id", error);
848
- }
849
- };
850
- const resolveInstanceId = (logger) => {
851
- const storage = getLocalStorage();
852
- if (!storage) return generateInstanceId();
853
- const storedInstanceId = readStoredInstanceId(storage, logger);
854
- if (storedInstanceId) return storedInstanceId;
855
- const instanceId = generateInstanceId();
856
- storeInstanceId(storage, instanceId, logger);
857
- return instanceId;
858
- };
859
- //#endregion
860
- //#region ../shared/src/transport/AbstractPollingTransport.ts
861
- var AbstractPollingTransport = class {
862
- constructor() {
863
- _defineProperty(this, "fatalError", false);
864
- _defineProperty(this, "pollingInterval", void 0);
865
- _defineProperty(this, "pollingIntervalSeconds", 60);
866
- }
867
- clearPollingInterval() {
868
- clearInterval(this.pollingInterval);
869
- }
870
- schedulePollingInterval(callback) {
871
- clearInterval(this.pollingInterval);
872
- if (this.pollingIntervalSeconds <= 0) return;
873
- this.pollingInterval = setInterval(callback, this.pollingIntervalSeconds * 1e3);
874
- }
875
- async handleNonOkResponse(response) {
876
- if (!response.ok) if (this.isStatusFatal(response.status)) {
877
- this.fatalError = true;
878
- this.close();
879
- throw this.prepareFatalResponseStatusError(response.status, await response.text());
880
- } else throw new ConfigDirectorConnectionError(`Connection failed with status: ${response.status}`, response.status);
881
- }
882
- handleFetchError(fetchError) {
883
- if (this.isFetchErrorFatal(fetchError)) {
884
- this.fatalError = true;
885
- this.close();
886
- throw new ConfigDirectorConnectionError(`Connection failed with fatal error: ${fetchError}. This is an unrecoverable error, retry attempts will be ignored.`);
887
- } else if (fetchError instanceof SyntaxError) throw new ConfigDirectorConnectionError(`Failed to parse the response from the server: ${fetchError}`);
888
- else throw new ConfigDirectorConnectionError(`Connection failed with error: ${fetchError}.`);
889
- }
890
- prepareFatalResponseStatusError(responseStatus, errorBody) {
891
- var _errorBody$trim$lengt, _errorBody$trim;
892
- const status = responseStatus !== null && responseStatus !== void 0 ? responseStatus : 0;
893
- return new ConfigDirectorConnectionError(`${`Connection failed with status: ${responseStatus !== null && responseStatus !== void 0 ? responseStatus : "unknown"}`}${((_errorBody$trim$lengt = errorBody === null || errorBody === void 0 || (_errorBody$trim = errorBody.trim()) === null || _errorBody$trim === void 0 ? void 0 : _errorBody$trim.length) !== null && _errorBody$trim$lengt !== void 0 ? _errorBody$trim$lengt : 0) > 0 ? ` (${errorBody})` : ""}. This is an unrecoverable error, retry attempts will be ignored.`, status);
894
- }
895
- isStatusFatal(status) {
896
- return !!status && status >= 400 && status < 500 && status != 429;
897
- }
898
- isFetchErrorFatal(fetchError) {
899
- return (fetchError === null || fetchError === void 0 ? void 0 : fetchError.name) === "NotAllowedError";
900
- }
901
- get isConnected() {
902
- return this.pollingInterval !== void 0;
903
- }
904
- close() {
905
- clearInterval(this.pollingInterval);
906
- this.pollingInterval = void 0;
907
- }
908
- dispose() {
909
- this.close();
910
- this.clear();
911
- }
912
- };
913
- //#endregion
914
- //#region ../shared/src/fetchWithTimeout.ts
915
- const fetchWithTimeout = async (timeout, resource, options, logger) => {
916
- const abortController = new AbortController();
917
- const abortTimeoutId = setTimeout(() => {
918
- logger.debug("[fetchWithTimeout] Reached timeout, aborting request");
919
- abortController.abort();
920
- }, timeout);
921
- try {
922
- const response = await fetch(typeof resource === "string" || resource instanceof Request ? resource : resource.toString(), {
923
- ...options,
924
- signal: abortController.signal
925
- });
926
- clearTimeout(abortTimeoutId);
927
- return response;
928
- } catch (error) {
929
- logger.warn("[fetchWithTimeout] Fetch error: ", error);
930
- clearTimeout(abortTimeoutId);
931
- throw error;
932
- }
933
- };
934
- //#endregion
935
- //#region ../js-client-core/src/PollingTransport.ts
936
- var PollingTransport = class extends AbstractPollingTransport {
937
- constructor(options) {
938
- var _options$pollingInter;
939
- super();
940
- this.options = options;
941
- _defineProperty(this, "logger", void 0);
942
- _defineProperty(this, "eventEmitter", new Emitter());
943
- _defineProperty(this, "url", void 0);
944
- _defineProperty(this, "lastUpdateTimestamp", void 0);
945
- this.options = options;
946
- this.logger = options.logger;
947
- this.url = options.resolveUrl("client/polling/v1", options.baseUrl);
948
- const requestedInterval = (_options$pollingInter = options.pollingInterval) !== null && _options$pollingInter !== void 0 ? _options$pollingInter : 300;
949
- if (requestedInterval < 60) this.logger.warn(`[PollingTransport] pollingInterval of ${requestedInterval} seconds is below the minimum of 60 seconds. Using 60 seconds.`);
950
- this.pollingIntervalSeconds = Math.max(requestedInterval, 60);
951
- }
952
- async connect(context, timeout) {
953
- this.clearPollingInterval();
954
- try {
955
- await this.fetchConfigs(context, timeout);
956
- } finally {
957
- if (!this.fatalError) this.schedulePollingInterval(() => {
958
- this.fetchConfigs(context, timeout).catch((error) => {
959
- this.logger.error("[PollingTransport] Error during polling:", error);
960
- });
961
- });
962
- }
963
- return this;
964
- }
965
- async fetchConfigs(context, timeout) {
966
- if (this.fatalError) {
967
- this.logger.warn("[PollingTransport] There was a prior unrecoverable error. Ignoring attempt to reconnect.");
968
- return;
969
- }
970
- try {
971
- const response = await fetchWithTimeout(timeout, this.url, {
972
- method: "POST",
973
- headers: { "Content-Type": "application/json" },
974
- body: JSON.stringify({
975
- givenContext: context,
976
- metaContext: this.options.metaContext,
977
- clientSdkKey: this.options.clientSdkKey,
978
- instanceId: this.options.instanceId,
979
- lastUpdateTimestamp: this.lastUpdateTimestamp
980
- })
981
- }, this.logger);
982
- await this.handleNonOkResponse(response);
983
- if (response.status == 200) {
984
- const json = JSON.parse(await response.text());
985
- this.lastUpdateTimestamp = json.timestamp;
986
- this.eventEmitter.emit("configSetReceived", json);
987
- }
988
- } catch (fetchError) {
989
- try {
990
- this.handleFetchError(fetchError);
991
- } catch (handledError) {
992
- if (this.fatalError) this.eventEmitter.emit("connectionError", handledError);
993
- throw handledError;
994
- }
995
- }
996
- }
997
- on(name, handler) {
998
- this.eventEmitter.on(name, handler);
999
- }
1000
- off(name, handler) {
1001
- this.eventEmitter.off(name, handler);
1002
- }
1003
- clear() {
1004
- this.eventEmitter.clear();
1005
- }
1006
- };
1007
- //#endregion
1008
- //#region ../js-client-core/src/browser-globals.ts
1009
- const read = (readValue) => {
1010
- try {
1011
- return readValue(globalThis);
1012
- } catch {
1013
- return;
1014
- }
1015
- };
1016
- const readUserAgent = () => read((globals) => {
1017
- var _globals$navigator;
1018
- return (_globals$navigator = globals.navigator) === null || _globals$navigator === void 0 ? void 0 : _globals$navigator.userAgent;
1019
- });
1020
- const readHost = () => read((globals) => {
1021
- var _globals$location;
1022
- return (_globals$location = globals.location) === null || _globals$location === void 0 ? void 0 : _globals$location.host;
1023
- });
1024
- //#endregion
1025
- //#region ../js-client-core/src/DefaultConfigDirectorClient.ts
1026
- const MAX_EXPONENTIAL_DELAY = 9;
1027
- var DefaultConfigDirectorClient = class {
1028
- constructor(telemetryClient, clientSdkKey, sdkOptions, clientOptions, internalClientOptions) {
1029
- var _clientOptions$logger, _clientOptions$connec, _clientOptions$connec2, _internalClientOption, _this$parseUrl, _clientOptions$connec3, _clientOptions$connec4, _clientOptions$connec5, _internalClientOption2, _clientOptions$connec6, _clientOptions$hooks, _clientOptions$hooks2, _clientOptions$hooks3, _clientOptions$hooks4, _clientOptions$hooks5;
1030
- _defineProperty(this, "logger", void 0);
1031
- _defineProperty(this, "telemetryClient", void 0);
1032
- _defineProperty(this, "configSet", void 0);
1033
- _defineProperty(this, "handlersMap", /* @__PURE__ */ new Map());
1034
- _defineProperty(this, "transport", void 0);
1035
- _defineProperty(this, "eventEmitter", new Emitter());
1036
- _defineProperty(this, "timeout", void 0);
1037
- _defineProperty(this, "timeoutTimer", void 0);
1038
- _defineProperty(this, "ready", false);
1039
- _defineProperty(this, "initializing", false);
1040
- _defineProperty(this, "readyPromise", void 0);
1041
- _defineProperty(this, "readyResolve", void 0);
1042
- _defineProperty(this, "currentContext", void 0);
1043
- _defineProperty(this, "connectionMode", void 0);
1044
- _defineProperty(this, "instanceId", void 0);
1045
- this.logger = (_clientOptions$logger = clientOptions === null || clientOptions === void 0 ? void 0 : clientOptions.logger) !== null && _clientOptions$logger !== void 0 ? _clientOptions$logger : createDefaultLogger();
1046
- this.instanceId = resolveInstanceId(this.logger);
1047
- this.timeout = (_clientOptions$connec = clientOptions === null || clientOptions === void 0 || (_clientOptions$connec2 = clientOptions.connection) === null || _clientOptions$connec2 === void 0 ? void 0 : _clientOptions$connec2.timeout) !== null && _clientOptions$connec !== void 0 ? _clientOptions$connec : 3e3;
1048
- this.validateSdkKeyPresence(clientSdkKey);
1049
- const urlFactory = (_internalClientOption = internalClientOptions === null || internalClientOptions === void 0 ? void 0 : internalClientOptions.urlFactory) !== null && _internalClientOption !== void 0 ? _internalClientOption : defaultUrlFactory;
1050
- const baseUrl = (_this$parseUrl = this.parseUrl(clientOptions === null || clientOptions === void 0 || (_clientOptions$connec3 = clientOptions.connection) === null || _clientOptions$connec3 === void 0 ? void 0 : _clientOptions$connec3.url, urlFactory)) !== null && _this$parseUrl !== void 0 ? _this$parseUrl : CLIENT_BASE_URL;
1051
- this.connectionMode = (_clientOptions$connec4 = clientOptions === null || clientOptions === void 0 || (_clientOptions$connec5 = clientOptions.connection) === null || _clientOptions$connec5 === void 0 ? void 0 : _clientOptions$connec5.mode) !== null && _clientOptions$connec4 !== void 0 ? _clientOptions$connec4 : "streaming";
1052
- const transportConstructor = this.getTransportConstructor(this.connectionMode);
1053
- this.telemetryClient = telemetryClient;
1054
- this.transport = new transportConstructor({
1055
- instanceId: this.instanceId,
1056
- clientSdkKey,
1057
- baseUrl,
1058
- resolveUrl: urlFactory,
1059
- metaContext: {
1060
- ...clientOptions === null || clientOptions === void 0 ? void 0 : clientOptions.metadata,
1061
- sdkName: sdkOptions.sdkName,
1062
- sdkVersion: sdkOptions.sdkVersion,
1063
- userAgent: readUserAgent(),
1064
- host: readHost()
1065
- },
1066
- logger: this.logger,
1067
- fetch: internalClientOptions === null || internalClientOptions === void 0 ? void 0 : internalClientOptions.fetch,
1068
- connectionRetryDelay: (_internalClientOption2 = internalClientOptions === null || internalClientOptions === void 0 ? void 0 : internalClientOptions.connectionRetryDelay) !== null && _internalClientOption2 !== void 0 ? _internalClientOption2 : ((attempt) => {
1069
- return Math.pow(2, Math.min(attempt, MAX_EXPONENTIAL_DELAY)) * 1e3;
1070
- }),
1071
- pollingInterval: clientOptions === null || clientOptions === void 0 || (_clientOptions$connec6 = clientOptions.connection) === null || _clientOptions$connec6 === void 0 ? void 0 : _clientOptions$connec6.pollingInterval
1072
- });
1073
- this.transport.on("configSetReceived", (configSet) => {
1074
- var _this$readyResolve;
1075
- (_this$readyResolve = this.readyResolve) === null || _this$readyResolve === void 0 || _this$readyResolve.call(this);
1076
- const configKeys = Object.keys(configSet.configs);
1077
- if (!this.configSet || configSet.kind == "full") {
1078
- this.configSet = configSet;
1079
- this.emit("configsUpdated", { keys: configKeys });
1080
- this.updateWatchers(configSet.configs);
1081
- } else {
1082
- this.configSet.configs = {
1083
- ...this.configSet.configs,
1084
- ...configSet.configs
1085
- };
1086
- this.emit("configsUpdated", { keys: configKeys });
1087
- this.updateWatchers(configSet.configs);
1088
- }
1089
- this.logger.debug("[ConfigDirectorClient] ConfigSet updated from server:", { keys: configKeys });
1090
- });
1091
- this.transport.on("connectionError", (error) => {
1092
- this.logger.error("[ConfigDirectorClient] The connection encountered an unrecoverable error and will not be retried. Configs will no longer receive updates.", error);
1093
- this.emit("connectionError", { error });
1094
- });
1095
- this.registerHandler("clientReady", clientOptions === null || clientOptions === void 0 || (_clientOptions$hooks = clientOptions.hooks) === null || _clientOptions$hooks === void 0 ? void 0 : _clientOptions$hooks.clientReady);
1096
- this.registerHandler("configEvaluated", clientOptions === null || clientOptions === void 0 || (_clientOptions$hooks2 = clientOptions.hooks) === null || _clientOptions$hooks2 === void 0 ? void 0 : _clientOptions$hooks2.configEvaluated);
1097
- this.registerHandler("configsUpdated", clientOptions === null || clientOptions === void 0 || (_clientOptions$hooks3 = clientOptions.hooks) === null || _clientOptions$hooks3 === void 0 ? void 0 : _clientOptions$hooks3.configsUpdated);
1098
- this.registerHandler("contextUpdated", clientOptions === null || clientOptions === void 0 || (_clientOptions$hooks4 = clientOptions.hooks) === null || _clientOptions$hooks4 === void 0 ? void 0 : _clientOptions$hooks4.contextUpdated);
1099
- this.registerHandler("connectionError", clientOptions === null || clientOptions === void 0 || (_clientOptions$hooks5 = clientOptions.hooks) === null || _clientOptions$hooks5 === void 0 ? void 0 : _clientOptions$hooks5.connectionError);
1100
- }
1101
- registerHandler(event, handler) {
1102
- if (!handler) return;
1103
- const handlers = Array.isArray(handler) ? handler : [handler];
1104
- for (const h of handlers) if (typeof h === "function") this.on(event, h);
1105
- }
1106
- getTransportConstructor(connectionMode) {
1107
- switch (connectionMode) {
1108
- case "polling": return PollingTransport;
1109
- default: return StreamingTransport;
1110
- }
1111
- }
1112
- async initialize(context) {
1113
- this.initializing = true;
1114
- await this.connectToTransport(context, "initialization");
1115
- }
1116
- async updateContext(context) {
1117
- await this.connectToTransport(context, "context update");
1118
- }
1119
- get context() {
1120
- return this.currentContext;
1121
- }
1122
- async connectToTransport(context, caller) {
1123
- try {
1124
- this.ready = false;
1125
- this.readyPromise = new Promise((resolve) => {
1126
- this.readyResolve = resolve;
1127
- }).then((cancelled) => {
1128
- if (cancelled) return;
1129
- this.ready = true;
1130
- this.initializing = false;
1131
- this.emit("clientReady", { action: caller });
1132
- this.logger.debug("[ConfigDirectorClient] Received initial payload from the server, client is ready");
1133
- });
1134
- const startTime = (/* @__PURE__ */ new Date()).getTime();
1135
- await this.transport.connect(context !== null && context !== void 0 ? context : {}, this.timeout);
1136
- this.currentContext = context;
1137
- this.telemetryClient.updateContext(context);
1138
- this.emit("contextUpdated", { context });
1139
- const elapsedTime = (/* @__PURE__ */ new Date()).getTime() - startTime;
1140
- const remainingTimeout = this.timeout - elapsedTime;
1141
- clearTimeout(this.timeoutTimer);
1142
- this.timeoutTimer = void 0;
1143
- if (remainingTimeout > 0) await Promise.race([this.readyPromise, new Promise((resolve) => {
1144
- this.timeoutTimer = setTimeout(() => resolve(), remainingTimeout);
1145
- })]).finally(() => clearTimeout(this.timeoutTimer));
1146
- if (!this.ready) this.logger.warn(`[ConfigDirectorClient] Timed out waiting for ${caller} after ${this.timeout}ms. The client will continue to retry since there were no fatal errors detected. Configs will return the default value until the connection succeeds.`);
1147
- } catch (error) {
1148
- this.logger.error(`[ConfigDirectorClient] An error occurred during ${caller}: `, error);
1149
- }
1150
- }
1151
- updateWatchers(configsMap) {
1152
- Object.values(configsMap).forEach((v) => this.updateWatchersForConfig(v));
1153
- }
1154
- updateWatchersForConfig(configState) {
1155
- var _this$handlersMap$get;
1156
- (_this$handlersMap$get = this.handlersMap.get(configState.key)) === null || _this$handlersMap$get === void 0 || _this$handlersMap$get.forEach((h) => {
1157
- const value = this.getValueFromConfigState(configState.key, configState, h.defaultValue);
1158
- h.handler(value);
1159
- });
1160
- }
1161
- watch(configKey, defaultValue, callback) {
1162
- this.validateDefaultValue(defaultValue);
1163
- const handlers = this.handlersMap.get(configKey);
1164
- const handlerWithOptions = {
1165
- handler: callback,
1166
- defaultValue,
1167
- requestedType: typeof defaultValue
1168
- };
1169
- if (handlers) handlers.push(handlerWithOptions);
1170
- else this.handlersMap.set(configKey, [handlerWithOptions]);
1171
- return () => this.unwatch(configKey, callback);
1172
- }
1173
- unwatch(configKey, callback) {
1174
- const handlers = this.handlersMap.get(configKey);
1175
- if (!handlers) return;
1176
- if (callback) {
1177
- const index = handlers.findIndex((h) => h.handler == callback);
1178
- if (index >= 0) handlers === null || handlers === void 0 || handlers.splice(index, 1);
1179
- } else handlers.splice(0);
1180
- }
1181
- getValue(configKey, defaultValue) {
1182
- var _this$configSet;
1183
- this.validateDefaultValue(defaultValue);
1184
- const configState = (_this$configSet = this.configSet) === null || _this$configSet === void 0 ? void 0 : _this$configSet.configs[configKey];
1185
- return this.getValueFromConfigState(configKey, configState, defaultValue);
1186
- }
1187
- getValueFromConfigState(configKey, configState, defaultValue) {
1188
- var _this$currentContext2, _parseResult$parsedVa;
1189
- if (!configState) {
1190
- var _this$currentContext;
1191
- this.logger.debug(`[ConfigDirectorClient] No config state found for '${configKey}', returning default value '${defaultValue}'`);
1192
- const reason = this.isReady ? "config-state-missing" : "client-not-ready";
1193
- this.telemetryClient.evaluatedConfig({
1194
- contextId: (_this$currentContext = this.currentContext) === null || _this$currentContext === void 0 ? void 0 : _this$currentContext.id,
1195
- key: configKey,
1196
- defaultValue,
1197
- requestedType: getRequestedType(defaultValue),
1198
- evaluatedValue: defaultValue,
1199
- usedDefault: true,
1200
- evaluationReason: reason
1201
- });
1202
- this.dispatchEvaluationEvent({
1203
- key: configKey,
1204
- value: defaultValue,
1205
- isDefaultValue: true,
1206
- reason,
1207
- context: this.context
1208
- });
1209
- return defaultValue;
1210
- }
1211
- const parseResult = parseConfigValue(configState, defaultValue);
1212
- this.telemetryClient.evaluatedConfig({
1213
- contextId: (_this$currentContext2 = this.currentContext) === null || _this$currentContext2 === void 0 ? void 0 : _this$currentContext2.id,
1214
- type: configState.type,
1215
- key: configKey,
1216
- defaultValue,
1217
- requestedType: parseResult.requestedType,
1218
- evaluatedValue: parseResult.parsedValue,
1219
- evaluatedValueId: parseResult.parsedValueId,
1220
- usedDefault: parseResult.usedDefault,
1221
- evaluationReason: parseResult.reason
1222
- });
1223
- this.dispatchEvaluationEvent({
1224
- key: configKey,
1225
- value: parseResult.parsedValue,
1226
- valueId: (_parseResult$parsedVa = parseResult.parsedValueId) !== null && _parseResult$parsedVa !== void 0 ? _parseResult$parsedVa : void 0,
1227
- isDefaultValue: parseResult.usedDefault,
1228
- reason: parseResult.reason,
1229
- context: this.context
1230
- });
1231
- this.logger.debug(`[ConfigDirectorClient] Evaluated '${configKey}' to '${parseResult.parsedValue}'`);
1232
- return parseResult.parsedValue;
1233
- }
1234
- dispatchEvaluationEvent(event) {
1235
- setTimeout(() => this.emit("configEvaluated", { evaluation: event }), 0);
1236
- }
1237
- emit(name, payload) {
1238
- try {
1239
- this.eventEmitter.emit(name, payload);
1240
- } catch (error) {
1241
- this.logger.error(`[ConfigDirectorClient] Error executing event handlers for '${name}'`, error);
1242
- }
1243
- }
1244
- parseUrl(url, urlFactory) {
1245
- if (!url) return;
1246
- try {
1247
- return urlFactory(url);
1248
- } catch (error) {
1249
- throw new ConfigDirectorValidationError(`Invalid base URL '${url}'. Parsing failed: ${error}`);
1250
- }
1251
- }
1252
- validateSdkKeyPresence(sdkKey) {
1253
- if (!sdkKey || sdkKey.trim().length == 0) throw new ConfigDirectorValidationError("No client SDK key was provided, the client cannot be instantiated without a valid client SDK key");
1254
- }
1255
- validateDefaultValue(defaultValue) {
1256
- if (defaultValue === void 0 || defaultValue === null) throw new ConfigDirectorValidationError("Invalid default value. The default value for a config must be defined and non-null.");
1257
- if (typeof defaultValue === "function") throw new ConfigDirectorValidationError("Invalid default value. The default value for a config cannot be a function.");
1258
- }
1259
- get isReady() {
1260
- return this.ready;
1261
- }
1262
- get isInitializing() {
1263
- return this.initializing;
1264
- }
1265
- on(eventName, handler) {
1266
- this.eventEmitter.on(eventName, handler);
1267
- }
1268
- off(eventName, handler) {
1269
- this.eventEmitter.off(eventName, handler);
1270
- }
1271
- clear() {
1272
- this.logger.debug("[ConfigDirectorClient] clear() has been called, removing all observers");
1273
- this.eventEmitter.clear();
1274
- this.handlersMap.clear();
1275
- }
1276
- unwatchAll() {
1277
- this.handlersMap.clear();
1278
- }
1279
- pauseNetwork() {
1280
- this.logger.debug("[ConfigDirectorClient] pauseNetwork() called, pausing transport connection");
1281
- clearTimeout(this.timeoutTimer);
1282
- this.timeoutTimer = void 0;
1283
- this.transport.close();
1284
- this.ready = false;
1285
- }
1286
- async resumeNetwork() {
1287
- await this.connectToTransport(this.currentContext, "network resume");
1288
- }
1289
- async close() {
1290
- var _this$readyResolve2;
1291
- this.logger.debug("[ConfigDirectorClient] close() has been called, closing connection to server");
1292
- clearTimeout(this.timeoutTimer);
1293
- (_this$readyResolve2 = this.readyResolve) === null || _this$readyResolve2 === void 0 || _this$readyResolve2.call(this, true);
1294
- this.telemetryClient.close();
1295
- this.transport.close();
1296
- this.ready = false;
1297
- this.initializing = false;
1298
- }
1299
- dispose() {
1300
- this.clear();
1301
- this.close();
1302
- }
1303
- };
1304
- //#endregion
1305
6
  //#region ../shared/src/telemetry/utils.ts
1306
7
  const CONFIG_VALUE_MAX_LENGTH = 500;
1307
8
  const compactTelemetryValue = async (tv, valueIdGenerator) => {
@@ -1513,11 +214,17 @@ var TelemetryEventCollector = class {
1513
214
  _defineProperty(this, "flushIntervalDelay", void 0);
1514
215
  _defineProperty(this, "flushTimeout", void 0);
1515
216
  _defineProperty(this, "collectEvents", true);
217
+ _defineProperty(this, "initialFlushIntervalDelay", void 0);
218
+ _defineProperty(this, "started", false);
1516
219
  this.logger = options.logger;
1517
220
  this.evaluationEventQueue = new EventQueue((_options$evaluationQu = options.evaluationQueueLimit) !== null && _options$evaluationQu !== void 0 ? _options$evaluationQu : 1e3);
1518
221
  this.flushIntervalDelay = (_options$flushInterva = options.flushIntervalDelay) !== null && _options$flushInterva !== void 0 ? _options$flushInterva : 3e4;
1519
- const initialDelay = (_options$initialFlush = options.initialFlushIntervalDelay) !== null && _options$initialFlush !== void 0 ? _options$initialFlush : 5e3;
1520
- this.flushTimeout = setTimeout(() => this.flushAndScheduleNext(), initialDelay);
222
+ this.initialFlushIntervalDelay = (_options$initialFlush = options.initialFlushIntervalDelay) !== null && _options$initialFlush !== void 0 ? _options$initialFlush : 5e3;
223
+ }
224
+ start() {
225
+ if (this.started || !this.collectEvents) return;
226
+ this.started = true;
227
+ this.flushTimeout = setTimeout(() => this.flushAndScheduleNext(), this.initialFlushIntervalDelay);
1521
228
  }
1522
229
  async flushAndScheduleNext() {
1523
230
  if (!this.collectEvents) return;
@@ -1627,6 +334,125 @@ var MessageDecorator = class {
1627
334
  };
1628
335
  const createConsoleLogger = (level) => createDefaultLogger(level, new MessageDecorator());
1629
336
  //#endregion
337
+ //#region src/context.tsx
338
+ const createReactContext = () => React.createContext({ status: "loading" });
339
+ const reactContext = createReactContext();
340
+ //#endregion
341
+ //#region ../js-client-core/src/clientHandlers.ts
342
+ var ClientHandlers = class {
343
+ constructor(client) {
344
+ this.client = client;
345
+ _defineProperty(this, "registrations", []);
346
+ }
347
+ on(event, handler) {
348
+ this.client.on(event, handler);
349
+ this.registrations.push({
350
+ event,
351
+ handler
352
+ });
353
+ }
354
+ registerHooks(hooks) {
355
+ for (const [event, handlerOrHandlers] of Object.entries(hooks !== null && hooks !== void 0 ? hooks : {})) {
356
+ const handlers = Array.isArray(handlerOrHandlers) ? handlerOrHandlers : [handlerOrHandlers];
357
+ for (const handler of handlers) if (typeof handler === "function") this.on(event, handler);
358
+ }
359
+ }
360
+ removeAll() {
361
+ for (const { event, handler } of this.registrations) this.client.off(event, handler);
362
+ this.registrations = [];
363
+ }
364
+ };
365
+ //#endregion
366
+ //#region ../shared/src/deepEqual.ts
367
+ const deepEqual = (left, right) => {
368
+ if (Object.is(left, right)) return true;
369
+ if (typeof left !== "object" || typeof right !== "object" || left === null || right === null) return false;
370
+ if (Object.getPrototypeOf(left) !== Object.getPrototypeOf(right)) return false;
371
+ if (left instanceof Date && right instanceof Date) return left.getTime() === right.getTime();
372
+ if (Array.isArray(left) && Array.isArray(right)) return left.length === right.length && left.every((item, index) => deepEqual(item, right[index]));
373
+ if (!isPlainObject(left) || !isPlainObject(right)) return false;
374
+ const leftKeys = Object.keys(left);
375
+ const rightKeys = Object.keys(right);
376
+ return leftKeys.length === rightKeys.length && leftKeys.every((key) => Object.prototype.hasOwnProperty.call(right, key) && deepEqual(left[key], right[key]));
377
+ };
378
+ const isPlainObject = (value) => {
379
+ const prototype = Object.getPrototypeOf(value);
380
+ return prototype === Object.prototype || prototype === null;
381
+ };
382
+ //#endregion
383
+ //#region ../js-client-core/src/mountAction.ts
384
+ const decideMountAction = (client, context) => {
385
+ if (client.isInitializing) return "none";
386
+ if (!client.isReady) return "initialize";
387
+ if (context !== void 0 && !deepEqual(context, client.context)) return "updateContext";
388
+ return "none";
389
+ };
390
+ //#endregion
391
+ //#region src/appInfo.ts
392
+ const APP_INFO_SOURCES = [{
393
+ module: (sources) => {
394
+ var _sources$expoModules;
395
+ return (_sources$expoModules = sources.expoModules) === null || _sources$expoModules === void 0 ? void 0 : _sources$expoModules.ExpoApplication;
396
+ },
397
+ constantNames: {
398
+ appName: "applicationName",
399
+ appVersion: "nativeApplicationVersion"
400
+ }
401
+ }, {
402
+ module: (sources) => {
403
+ var _sources$nativeModule;
404
+ return (_sources$nativeModule = sources.nativeModules) === null || _sources$nativeModule === void 0 ? void 0 : _sources$nativeModule.RNDeviceInfo;
405
+ },
406
+ constantNames: {
407
+ appName: "appName",
408
+ appVersion: "appVersion"
409
+ }
410
+ }];
411
+ const constantsOf = (module) => {
412
+ if (typeof module !== "object" || module === null) return;
413
+ const nativeModule = module;
414
+ try {
415
+ return typeof nativeModule.getConstants === "function" ? nativeModule.getConstants() : nativeModule;
416
+ } catch {
417
+ return;
418
+ }
419
+ };
420
+ const nonBlankString = (value) => typeof value === "string" && value.trim() !== "" ? value : void 0;
421
+ const readAppInfo = (sources) => {
422
+ const reported = APP_INFO_SOURCES.map((source) => ({
423
+ constantNames: source.constantNames,
424
+ constants: constantsOf(source.module(sources))
425
+ }));
426
+ const firstReported = (field) => reported.map(({ constantNames, constants }) => nonBlankString(constants === null || constants === void 0 ? void 0 : constants[constantNames[field]])).find((value) => value !== void 0);
427
+ const appName = firstReported("appName");
428
+ const appVersion = firstReported("appVersion");
429
+ return {
430
+ ...appName !== void 0 && { appName },
431
+ ...appVersion !== void 0 && { appVersion }
432
+ };
433
+ };
434
+ const platformAppInfoSources = () => {
435
+ var _expo;
436
+ return {
437
+ expoModules: (_expo = globalThis.expo) === null || _expo === void 0 ? void 0 : _expo.modules,
438
+ nativeModules: NativeModules
439
+ };
440
+ };
441
+ const resolveMetadata = (provided, logger) => {
442
+ var _provided$appName, _provided$appVersion;
443
+ const detected = (provided === null || provided === void 0 ? void 0 : provided.appName) !== void 0 && (provided === null || provided === void 0 ? void 0 : provided.appVersion) !== void 0 ? {} : readAppInfo(platformAppInfoSources());
444
+ const metadata = {
445
+ appName: (_provided$appName = provided === null || provided === void 0 ? void 0 : provided.appName) !== null && _provided$appName !== void 0 ? _provided$appName : detected.appName,
446
+ appVersion: (_provided$appVersion = provided === null || provided === void 0 ? void 0 : provided.appVersion) !== null && _provided$appVersion !== void 0 ? _provided$appVersion : detected.appVersion
447
+ };
448
+ const missing = [...metadata.appName === void 0 ? ["name"] : [], ...metadata.appVersion === void 0 ? ["version"] : []];
449
+ if (missing.length > 0) {
450
+ const pronoun = missing.length === 1 ? "it" : "them";
451
+ logger.info(`The ConfigDirector SDK could not find an app ${missing.join(" and ")}, so targeting rules that use ${pronoun} will not match. Provide ${pronoun} through the appName and appVersion props of ConfigDirectorProvider or the metadata option of createClient, or install expo-application or react-native-device-info for the SDK to read ${pronoun} from.`);
452
+ }
453
+ return metadata;
454
+ };
455
+ //#endregion
1630
456
  //#region src/utf8Encoder.ts
1631
457
  /**
1632
458
  * Encodes a string to a UTF-8 byte sequence.
@@ -1775,6 +601,7 @@ var ReactNativeTelemetryClient = class {
1775
601
  });
1776
602
  }
1777
603
  async updateContext(value) {
604
+ this.collector.start();
1778
605
  return await this.collector.updateContext(value);
1779
606
  }
1780
607
  evaluatedConfig(event) {
@@ -1816,41 +643,75 @@ const urlFactory = (input, base) => new MinimalUrl(input, base);
1816
643
  //#endregion
1817
644
  //#region src/client.ts
1818
645
  const createClient = (clientSdkKey, clientOptions) => {
1819
- var _clientOptions$logger, _clientOptions$connec, _clientOptions$metada;
646
+ var _clientOptions$logger, _clientOptions$connec;
1820
647
  const sdkIdentity = {
1821
648
  sdkName: "react-native-sdk",
1822
- sdkVersion: "1.5.1"
649
+ sdkVersion: "1.6.0"
1823
650
  };
1824
651
  const logger = (_clientOptions$logger = clientOptions === null || clientOptions === void 0 ? void 0 : clientOptions.logger) !== null && _clientOptions$logger !== void 0 ? _clientOptions$logger : createConsoleLogger("warn");
1825
- const baseUrl = (clientOptions === null || clientOptions === void 0 || (_clientOptions$connec = clientOptions.connection) === null || _clientOptions$connec === void 0 ? void 0 : _clientOptions$connec.url) ? urlFactory(clientOptions.connection.url) : CLIENT_BASE_URL;
652
+ const metadata = resolveMetadata(clientOptions === null || clientOptions === void 0 ? void 0 : clientOptions.metadata, logger);
1826
653
  return new DefaultConfigDirectorClient(new ReactNativeTelemetryClient({
1827
654
  sdkKey: clientSdkKey,
1828
655
  sdkIdentity,
1829
- metaContext: (_clientOptions$metada = clientOptions === null || clientOptions === void 0 ? void 0 : clientOptions.metadata) !== null && _clientOptions$metada !== void 0 ? _clientOptions$metada : {},
1830
- baseUrl,
656
+ metaContext: metadata,
657
+ baseUrl: (clientOptions === null || clientOptions === void 0 || (_clientOptions$connec = clientOptions.connection) === null || _clientOptions$connec === void 0 ? void 0 : _clientOptions$connec.url) ? urlFactory(clientOptions.connection.url) : CLIENT_BASE_URL,
1831
658
  logger,
1832
659
  urlFactory
1833
660
  }), clientSdkKey, sdkIdentity, {
1834
661
  ...clientOptions,
1835
- logger
662
+ logger,
663
+ metadata
1836
664
  }, {
1837
665
  fetch: reactNativeStreamingFetch,
1838
666
  urlFactory
1839
667
  });
1840
668
  };
1841
669
  //#endregion
1842
- //#region src/context.tsx
1843
- const createReactContext = () => React.createContext({ status: "loading" });
1844
- const reactContext = createReactContext();
670
+ //#region src/clientSource.ts
671
+ const buildClient = (options) => {
672
+ var _options$logger;
673
+ return createClient(options.sdkKey, {
674
+ connection: {
675
+ url: options.url,
676
+ timeout: options.timeout,
677
+ mode: options.mode,
678
+ pollingInterval: options.pollingInterval
679
+ },
680
+ metadata: {
681
+ appName: options.appName,
682
+ appVersion: options.appVersion
683
+ },
684
+ logger: (_options$logger = options.logger) !== null && _options$logger !== void 0 ? _options$logger : createConsoleLogger("warn"),
685
+ hooks: options.hooks
686
+ });
687
+ };
688
+ const hasGivenClient = (props) => "client" in props && props.client !== void 0;
689
+ const resolveClientSource = (props) => {
690
+ if (hasGivenClient(props)) return {
691
+ client: props.client,
692
+ owned: false
693
+ };
694
+ const installed = readInstalledTestClient();
695
+ if (installed) return {
696
+ client: installed,
697
+ owned: false
698
+ };
699
+ return {
700
+ client: buildClient(props),
701
+ owned: true
702
+ };
703
+ };
1845
704
  //#endregion
1846
705
  //#region src/provider.tsx
1847
706
  var ConfigDirectorProvider = class extends Component {
1848
707
  constructor(props) {
1849
- var _props$logger;
1850
708
  super(props);
1851
709
  _defineProperty(this, "appStateSubscription", null);
1852
710
  _defineProperty(this, "netInfoUnsubscribe", null);
1853
711
  _defineProperty(this, "wasOffline", false);
712
+ _defineProperty(this, "ownsClient", void 0);
713
+ _defineProperty(this, "disposedOwnClient", false);
714
+ _defineProperty(this, "handlers", void 0);
1854
715
  _defineProperty(this, "handleAppStateChange", async (nextState) => {
1855
716
  if (nextState === "active") await this.reconnect();
1856
717
  else if (nextState === "background") {
@@ -1869,48 +730,70 @@ var ConfigDirectorProvider = class extends Component {
1869
730
  var _this$state$client2;
1870
731
  await ((_this$state$client2 = this.state.client) === null || _this$state$client2 === void 0 ? void 0 : _this$state$client2.resumeNetwork());
1871
732
  });
733
+ const source = resolveClientSource(props);
734
+ this.ownsClient = source.owned;
1872
735
  this.state = {
1873
- client: createClient(props.sdkKey, {
1874
- connection: {
1875
- url: props.url,
1876
- timeout: props.timeout
1877
- },
1878
- metadata: {
1879
- appName: props.appName,
1880
- appVersion: props.appVersion
1881
- },
1882
- logger: (_props$logger = props.logger) !== null && _props$logger !== void 0 ? _props$logger : createConsoleLogger("warn"),
1883
- hooks: props.hooks
1884
- }),
1885
- status: "loading"
736
+ client: source.client,
737
+ status: source.client.isReady ? "ready" : "loading"
1886
738
  };
1887
739
  }
1888
740
  async componentDidMount() {
1889
- var _this$state$client3, _this$state$client4, _this$state$client5, _this$state$client6;
1890
- (_this$state$client3 = this.state.client) === null || _this$state$client3 === void 0 || _this$state$client3.on("configsUpdated", () => {
741
+ let client = this.state.client;
742
+ if (this.ownsClient && this.disposedOwnClient) {
743
+ client = buildClient(this.props);
744
+ this.disposedOwnClient = false;
745
+ this.setState({
746
+ client,
747
+ status: "loading"
748
+ });
749
+ }
750
+ this.handlers = new ClientHandlers(client);
751
+ this.handlers.on("configsUpdated", () => {
1891
752
  this.setState({ updatedAt: /* @__PURE__ */ new Date() });
1892
753
  });
1893
- (_this$state$client4 = this.state.client) === null || _this$state$client4 === void 0 || _this$state$client4.on("clientReady", () => {
754
+ this.handlers.on("clientReady", () => {
1894
755
  this.setState({ status: "ready" });
1895
756
  });
1896
- await ((_this$state$client5 = this.state.client) === null || _this$state$client5 === void 0 ? void 0 : _this$state$client5.initialize(this.props.context));
1897
- if (!((_this$state$client6 = this.state.client) === null || _this$state$client6 === void 0 ? void 0 : _this$state$client6.isReady)) this.setState({ status: "default" });
757
+ if (!this.ownsClient) this.handlers.registerHooks(this.props.hooks);
1898
758
  this.appStateSubscription = AppState.addEventListener("change", this.handleAppStateChange);
1899
759
  if (this.props.netInfoSubscribe) this.netInfoUnsubscribe = this.props.netInfoSubscribe(this.handleConnectivityChange);
760
+ await this.connect(client, this.props.context);
761
+ }
762
+ async connect(client, context) {
763
+ switch (decideMountAction(client, context)) {
764
+ case "initialize":
765
+ await client.initialize(context);
766
+ break;
767
+ case "updateContext":
768
+ this.setState({ status: "loading" });
769
+ await client.updateContext(context);
770
+ break;
771
+ case "none": return;
772
+ }
773
+ if (!client.isReady) this.setState({ status: "default" });
1900
774
  }
1901
775
  async componentDidUpdate(prevProps) {
1902
- if (prevProps.context !== this.props.context) {
1903
- var _this$state$client7, _this$props$context, _this$state$client8;
776
+ if (hasGivenClient(prevProps) && hasGivenClient(this.props) && prevProps.client !== this.props.client) createConsoleLogger("warn").warn("The client prop of ConfigDirectorProvider changed after it mounted and is ignored. Remount the provider, for example with a new key, to use another client.");
777
+ if (!deepEqual(prevProps.context, this.props.context)) {
778
+ var _this$state$client3, _this$props$context, _this$state$client4;
1904
779
  this.setState({ status: "loading" });
1905
- await ((_this$state$client7 = this.state.client) === null || _this$state$client7 === void 0 ? void 0 : _this$state$client7.updateContext((_this$props$context = this.props.context) !== null && _this$props$context !== void 0 ? _this$props$context : {}));
1906
- if (!((_this$state$client8 = this.state.client) === null || _this$state$client8 === void 0 ? void 0 : _this$state$client8.isReady)) this.setState({ status: "default" });
780
+ await ((_this$state$client3 = this.state.client) === null || _this$state$client3 === void 0 ? void 0 : _this$state$client3.updateContext((_this$props$context = this.props.context) !== null && _this$props$context !== void 0 ? _this$props$context : {}));
781
+ if (!((_this$state$client4 = this.state.client) === null || _this$state$client4 === void 0 ? void 0 : _this$state$client4.isReady)) this.setState({ status: "default" });
1907
782
  }
1908
783
  }
1909
784
  componentWillUnmount() {
1910
- var _this$appStateSubscri, _this$netInfoUnsubscr, _this$state$client9;
785
+ var _this$appStateSubscri, _this$netInfoUnsubscr, _this$handlers;
1911
786
  (_this$appStateSubscri = this.appStateSubscription) === null || _this$appStateSubscri === void 0 || _this$appStateSubscri.remove();
787
+ this.appStateSubscription = null;
1912
788
  (_this$netInfoUnsubscr = this.netInfoUnsubscribe) === null || _this$netInfoUnsubscr === void 0 || _this$netInfoUnsubscr.call(this);
1913
- (_this$state$client9 = this.state.client) === null || _this$state$client9 === void 0 || _this$state$client9.dispose();
789
+ this.netInfoUnsubscribe = null;
790
+ (_this$handlers = this.handlers) === null || _this$handlers === void 0 || _this$handlers.removeAll();
791
+ this.handlers = void 0;
792
+ if (this.ownsClient) {
793
+ var _this$state$client5;
794
+ (_this$state$client5 = this.state.client) === null || _this$state$client5 === void 0 || _this$state$client5.dispose();
795
+ this.disposedOwnClient = true;
796
+ }
1914
797
  }
1915
798
  render() {
1916
799
  return /* @__PURE__ */ jsx(reactContext.Provider, {