@configdirector/nuxt-sdk 1.11.1 → 1.12.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.
- package/dist/module.d.mts +13 -0
- package/dist/module.json +1 -1
- package/dist/module.mjs +2 -1
- package/dist/runtime/app/composables/useConfigDirectorClient.d.mts +1 -1
- package/dist/runtime/app/composables/useConfigDirectorClient.d.ts +1 -1
- package/dist/runtime/app/composables/useConfigDirectorClientHooks.d.mts +1 -1
- package/dist/runtime/app/composables/useConfigDirectorClientHooks.d.ts +1 -1
- package/dist/runtime/app/composables/useConfigDirectorContext.d.mts +1 -1
- package/dist/runtime/app/composables/useConfigDirectorContext.d.ts +1 -1
- package/dist/runtime/app/composables/useConfigDirectorContext.mjs +41 -2
- package/dist/runtime/app/composables/useConfigDirectorValue.d.mts +1 -1
- package/dist/runtime/app/composables/useConfigDirectorValue.d.ts +1 -1
- package/dist/runtime/{native-values-BV2k_xR2.d.mts → native-values-ChQdi7c7.d.mts} +6 -4
- package/dist/runtime/nitro/offline-values.d.mts +1 -1
- package/dist/runtime/nitro/offline-values.d.ts +1 -1
- package/dist/runtime/nitro/plugin.d.mts +2 -2
- package/dist/runtime/nitro/plugin.d.ts +2 -2
- package/dist/runtime/nitro/plugin.mjs +1 -1
- package/dist/runtime/plugin.client.mjs +403 -73
- package/dist/runtime/plugin.server.mjs +46 -2
- package/dist/runtime/server/composables/useConfigDirectorServerClient.d.mts +1 -1
- package/dist/runtime/server/composables/useConfigDirectorServerClient.d.ts +1 -1
- package/dist/runtime/server/composables/useConfigDirectorServerHooks.d.mts +1 -1
- package/dist/runtime/server/composables/useConfigDirectorServerHooks.d.ts +1 -1
- package/dist/runtime/{types-CS7AMNWe.d.mts → types-BoDJiQsr.d.mts} +1 -1
- package/dist/runtime/{types-Dt3bENKf.d.mts → types-CuzrFq9I.d.mts} +3 -1
- package/dist/testing.d.mts +7 -4
- package/dist/testing.mjs +280 -31
- package/package.json +2 -2
|
@@ -923,10 +923,27 @@ const generateInstanceId = () => {
|
|
|
923
923
|
});
|
|
924
924
|
};
|
|
925
925
|
//#endregion
|
|
926
|
+
//#region ../js-client-core/src/browser-globals.ts
|
|
927
|
+
const read = (readValue) => {
|
|
928
|
+
try {
|
|
929
|
+
return readValue(globalThis);
|
|
930
|
+
} catch {
|
|
931
|
+
return;
|
|
932
|
+
}
|
|
933
|
+
};
|
|
934
|
+
const readUserAgent = () => read((globals) => globals.navigator?.userAgent);
|
|
935
|
+
const readHost = () => read((globals) => globals.location?.host);
|
|
936
|
+
const localStorageIfAvailable = () => {
|
|
937
|
+
try {
|
|
938
|
+
return globalThis.localStorage ?? void 0;
|
|
939
|
+
} catch {
|
|
940
|
+
return;
|
|
941
|
+
}
|
|
942
|
+
};
|
|
943
|
+
//#endregion
|
|
926
944
|
//#region ../js-client-core/src/client-session.ts
|
|
927
945
|
const STORAGE_KEY = "configdirector-sdk:instance-id";
|
|
928
946
|
const RECORD_VERSION = 1;
|
|
929
|
-
const MAX_AGE_MS = 360 * 60 * 1e3;
|
|
930
947
|
const STORAGE_DEADLINE_MS = 1e3;
|
|
931
948
|
const CHECKSUM_SALT = "configdirector.instanceId.v1";
|
|
932
949
|
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}$/;
|
|
@@ -943,13 +960,6 @@ const checksumFor = (id, createdAt) => {
|
|
|
943
960
|
const low = fnv1a(payload, 2166136261);
|
|
944
961
|
return fnv1a(`${low}|${payload}`, 16777619).toString(16).padStart(8, "0") + low.toString(16).padStart(8, "0");
|
|
945
962
|
};
|
|
946
|
-
const localStorageIfAvailable = () => {
|
|
947
|
-
try {
|
|
948
|
-
return globalThis.localStorage ?? void 0;
|
|
949
|
-
} catch {
|
|
950
|
-
return;
|
|
951
|
-
}
|
|
952
|
-
};
|
|
953
963
|
const isUsableRecord = (record) => {
|
|
954
964
|
if (!record || typeof record !== "object") return false;
|
|
955
965
|
const { v, id, createdAt, checksum } = record;
|
|
@@ -978,11 +988,6 @@ const readStoredInstanceId = async (storage, logger) => {
|
|
|
978
988
|
logger.debug("[ConfigDirectorClient] The stored instance id failed validation, generating a new one");
|
|
979
989
|
return { outcome: "nothing usable" };
|
|
980
990
|
}
|
|
981
|
-
const age = Date.now() - record.createdAt;
|
|
982
|
-
if (age < 0 || age >= MAX_AGE_MS) {
|
|
983
|
-
logger.debug("[ConfigDirectorClient] The stored instance id has expired, generating a new one");
|
|
984
|
-
return { outcome: "nothing usable" };
|
|
985
|
-
}
|
|
986
991
|
return {
|
|
987
992
|
outcome: "stored",
|
|
988
993
|
instanceId: record.id
|
|
@@ -1198,17 +1203,6 @@ var PollingTransport = class extends AbstractPollingTransport {
|
|
|
1198
1203
|
}
|
|
1199
1204
|
};
|
|
1200
1205
|
//#endregion
|
|
1201
|
-
//#region ../js-client-core/src/browser-globals.ts
|
|
1202
|
-
const read = (readValue) => {
|
|
1203
|
-
try {
|
|
1204
|
-
return readValue(globalThis);
|
|
1205
|
-
} catch {
|
|
1206
|
-
return;
|
|
1207
|
-
}
|
|
1208
|
-
};
|
|
1209
|
-
const readUserAgent = () => read((globals) => globals.navigator?.userAgent);
|
|
1210
|
-
const readHost = () => read((globals) => globals.location?.host);
|
|
1211
|
-
//#endregion
|
|
1212
1206
|
//#region ../shared/src/context-kind.ts
|
|
1213
1207
|
const CONTEXT_KIND_MAXIMUM_LENGTH = 64;
|
|
1214
1208
|
const KIND_SAMPLE_MAXIMUM_CHARACTERS = 128;
|
|
@@ -1253,6 +1247,178 @@ const admitContext = (context) => {
|
|
|
1253
1247
|
};
|
|
1254
1248
|
};
|
|
1255
1249
|
//#endregion
|
|
1250
|
+
//#region ../shared/src/utf8-byte-length.ts
|
|
1251
|
+
const isHighSurrogate = (codeUnit) => codeUnit >= 55296 && codeUnit <= 56319;
|
|
1252
|
+
const isLowSurrogate = (codeUnit) => codeUnit >= 56320 && codeUnit <= 57343;
|
|
1253
|
+
const utf8ByteLength = (text) => {
|
|
1254
|
+
let bytes = 0;
|
|
1255
|
+
for (let i = 0; i < text.length; i++) {
|
|
1256
|
+
const codeUnit = text.charCodeAt(i);
|
|
1257
|
+
if (codeUnit <= 127) bytes += 1;
|
|
1258
|
+
else if (codeUnit <= 2047) bytes += 2;
|
|
1259
|
+
else if (isHighSurrogate(codeUnit) && i + 1 < text.length && isLowSurrogate(text.charCodeAt(i + 1))) {
|
|
1260
|
+
bytes += 4;
|
|
1261
|
+
i++;
|
|
1262
|
+
} else bytes += 3;
|
|
1263
|
+
}
|
|
1264
|
+
return bytes;
|
|
1265
|
+
};
|
|
1266
|
+
//#endregion
|
|
1267
|
+
//#region ../js-client-core/src/cached-values.ts
|
|
1268
|
+
const ENTRY_FORMAT_VERSION = 1;
|
|
1269
|
+
const DEFAULT_CONTEXT_KIND = "user";
|
|
1270
|
+
const CACHED_VALUES_STORAGE_KEY_PREFIX = "configdirector-sdk:cached-values:";
|
|
1271
|
+
const DEFAULT_CACHING_SIZE_LIMIT_BYTES = 256 * 1024;
|
|
1272
|
+
const planCaching = (connectionPlan, caching, platformProvider) => {
|
|
1273
|
+
if (connectionPlan.kind === "stay-offline") return {
|
|
1274
|
+
kind: "no cache",
|
|
1275
|
+
reason: "offline mode"
|
|
1276
|
+
};
|
|
1277
|
+
if (caching?.enabled === false) return {
|
|
1278
|
+
kind: "no cache",
|
|
1279
|
+
reason: "turned off"
|
|
1280
|
+
};
|
|
1281
|
+
const provider = caching?.provider ?? platformProvider?.(connectionPlan.clientSdkKey);
|
|
1282
|
+
if (!provider) return {
|
|
1283
|
+
kind: "no cache",
|
|
1284
|
+
reason: "no provider available"
|
|
1285
|
+
};
|
|
1286
|
+
return {
|
|
1287
|
+
kind: "cache",
|
|
1288
|
+
provider,
|
|
1289
|
+
clientSdkKey: connectionPlan.clientSdkKey
|
|
1290
|
+
};
|
|
1291
|
+
};
|
|
1292
|
+
const contextTagFor = (context, instanceId) => {
|
|
1293
|
+
const contextKind = context?.kind ?? DEFAULT_CONTEXT_KIND;
|
|
1294
|
+
if (context?.id != null) return {
|
|
1295
|
+
contextKind,
|
|
1296
|
+
contextId: context.id
|
|
1297
|
+
};
|
|
1298
|
+
return {
|
|
1299
|
+
contextKind,
|
|
1300
|
+
anonymousInstanceId: instanceId
|
|
1301
|
+
};
|
|
1302
|
+
};
|
|
1303
|
+
const sameContextTag = (stored, launch) => {
|
|
1304
|
+
if (stored.contextKind !== launch.contextKind) return false;
|
|
1305
|
+
if ("contextId" in stored && "contextId" in launch) return stored.contextId === launch.contextId;
|
|
1306
|
+
if ("anonymousInstanceId" in stored && "anonymousInstanceId" in launch) return stored.anonymousInstanceId === launch.anonymousInstanceId;
|
|
1307
|
+
return false;
|
|
1308
|
+
};
|
|
1309
|
+
const decideCachedValuesAdoption = (cachedValues, launchContextTag, hasReceivedLiveValues) => {
|
|
1310
|
+
if (hasReceivedLiveValues) return "live values already arrived";
|
|
1311
|
+
if (!sameContextTag(cachedValues.contextTag, launchContextTag)) return "context mismatch";
|
|
1312
|
+
return "adopt";
|
|
1313
|
+
};
|
|
1314
|
+
const isContextTag = (candidate) => {
|
|
1315
|
+
if (!candidate || typeof candidate !== "object") return false;
|
|
1316
|
+
const { contextKind, contextId, anonymousInstanceId } = candidate;
|
|
1317
|
+
if (typeof contextKind !== "string") return false;
|
|
1318
|
+
return typeof contextId === "string" || typeof anonymousInstanceId === "string";
|
|
1319
|
+
};
|
|
1320
|
+
const isConfigStateMap = (candidate) => {
|
|
1321
|
+
if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) return false;
|
|
1322
|
+
return Object.values(candidate).every((config) => config && typeof config === "object" && typeof config.id === "string" && typeof config.key === "string");
|
|
1323
|
+
};
|
|
1324
|
+
const isEntryFor = (clientSdkKey, candidate) => {
|
|
1325
|
+
if (!candidate || typeof candidate !== "object") return false;
|
|
1326
|
+
const entry = candidate;
|
|
1327
|
+
return entry.formatVersion === ENTRY_FORMAT_VERSION && entry.clientSdkKey === clientSdkKey && isContextTag(entry.contextTag) && typeof entry.environmentId === "string" && typeof entry.projectId === "string" && isConfigStateMap(entry.configs) && typeof entry.receivedAt === "number";
|
|
1328
|
+
};
|
|
1329
|
+
var CachedValuesStore = class {
|
|
1330
|
+
warned = /* @__PURE__ */ new Set();
|
|
1331
|
+
pendingRead;
|
|
1332
|
+
queuedCachedValues;
|
|
1333
|
+
writing = false;
|
|
1334
|
+
constructor(provider, clientSdkKey, logger) {
|
|
1335
|
+
this.provider = provider;
|
|
1336
|
+
this.clientSdkKey = clientSdkKey;
|
|
1337
|
+
this.logger = logger;
|
|
1338
|
+
}
|
|
1339
|
+
read() {
|
|
1340
|
+
this.pendingRead ??= this.readEntry();
|
|
1341
|
+
return this.pendingRead;
|
|
1342
|
+
}
|
|
1343
|
+
replace(cachedValues) {
|
|
1344
|
+
this.queuedCachedValues = cachedValues;
|
|
1345
|
+
this.writeQueued();
|
|
1346
|
+
}
|
|
1347
|
+
async readEntry() {
|
|
1348
|
+
let raw;
|
|
1349
|
+
try {
|
|
1350
|
+
raw = await this.provider.read();
|
|
1351
|
+
} catch (error) {
|
|
1352
|
+
this.warnOnce("read failed", "Could not read the values kept on the device, starting without them:", error);
|
|
1353
|
+
return { outcome: "failed" };
|
|
1354
|
+
}
|
|
1355
|
+
if (raw == null || raw === "") return { outcome: "nothing stored" };
|
|
1356
|
+
const entry = this.parseEntry(raw);
|
|
1357
|
+
if (!entry) {
|
|
1358
|
+
this.warnOnce("unreadable", "The values kept on the device could not be read and were removed, starting without them");
|
|
1359
|
+
await this.removeEntry();
|
|
1360
|
+
return { outcome: "unreadable" };
|
|
1361
|
+
}
|
|
1362
|
+
return {
|
|
1363
|
+
outcome: "stored",
|
|
1364
|
+
cachedValues: entry
|
|
1365
|
+
};
|
|
1366
|
+
}
|
|
1367
|
+
parseEntry(raw) {
|
|
1368
|
+
try {
|
|
1369
|
+
const candidate = JSON.parse(raw);
|
|
1370
|
+
return isEntryFor(this.clientSdkKey, candidate) ? candidate : void 0;
|
|
1371
|
+
} catch {
|
|
1372
|
+
return;
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
async writeQueued() {
|
|
1376
|
+
if (this.writing) return;
|
|
1377
|
+
this.writing = true;
|
|
1378
|
+
try {
|
|
1379
|
+
while (this.queuedCachedValues) {
|
|
1380
|
+
const cachedValues = this.queuedCachedValues;
|
|
1381
|
+
this.queuedCachedValues = void 0;
|
|
1382
|
+
await this.writeEntry(cachedValues);
|
|
1383
|
+
}
|
|
1384
|
+
} finally {
|
|
1385
|
+
this.writing = false;
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
async writeEntry(cachedValues) {
|
|
1389
|
+
const entry = {
|
|
1390
|
+
formatVersion: ENTRY_FORMAT_VERSION,
|
|
1391
|
+
clientSdkKey: this.clientSdkKey,
|
|
1392
|
+
...cachedValues
|
|
1393
|
+
};
|
|
1394
|
+
const serialized = JSON.stringify(entry);
|
|
1395
|
+
const size = utf8ByteLength(serialized);
|
|
1396
|
+
const limit = this.provider.sizeLimit();
|
|
1397
|
+
if (size > limit) {
|
|
1398
|
+
this.warnOnce("over the size limit", `The values received (${size} bytes) exceed the caching provider's limit of ${limit} bytes, so no values are kept on the device`);
|
|
1399
|
+
await this.removeEntry();
|
|
1400
|
+
return;
|
|
1401
|
+
}
|
|
1402
|
+
try {
|
|
1403
|
+
await this.provider.write(serialized);
|
|
1404
|
+
} catch (error) {
|
|
1405
|
+
this.warnOnce("write failed", "Could not keep the values on the device:", error);
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
async removeEntry() {
|
|
1409
|
+
try {
|
|
1410
|
+
await this.provider.remove();
|
|
1411
|
+
} catch (error) {
|
|
1412
|
+
this.warnOnce("remove failed", "Could not remove the values kept on the device:", error);
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
warnOnce(warning, message, ...details) {
|
|
1416
|
+
if (this.warned.has(warning)) return;
|
|
1417
|
+
this.warned.add(warning);
|
|
1418
|
+
this.logger.warn(`[ConfigDirectorClient] ${message}`, ...details);
|
|
1419
|
+
}
|
|
1420
|
+
};
|
|
1421
|
+
//#endregion
|
|
1256
1422
|
//#region ../js-client-core/src/DefaultConfigDirectorClient.ts
|
|
1257
1423
|
const MAX_EXPONENTIAL_DELAY = 9;
|
|
1258
1424
|
var DefaultConfigDirectorClient = class DefaultConfigDirectorClient {
|
|
@@ -1270,11 +1436,15 @@ var DefaultConfigDirectorClient = class DefaultConfigDirectorClient {
|
|
|
1270
1436
|
readyPromise;
|
|
1271
1437
|
readyResolve;
|
|
1272
1438
|
currentContext;
|
|
1439
|
+
submittedContext;
|
|
1273
1440
|
connectionPlan;
|
|
1274
1441
|
clientSession;
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1442
|
+
cachedValuesStore;
|
|
1443
|
+
servingCachedValues = false;
|
|
1444
|
+
cachedValuesAdoptionStarted = false;
|
|
1445
|
+
static from(liveTelemetryClient, clientSdkKeyOrOptions, sdkOptions, clientOptions, internalClientOptions) {
|
|
1446
|
+
if (typeof clientSdkKeyOrOptions === "object") return new DefaultConfigDirectorClient(liveTelemetryClient, void 0, sdkOptions, clientSdkKeyOrOptions, internalClientOptions);
|
|
1447
|
+
return new DefaultConfigDirectorClient(liveTelemetryClient, clientSdkKeyOrOptions, sdkOptions, clientOptions, internalClientOptions);
|
|
1278
1448
|
}
|
|
1279
1449
|
constructor(liveTelemetryClient, clientSdkKey, sdkOptions, clientOptions, internalClientOptions) {
|
|
1280
1450
|
this.logger = clientOptions?.logger ?? createDefaultLogger$1();
|
|
@@ -1282,10 +1452,12 @@ var DefaultConfigDirectorClient = class DefaultConfigDirectorClient {
|
|
|
1282
1452
|
this.timeout = clientOptions?.connection?.timeout ?? 3e3;
|
|
1283
1453
|
const urlFactory = internalClientOptions?.urlFactory ?? defaultUrlFactory;
|
|
1284
1454
|
this.connectionPlan = planConnection(clientSdkKey, clientOptions?.connection, urlFactory);
|
|
1455
|
+
this.cachedValuesStore = this.buildCachedValuesStore(planCaching(this.connectionPlan, clientOptions?.caching, internalClientOptions?.platformCachingProvider));
|
|
1285
1456
|
this.telemetryClient = this.buildTelemetryClient(liveTelemetryClient, clientOptions);
|
|
1286
1457
|
this.transport = internalClientOptions?.transport ?? this.buildTransport(sdkOptions, clientOptions, urlFactory, internalClientOptions);
|
|
1287
1458
|
this.transport.on("configSetReceived", (configSet) => {
|
|
1288
1459
|
this.hasReceivedConfigSet = true;
|
|
1460
|
+
this.servingCachedValues = false;
|
|
1289
1461
|
this.readyResolve?.();
|
|
1290
1462
|
const removedKeys = this.findKeysRemovedBy(configSet);
|
|
1291
1463
|
if (!this.configSet || configSet.kind == "full") this.configSet = configSet;
|
|
@@ -1293,6 +1465,7 @@ var DefaultConfigDirectorClient = class DefaultConfigDirectorClient {
|
|
|
1293
1465
|
...this.configSet.configs,
|
|
1294
1466
|
...configSet.configs
|
|
1295
1467
|
};
|
|
1468
|
+
this.keepValuesOnTheDevice(this.configSet);
|
|
1296
1469
|
const keys = Object.keys(configSet.configs);
|
|
1297
1470
|
this.emit("configsUpdated", {
|
|
1298
1471
|
keys,
|
|
@@ -1314,6 +1487,80 @@ var DefaultConfigDirectorClient = class DefaultConfigDirectorClient {
|
|
|
1314
1487
|
this.registerHandler("configsUpdated", clientOptions?.hooks?.configsUpdated);
|
|
1315
1488
|
this.registerHandler("contextUpdated", clientOptions?.hooks?.contextUpdated);
|
|
1316
1489
|
this.registerHandler("connectionError", clientOptions?.hooks?.connectionError);
|
|
1490
|
+
this.registerHandler("cachedValuesLoaded", clientOptions?.hooks?.cachedValuesLoaded);
|
|
1491
|
+
}
|
|
1492
|
+
buildCachedValuesStore(cachingPlan) {
|
|
1493
|
+
switch (cachingPlan.kind) {
|
|
1494
|
+
case "no cache":
|
|
1495
|
+
this.logger.debug(`[ConfigDirectorClient] No values are kept on the device: ${cachingPlan.reason}`);
|
|
1496
|
+
return;
|
|
1497
|
+
case "cache": {
|
|
1498
|
+
const store = new CachedValuesStore(cachingPlan.provider, cachingPlan.clientSdkKey, this.logger);
|
|
1499
|
+
store.read();
|
|
1500
|
+
return store;
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1504
|
+
startCachedValuesAdoption() {
|
|
1505
|
+
const store = this.cachedValuesStore;
|
|
1506
|
+
if (!store || this.cachedValuesAdoptionStarted) return;
|
|
1507
|
+
this.cachedValuesAdoptionStarted = true;
|
|
1508
|
+
this.adoptCachedValues(store);
|
|
1509
|
+
}
|
|
1510
|
+
async adoptCachedValues(store) {
|
|
1511
|
+
const read = await store.read();
|
|
1512
|
+
switch (read.outcome) {
|
|
1513
|
+
case "nothing stored":
|
|
1514
|
+
case "unreadable":
|
|
1515
|
+
case "failed": return;
|
|
1516
|
+
case "stored": break;
|
|
1517
|
+
}
|
|
1518
|
+
const { instanceId } = await this.clientSession;
|
|
1519
|
+
const launchContextTag = contextTagFor(this.submittedContext, instanceId);
|
|
1520
|
+
const adoption = decideCachedValuesAdoption(read.cachedValues, launchContextTag, this.hasReceivedConfigSet);
|
|
1521
|
+
switch (adoption) {
|
|
1522
|
+
case "adopt":
|
|
1523
|
+
this.serveCachedValues(read.cachedValues);
|
|
1524
|
+
return;
|
|
1525
|
+
case "live values already arrived":
|
|
1526
|
+
case "context mismatch":
|
|
1527
|
+
this.logger.debug(`[ConfigDirectorClient] The values kept on the device were not adopted: ${adoption}`);
|
|
1528
|
+
return;
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
serveCachedValues(cachedValues) {
|
|
1532
|
+
const { environmentId, projectId, configs } = cachedValues;
|
|
1533
|
+
this.configSet = {
|
|
1534
|
+
environmentId,
|
|
1535
|
+
projectId,
|
|
1536
|
+
configs,
|
|
1537
|
+
kind: "full"
|
|
1538
|
+
};
|
|
1539
|
+
this.servingCachedValues = true;
|
|
1540
|
+
const keys = Object.keys(configs);
|
|
1541
|
+
this.emit("cachedValuesLoaded", {});
|
|
1542
|
+
this.emit("configsUpdated", {
|
|
1543
|
+
keys,
|
|
1544
|
+
removedKeys: []
|
|
1545
|
+
});
|
|
1546
|
+
this.updateWatchers(keys);
|
|
1547
|
+
this.logger.debug("[ConfigDirectorClient] Serving the values kept on the device until live values arrive:", { keys });
|
|
1548
|
+
}
|
|
1549
|
+
keepValuesOnTheDevice(configSet) {
|
|
1550
|
+
const store = this.cachedValuesStore;
|
|
1551
|
+
if (!store) return;
|
|
1552
|
+
const context = this.submittedContext;
|
|
1553
|
+
const receivedAt = Date.now();
|
|
1554
|
+
this.clientSession.then(({ instanceId }) => store.replace({
|
|
1555
|
+
contextTag: contextTagFor(context, instanceId),
|
|
1556
|
+
environmentId: configSet.environmentId,
|
|
1557
|
+
projectId: configSet.projectId,
|
|
1558
|
+
configs: configSet.configs,
|
|
1559
|
+
receivedAt
|
|
1560
|
+
}));
|
|
1561
|
+
}
|
|
1562
|
+
servedReason(reason) {
|
|
1563
|
+
return this.servingCachedValues && reason === "found-match" ? "cached" : reason;
|
|
1317
1564
|
}
|
|
1318
1565
|
registerHandler(event, handler) {
|
|
1319
1566
|
if (!handler) return;
|
|
@@ -1337,7 +1584,8 @@ var DefaultConfigDirectorClient = class DefaultConfigDirectorClient {
|
|
|
1337
1584
|
clientSdkKey: plan.clientSdkKey,
|
|
1338
1585
|
baseUrl: plan.baseUrl,
|
|
1339
1586
|
logger: this.logger,
|
|
1340
|
-
metadata: clientOptions?.metadata
|
|
1587
|
+
metadata: clientOptions?.metadata,
|
|
1588
|
+
clientSession: this.clientSession
|
|
1341
1589
|
});
|
|
1342
1590
|
}
|
|
1343
1591
|
}
|
|
@@ -1407,6 +1655,8 @@ var DefaultConfigDirectorClient = class DefaultConfigDirectorClient {
|
|
|
1407
1655
|
return this.currentContext;
|
|
1408
1656
|
}
|
|
1409
1657
|
async connectToTransport(context, caller) {
|
|
1658
|
+
this.submittedContext = context;
|
|
1659
|
+
if (caller === "initialization") this.startCachedValuesAdoption();
|
|
1410
1660
|
try {
|
|
1411
1661
|
this.ready = false;
|
|
1412
1662
|
this.readyPromise = new Promise((resolve) => {
|
|
@@ -1507,6 +1757,7 @@ var DefaultConfigDirectorClient = class DefaultConfigDirectorClient {
|
|
|
1507
1757
|
return defaultValue;
|
|
1508
1758
|
}
|
|
1509
1759
|
const parseResult = parseConfigValue(configState, defaultValue);
|
|
1760
|
+
const reason = this.servedReason(parseResult.reason);
|
|
1510
1761
|
this.telemetryClient.evaluatedConfig({
|
|
1511
1762
|
contextId: this.currentContext?.id,
|
|
1512
1763
|
type: configState.type,
|
|
@@ -1516,14 +1767,14 @@ var DefaultConfigDirectorClient = class DefaultConfigDirectorClient {
|
|
|
1516
1767
|
evaluatedValue: parseResult.parsedValue,
|
|
1517
1768
|
evaluatedValueId: parseResult.parsedValueId,
|
|
1518
1769
|
usedDefault: parseResult.usedDefault,
|
|
1519
|
-
evaluationReason:
|
|
1770
|
+
evaluationReason: reason
|
|
1520
1771
|
});
|
|
1521
1772
|
this.dispatchEvaluationEvent({
|
|
1522
1773
|
key: configKey,
|
|
1523
1774
|
value: parseResult.parsedValue,
|
|
1524
1775
|
valueId: parseResult.parsedValueId ?? void 0,
|
|
1525
1776
|
isDefaultValue: parseResult.usedDefault,
|
|
1526
|
-
reason
|
|
1777
|
+
reason,
|
|
1527
1778
|
context: this.context
|
|
1528
1779
|
});
|
|
1529
1780
|
this.logger.debug(`[ConfigDirectorClient] Evaluated '${configKey}' to '${parseResult.parsedValue}'`);
|
|
@@ -1681,6 +1932,7 @@ const DEFAULT_WORKER_CLOSE_TIMEOUT = 6e3;
|
|
|
1681
1932
|
var WebWorkerTelemetryClient = class {
|
|
1682
1933
|
logger;
|
|
1683
1934
|
initializeMessage;
|
|
1935
|
+
clientSession;
|
|
1684
1936
|
worker;
|
|
1685
1937
|
turnedOff = false;
|
|
1686
1938
|
pendingEvaluations = [];
|
|
@@ -1695,6 +1947,7 @@ var WebWorkerTelemetryClient = class {
|
|
|
1695
1947
|
this.logger = options.logger;
|
|
1696
1948
|
this.workerCloseTimeout = options.workerCloseTimeout ?? DEFAULT_WORKER_CLOSE_TIMEOUT;
|
|
1697
1949
|
this.pendingEvaluationLimit = options.evaluationQueueLimit ?? 1e3;
|
|
1950
|
+
this.clientSession = options.clientSession;
|
|
1698
1951
|
this.initializeMessage = {
|
|
1699
1952
|
type: "Initialize",
|
|
1700
1953
|
payload: {
|
|
@@ -1717,7 +1970,7 @@ var WebWorkerTelemetryClient = class {
|
|
|
1717
1970
|
return;
|
|
1718
1971
|
}
|
|
1719
1972
|
try {
|
|
1720
|
-
return new Worker(URL.createObjectURL(new Blob(["(function(){let e=async(e,t)=>e.valueId?{valueId:e.valueId}:e.value?e.value.length<=500&&e.type!=`json`?{value:e.value}:{valueId:await t(e.value)}:e,t=e=>{let t=Object.keys(e);if(t.length==0)return!0;for(let n of t)if((e[n]?.length??0)>0)return!1;return!0},n=e=>{if(!e)return!0;let t=Object.keys(e);if(t.length==0)return!0;for(let n of t)if((e[n]??0)>0)return!1;return!0},r=async(e,t,n,r)=>{let i=new AbortController,a=setTimeout(()=>{r.debug(`[fetchWithTimeout] Reached timeout, aborting request`),i.abort()},e);try{let e=await fetch(typeof t==`string`||t instanceof Request?t:t.toString(),{...n,signal:i.signal});return clearTimeout(a),e}catch(e){throw r.warn(`[fetchWithTimeout] Fetch error: `,e),clearTimeout(a),e}},i=e=>e?.name===`NotAllowedError`?!0:e instanceof TypeError;var a=class{constructor(e){this.executeRequests=!0,this.sdkKey=e.sdkKey,this.logger=e.logger,this.sdkIdentity=e.sdkIdentity,this.metaContext=e.metaContext,this.url=e.urlFactory(`client/telemetry/v1`,e.baseUrl)}async report({context:e,discreteEvents:t,aggregatedEvents:n,droppedEvents:r}){if(!this.executeRequests)return{success:!1,fatalError:!0};let
|
|
1973
|
+
return new Worker(URL.createObjectURL(new Blob(["(function(){let e=async(e,t)=>e.valueId?{valueId:e.valueId}:e.value?e.value.length<=500&&e.type!=`json`?{value:e.value}:{valueId:await t(e.value)}:e,t=e=>{let t=Object.keys(e);if(t.length==0)return!0;for(let n of t)if((e[n]?.length??0)>0)return!1;return!0},n=e=>{if(!e)return!0;let t=Object.keys(e);if(t.length==0)return!0;for(let n of t)if((e[n]??0)>0)return!1;return!0},r=async(e,t,n,r)=>{let i=new AbortController,a=setTimeout(()=>{r.debug(`[fetchWithTimeout] Reached timeout, aborting request`),i.abort()},e);try{let e=await fetch(typeof t==`string`||t instanceof Request?t:t.toString(),{...n,signal:i.signal});return clearTimeout(a),e}catch(e){throw r.warn(`[fetchWithTimeout] Fetch error: `,e),clearTimeout(a),e}},i=e=>e?.name===`NotAllowedError`?!0:e instanceof TypeError;var a=class{constructor(e){this.executeRequests=!0,this.sdkKey=e.sdkKey,this.logger=e.logger,this.sdkIdentity=e.sdkIdentity,this.metaContext=e.metaContext,this.url=e.urlFactory(`client/telemetry/v1`,e.baseUrl),this.clientSession=e.clientSession}async report({context:e,discreteEvents:t,aggregatedEvents:n,droppedEvents:r}){if(!this.executeRequests)return{success:!1,fatalError:!0};let{instanceId:i}=await this.clientSession,a={clientSdkKey:this.sdkKey,instanceId:i,metaContext:{sdkName:this.sdkIdentity.sdkName,sdkVersion:this.sdkIdentity.sdkVersion,appName:this.metaContext.appName,appVersion:this.metaContext.appVersion},context:e,discreteEvents:t,aggregatedEvents:n,droppedEvents:r};if(this.isReportEmpty(a))return{success:!0,fatalError:!1};let o=await this.sendReport(a);return o.fatalError&&(this.executeRequests=!1),o}isReportEmpty(e){return t(e.discreteEvents)&&t(e.aggregatedEvents)&&n(e.droppedEvents)}async sendReport(e){try{let t=await r(5e3,this.url,{method:`POST`,body:JSON.stringify(e),keepalive:!0},this.logger),n=this.isStatusFatal(t.status);return n&&this.logger.warn(`[EventReporter] Received a fatal status response (${t.status}) from the telemetry endpoint. No more telemetry data will be sent.`),{success:t.ok,fatalError:n}}catch(e){let t=i(e);return this.logger.warn(`[EventReporter] Error attempting to send telemetry data: ${e}`,{error:e}),{success:!1,fatalError:t}}}isStatusFatal(e){return!!e&&e>=400&&e<500&&e!=429}},o=class{aggregate(e){if(e.events.length==0)return[];let t=new Map;for(let n of e.events){let e=JSON.stringify(n),r=t.get(e)?.count||0;t.set(e,{event:n,count:r+1})}return Array.from(t).map(([,t])=>({startTime:e.startTime,endTime:e.endTime,count:t.count,event:t.event}))}},s=class{constructor(e){this._events=[],this._droppedEventCount=0,this.limit=e??1e3}get events(){return this._events}get reachedLimit(){return this._events.length>=this.limit}get droppedEventCount(){return this._droppedEventCount}push(...e){this.startTime||=new Date;let t=Math.max(0,e.length-this.limit),n=e.slice(t,e.length);if(this._droppedEventCount+=t,this._events.length+n.length>this.limit){let e=this._events.length+n.length-this.limit;this._events.splice(0,e),this._droppedEventCount+=e}return this._events.push(...n)}takeSnapshot(){let e=new Date,t=this.startTime??e,n=this._events.splice(0),r=this._droppedEventCount;return this.startTime=void 0,this._droppedEventCount=0,{startTime:t,endTime:e,events:n,droppedCount:r}}clear(){this._events=[],this.startTime=void 0,this._droppedEventCount=0}},c=class{constructor(e){this.aggregator=new o,this.collectEvents=!0,this.started=!1,this.logger=e.logger,this.evaluationEventQueue=new s(e.evaluationQueueLimit??1e3),this.flushIntervalDelay=e.flushIntervalDelay??3e4,this.initialFlushIntervalDelay=e.initialFlushIntervalDelay??5e3}start(){this.started||!this.collectEvents||(this.started=!0,this.flushTimeout=setTimeout(()=>this.flushAndScheduleNext(),this.initialFlushIntervalDelay))}async flushAndScheduleNext(){if(!this.collectEvents)return;let e;try{e=await this.flush()}catch(t){this.logger.warn(`[TelemetryEventCollector] Error flushing telemetry events: `,t),e={success:!1,fatalError:!1}}e.fatalError?(this.collectEvents=!1,this.close(),this.logger.warn(`[TelemetryEventCollector] Received a fatal error from telemetry collection. No longer collecting events.`)):this.collectEvents&&(this.cancelScheduledFlush(),this.flushTimeout=setTimeout(()=>this.flushAndScheduleNext(),this.flushIntervalDelay))}cancelScheduledFlush(){clearTimeout(this.flushTimeout)}async close(){this.collectEvents=!1,clearTimeout(this.flushTimeout);try{await this.flush()}catch(e){this.logger.warn(`[TelemetryEventCollector] Error flushing telemetry events during close: `,e)}this.evaluationEventQueue.clear()}dispose(){this.close()}},l=class{constructor(e){this.valueIdGenerator=e,this.valueIdGenerator=e}async process(t){let n=await Promise.all(t.events.map(async t=>({...t,defaultValue:await e(t.defaultValue,this.valueIdGenerator),evaluatedValue:await e(t.evaluatedValue,this.valueIdGenerator)})));return{...t,events:n}}},u=class{constructor(){this.countsByKindSample=new Map,this.countBeyondSampleLimit=0}record(e){return this.startTime??=new Date,this.add(e,1)}absorb(e){for(let t of e)(this.startTime===void 0||t.startTime<this.startTime)&&(this.startTime=t.startTime),this.add(t.event.kindSample,t.count)}add(e,t){if(e!==void 0){let n=this.countsByKindSample.get(e);if(n!==void 0)return this.countsByKindSample.set(e,n+t),`known-kind-sample`;if(this.countsByKindSample.size<20)return this.countsByKindSample.set(e,t),`new-kind-sample`}let n=this.countBeyondSampleLimit===0;return this.countBeyondSampleLimit+=t,n?`first-beyond-sample-limit`:`beyond-sample-limit`}takeSnapshot(){let e=new Date,t=this.startTime??e,n=(n,r)=>({startTime:t,endTime:e,count:n,event:{reason:`invalid-context-kind`,kindSample:r}}),r=Array.from(this.countsByKindSample,([e,t])=>n(t,e)),i=this.countBeyondSampleLimit>0?[...r,n(this.countBeyondSampleLimit)]:r;return this.clear(),i}clear(){this.countsByKindSample.clear(),this.countBeyondSampleLimit=0,this.startTime=void 0}},d=class extends c{constructor(e){super(e),this.invalidContextSubmissions=new u,this.reporter=new a(e),this.evaluationEventSnapshotPreprocessor=new l(e.valueIdGenerator)}async updateContext(e){this.cancelScheduledFlush(),await this.flush(),this._context=e,await this.flushAndScheduleNext()}evaluatedConfig(e){this.collectEvents&&this.evaluationEventQueue.push(e)}recordInvalidContext(e){this.invalidContextSubmissions.record(e)}absorbInvalidContextSubmissions(e){this.invalidContextSubmissions.absorb(e)}async forceFlush(){this.cancelScheduledFlush(),await this.flushAndScheduleNext()}async flush(){if(!this.reporter)return{success:!1,fatalError:!1};let e=await this.evaluationEventSnapshotPreprocessor.process(this.evaluationEventQueue.takeSnapshot());return await this.reporter?.report({context:this._context,discreteEvents:{},aggregatedEvents:{evaluatedConfig:this.aggregator.aggregate(e),invalidContext:this.invalidContextSubmissions.takeSnapshot()},droppedEvents:{evaluatedConfig:e.droppedCount}})}};let f=(e,t)=>new URL(e,t?.toString()),p=(e,t)=>{let n=BigInt(0),r=BigInt(62),i=Array.from(e).map(e=>e.toString(16).padStart(2,`0`)).join(``),a=BigInt(`0x`+i),o=``;for(;a>n;)o=`0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz`[Number(a%r)]+o,a/=r;return o.padStart(t,`0`)},m=Math.ceil(128/Math.log2(62)),h=new TextEncoder,g=async e=>{if(typeof crypto>`u`||crypto.subtle==null)return;let t=await crypto.subtle.digest(`SHA-256`,h.encode(e?.toString()??``));return p(new Uint8Array(t,0,16),m)},_,v,y=new Promise(e=>{v=e}),b=(e,t,...n)=>({type:`Log`,payload:{level:e,message:t,args:n}}),x=e=>{try{self.postMessage(e)}catch{self.postMessage(b(e.payload.level,e.payload.message))}},S={debug:function(e,...t){x(b(`debug`,e,t))},info:function(e,...t){x(b(`info`,e,t))},warn:function(e,...t){x(b(`warn`,e,t))},error:function(e,...t){x(b(`error`,e,t))}};addEventListener(`message`,e=>{let t=e.data;if(t.type!=`Initialize`&&!_){S.info(`[TelemetryWorker] Ignoring ${t.type} because no collector is configured`);return}switch(S.debug(`[TelemetryWorker] Received '${t.type}' event:`,t),t.type){case`Initialize`:if(_){S.warn(`[TelemetryWorker] Received a duplicate Initialize event, ignoring it`);break}_=new d({...t.payload,baseUrl:f(t.payload.baseUrl),urlFactory:f,valueIdGenerator:g,logger:S,clientSession:y}),_.start();break;case`ClientSession`:v(t.payload);break;case`EvaluatedConfigEvent`:_?.evaluatedConfig(t.payload);break;case`InvalidContextSubmissions`:_?.absorbInvalidContextSubmissions(t.payload.entries);break;case`UpdateContext`:_?.updateContext(t.payload.context);break;case`Flush`:_?.forceFlush();break;case`Close`:(_?.close()??Promise.resolve()).then(()=>{self.postMessage({type:`Closed`}),self.close()});break}})})();"], { type: "application/javascript" })));
|
|
1721
1974
|
} catch (error) {
|
|
1722
1975
|
this.turnOff("[WebWorkerTelemetryClient] Telemetry is off because the telemetry Web Worker could not be created: ", error);
|
|
1723
1976
|
return;
|
|
@@ -1736,6 +1989,7 @@ var WebWorkerTelemetryClient = class {
|
|
|
1736
1989
|
this.handleWorkerEvent(message.data);
|
|
1737
1990
|
};
|
|
1738
1991
|
worker.postMessage(this.initializeMessage);
|
|
1992
|
+
this.forwardClientSession(worker);
|
|
1739
1993
|
this.worker = worker;
|
|
1740
1994
|
try {
|
|
1741
1995
|
document.addEventListener("visibilitychange", this.visibilityHandler);
|
|
@@ -1747,6 +2001,15 @@ var WebWorkerTelemetryClient = class {
|
|
|
1747
2001
|
this.forwardInvalidContextSubmissions(worker);
|
|
1748
2002
|
return worker;
|
|
1749
2003
|
}
|
|
2004
|
+
forwardClientSession(worker) {
|
|
2005
|
+
this.clientSession.then((clientSession) => {
|
|
2006
|
+
const clientSessionMessage = {
|
|
2007
|
+
type: "ClientSession",
|
|
2008
|
+
payload: clientSession
|
|
2009
|
+
};
|
|
2010
|
+
worker.postMessage(clientSessionMessage);
|
|
2011
|
+
});
|
|
2012
|
+
}
|
|
1750
2013
|
forwardInvalidContextSubmissions(worker) {
|
|
1751
2014
|
if (!worker) return;
|
|
1752
2015
|
const entries = this.pendingInvalidContextSubmissions.takeSnapshot();
|
|
@@ -1879,6 +2142,25 @@ const generateValueId = async (v) => {
|
|
|
1879
2142
|
return toBase62(new Uint8Array(hashBuffer, 0, 16), BASE62_LENGTH);
|
|
1880
2143
|
};
|
|
1881
2144
|
//#endregion
|
|
2145
|
+
//#region ../js-browser-client/src/local-storage-caching-provider.ts
|
|
2146
|
+
const toHex = (bytes) => Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
2147
|
+
const storageKeyFor = async (clientSdkKey) => {
|
|
2148
|
+
const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(clientSdkKey));
|
|
2149
|
+
return CACHED_VALUES_STORAGE_KEY_PREFIX + toHex(new Uint8Array(digest));
|
|
2150
|
+
};
|
|
2151
|
+
const subtleCryptoAvailable = () => typeof crypto !== "undefined" && crypto.subtle != null;
|
|
2152
|
+
const localStorageCachingProvider = (clientSdkKey) => {
|
|
2153
|
+
const storage = localStorageIfAvailable();
|
|
2154
|
+
if (!storage || !subtleCryptoAvailable()) return;
|
|
2155
|
+
const storageKey = storageKeyFor(clientSdkKey);
|
|
2156
|
+
return {
|
|
2157
|
+
read: async () => storage.getItem(await storageKey),
|
|
2158
|
+
write: async (value) => storage.setItem(await storageKey, value),
|
|
2159
|
+
remove: async () => storage.removeItem(await storageKey),
|
|
2160
|
+
sizeLimit: () => DEFAULT_CACHING_SIZE_LIMIT_BYTES
|
|
2161
|
+
};
|
|
2162
|
+
};
|
|
2163
|
+
//#endregion
|
|
1882
2164
|
//#region ../js-browser-client/src/BrowserConfigDirectorClient.ts
|
|
1883
2165
|
/**
|
|
1884
2166
|
* Creates a browser client. With a client SDK key it connects to ConfigDirector; with
|
|
@@ -1887,7 +2169,7 @@ const generateValueId = async (v) => {
|
|
|
1887
2169
|
* and fails like a missing key otherwise.
|
|
1888
2170
|
*/
|
|
1889
2171
|
const createBrowserClient = (clientSdkKeyOrOptions, sdkOptions, clientOptions) => {
|
|
1890
|
-
return DefaultConfigDirectorClient.from(({ clientSdkKey, baseUrl, logger, metadata }) => new WebWorkerTelemetryClient({
|
|
2172
|
+
return DefaultConfigDirectorClient.from(({ clientSdkKey, baseUrl, logger, metadata, clientSession }) => new WebWorkerTelemetryClient({
|
|
1891
2173
|
sdkKey: clientSdkKey,
|
|
1892
2174
|
sdkIdentity: {
|
|
1893
2175
|
sdkName: sdkOptions.sdkName,
|
|
@@ -1900,14 +2182,28 @@ const createBrowserClient = (clientSdkKeyOrOptions, sdkOptions, clientOptions) =
|
|
|
1900
2182
|
logger,
|
|
1901
2183
|
baseUrl,
|
|
1902
2184
|
urlFactory: defaultUrlFactory,
|
|
1903
|
-
valueIdGenerator: generateValueId
|
|
1904
|
-
|
|
2185
|
+
valueIdGenerator: generateValueId,
|
|
2186
|
+
clientSession
|
|
2187
|
+
}), clientSdkKeyOrOptions, sdkOptions, clientOptions, { platformCachingProvider: localStorageCachingProvider });
|
|
1905
2188
|
};
|
|
1906
2189
|
//#endregion
|
|
1907
2190
|
//#region ../js-client-core/src/installedTestClient.ts
|
|
1908
2191
|
const INSTALLED_TEST_CLIENT_SLOT = Symbol.for("@configdirector/installed-test-client");
|
|
1909
2192
|
const readInstalledTestClient = () => globalThis[INSTALLED_TEST_CLIENT_SLOT];
|
|
1910
2193
|
//#endregion
|
|
2194
|
+
//#region ../shared/src/browser-half.ts
|
|
2195
|
+
const decideBrowserHalfSource = (browserHalf, serverConnectionMode) => {
|
|
2196
|
+
if (browserHalf.mode !== void 0 || browserHalf.values !== void 0) return "as-given";
|
|
2197
|
+
if (serverConnectionMode === "offline") return "inherit-offline";
|
|
2198
|
+
return "connect";
|
|
2199
|
+
};
|
|
2200
|
+
//#endregion
|
|
2201
|
+
//#region src/runtime/app/composables/useConfigDirectorClient.ts
|
|
2202
|
+
const useConfigDirectorClient = () => {
|
|
2203
|
+
const { $configDirectorClient } = useNuxtApp();
|
|
2204
|
+
return { client: $configDirectorClient };
|
|
2205
|
+
};
|
|
2206
|
+
//#endregion
|
|
1911
2207
|
//#region ../shared/src/deepEqual.ts
|
|
1912
2208
|
const deepEqual = (left, right) => {
|
|
1913
2209
|
if (Object.is(left, right)) return true;
|
|
@@ -1933,17 +2229,17 @@ const decideMountAction = (client, context) => {
|
|
|
1933
2229
|
return "none";
|
|
1934
2230
|
};
|
|
1935
2231
|
//#endregion
|
|
1936
|
-
//#region
|
|
1937
|
-
const
|
|
1938
|
-
|
|
1939
|
-
if (
|
|
1940
|
-
return "
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
const
|
|
1945
|
-
|
|
1946
|
-
return
|
|
2232
|
+
//#region src/runtime/client-startup.ts
|
|
2233
|
+
const decideStartupAction = (client, context, waitsForFirstContext) => {
|
|
2234
|
+
const mountAction = decideMountAction(client, context);
|
|
2235
|
+
if (!waitsForFirstContext) return mountAction;
|
|
2236
|
+
if (mountAction === "initialize" && context === void 0) return "waitForContext";
|
|
2237
|
+
if (mountAction === "none" && client.isInitializing) return "firstStartInFlight";
|
|
2238
|
+
return mountAction;
|
|
2239
|
+
};
|
|
2240
|
+
const decideContextUpdateAction = (client, context, waitsForFirstContext) => {
|
|
2241
|
+
if (waitsForFirstContext && decideMountAction(client, context) === "initialize") return "startClient";
|
|
2242
|
+
return "updateContext";
|
|
1947
2243
|
};
|
|
1948
2244
|
//#endregion
|
|
1949
2245
|
//#region src/runtime/app/composables/useConfigDirectorContext.ts
|
|
@@ -1954,6 +2250,14 @@ const getEffectiveTimeout = (option) => {
|
|
|
1954
2250
|
const useConfigDirectorContext = () => {
|
|
1955
2251
|
const state = useState("configdirector-context");
|
|
1956
2252
|
const { client } = useConfigDirectorClient();
|
|
2253
|
+
const { $configDirectorClientStartup } = useNuxtApp();
|
|
2254
|
+
const startup = $configDirectorClientStartup;
|
|
2255
|
+
const handContextToClient = (rawContext) => {
|
|
2256
|
+
switch (decideContextUpdateAction(client, rawContext, startup.waitsForFirstContext)) {
|
|
2257
|
+
case "startClient": return startup.start(rawContext);
|
|
2258
|
+
case "updateContext": return client.updateContext(rawContext);
|
|
2259
|
+
}
|
|
2260
|
+
};
|
|
1957
2261
|
const updateContext = async (context, options) => {
|
|
1958
2262
|
const rawContext = toRaw(context);
|
|
1959
2263
|
const oldContext = JSON.stringify(state.value);
|
|
@@ -1963,7 +2267,7 @@ const useConfigDirectorContext = () => {
|
|
|
1963
2267
|
const timeout = getEffectiveTimeout(options?.timeoutMilliseconds);
|
|
1964
2268
|
let timer;
|
|
1965
2269
|
try {
|
|
1966
|
-
await Promise.race([
|
|
2270
|
+
await Promise.race([handContextToClient(rawContext), new Promise((resolve) => {
|
|
1967
2271
|
timer = setTimeout(() => resolve(), timeout);
|
|
1968
2272
|
})]);
|
|
1969
2273
|
} finally {
|
|
@@ -1990,28 +2294,51 @@ const OFFLINE_VALUES_ROUTE = "/_configdirector/offline-values";
|
|
|
1990
2294
|
//#region src/runtime/plugin.client.ts
|
|
1991
2295
|
const DEFAULT_INITIALIZATION_TIMEOUT_MILLISECONDS = 2e3;
|
|
1992
2296
|
var plugin_client_default = defineNuxtPlugin(async (nuxtApp) => {
|
|
1993
|
-
const
|
|
1994
|
-
const logger = createDefaultLogger(
|
|
2297
|
+
const configdirector = useRuntimeConfig().public?.configdirector;
|
|
2298
|
+
const logger = createDefaultLogger(configdirector?.logLevel);
|
|
1995
2299
|
const installedTestClient = readInstalledTestClient();
|
|
1996
|
-
const client = installedTestClient
|
|
2300
|
+
const { client, runsOffline } = installedTestClient ? {
|
|
2301
|
+
client: installedTestClient,
|
|
2302
|
+
runsOffline: false
|
|
2303
|
+
} : await buildClient(configdirector, logger);
|
|
1997
2304
|
logger.debug(installedTestClient ? "Installed ConfigDirector Nuxt plugin with a test client" : "Installed ConfigDirector Nuxt plugin");
|
|
1998
2305
|
const readyStatus = shallowRef(client.isReady ? "ready" : "loading");
|
|
1999
2306
|
client.on("clientReady", () => {
|
|
2000
2307
|
readyStatus.value = "ready";
|
|
2001
2308
|
});
|
|
2309
|
+
const settleReadyStatus = () => {
|
|
2310
|
+
if (!client.isReady) readyStatus.value = "default";
|
|
2311
|
+
};
|
|
2312
|
+
const startup = {
|
|
2313
|
+
waitsForFirstContext: configdirector?.deferInitialization === true && !runsOffline,
|
|
2314
|
+
start: async (context) => {
|
|
2315
|
+
await client.initialize(context);
|
|
2316
|
+
settleReadyStatus();
|
|
2317
|
+
}
|
|
2318
|
+
};
|
|
2002
2319
|
nuxtApp.hooks.hook("app:created", async () => {
|
|
2003
2320
|
const { context } = useConfigDirectorContext();
|
|
2004
2321
|
const rawContext = toRaw(context.value);
|
|
2005
|
-
const
|
|
2006
|
-
logger.debug(`Browser client
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2322
|
+
const startupAction = decideStartupAction(client, rawContext, startup.waitsForFirstContext);
|
|
2323
|
+
logger.debug(`Browser client startup action: ${startupAction}`);
|
|
2324
|
+
switch (startupAction) {
|
|
2325
|
+
case "waitForContext": return;
|
|
2326
|
+
case "firstStartInFlight": return;
|
|
2327
|
+
case "initialize":
|
|
2328
|
+
await client.initialize(rawContext);
|
|
2329
|
+
break;
|
|
2330
|
+
case "updateContext":
|
|
2331
|
+
await client.updateContext(rawContext);
|
|
2332
|
+
break;
|
|
2333
|
+
case "none": break;
|
|
2334
|
+
}
|
|
2335
|
+
settleReadyStatus();
|
|
2336
|
+
logger.debug(`Browser client startup action awaited, ready status: ${client.isReady}`);
|
|
2011
2337
|
});
|
|
2012
2338
|
return { provide: {
|
|
2013
2339
|
configDirectorClient: client,
|
|
2014
|
-
configDirectorClientReadyStatus: readonly(readyStatus)
|
|
2340
|
+
configDirectorClientReadyStatus: readonly(readyStatus),
|
|
2341
|
+
configDirectorClientStartup: startup
|
|
2015
2342
|
} };
|
|
2016
2343
|
});
|
|
2017
2344
|
const buildClient = async (configdirector, logger) => {
|
|
@@ -2021,23 +2348,26 @@ const buildClient = async (configdirector, logger) => {
|
|
|
2021
2348
|
logger.error(message);
|
|
2022
2349
|
throw new ConfigDirectorInitializationError(message);
|
|
2023
2350
|
}
|
|
2024
|
-
return
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2351
|
+
return {
|
|
2352
|
+
client: createBrowserClient(configdirector?.clientSdkKey || void 0, {
|
|
2353
|
+
sdkName: "nuxt-sdk",
|
|
2354
|
+
sdkVersion: "1.12.1"
|
|
2355
|
+
}, {
|
|
2356
|
+
metadata: {
|
|
2357
|
+
appName: configdirector?.appName,
|
|
2358
|
+
appVersion: configdirector?.appVersion
|
|
2359
|
+
},
|
|
2360
|
+
connection: {
|
|
2361
|
+
url: configdirector?.baseUrl || void 0,
|
|
2362
|
+
mode: browserHalf.mode,
|
|
2363
|
+
values: browserHalf.values,
|
|
2364
|
+
pollingInterval: configdirector?.connection?.pollingInterval || void 0,
|
|
2365
|
+
timeout: configdirector?.connection?.timeout || DEFAULT_INITIALIZATION_TIMEOUT_MILLISECONDS
|
|
2366
|
+
},
|
|
2367
|
+
logger
|
|
2368
|
+
}),
|
|
2369
|
+
runsOffline: browserHalf.mode === "offline"
|
|
2370
|
+
};
|
|
2041
2371
|
};
|
|
2042
2372
|
const browserHalfConnectionOf = async (configdirector, logger) => {
|
|
2043
2373
|
const givenConnection = {
|