@ops-ai/react-router-toggly 1.1.0 → 1.2.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.
- package/CHANGELOG.md +11 -0
- package/dist/client.cjs +72 -7
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +26 -0
- package/dist/client.d.ts +26 -0
- package/dist/client.js +72 -7
- package/dist/client.js.map +1 -1
- package/dist/server.cjs +212 -64
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +49 -5
- package/dist/server.d.ts +49 -5
- package/dist/server.js +212 -64
- package/dist/server.js.map +1 -1
- package/dist/telemetry/grpc.cjs +1 -1
- package/dist/telemetry/grpc.cjs.map +1 -1
- package/dist/telemetry/grpc.js +1 -1
- package/dist/telemetry/grpc.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
3
|
|
|
4
|
+
## [1.2.0] - 2026-09-26
|
|
5
|
+
|
|
6
|
+
### Added
|
|
7
|
+
- Opt-in `enableVariants` on `TogglyConfig`. When set, the browser client fetches
|
|
8
|
+
`/evaluated-variants-signed` instead of `/evaluated-signed`, and the server
|
|
9
|
+
client switches from `definitions-signed` local eval to remote variant
|
|
10
|
+
assignment. Exposes `getVariant` / `getVariantValue` on the provider context
|
|
11
|
+
and `TogglyServerClient` [OPS-1425].
|
|
12
|
+
- Typed soft-null `getVariantValue<T>(…, isT?)` and exported `decodeVariantValue`
|
|
13
|
+
matching the JS/Vue SDK contract.
|
|
14
|
+
|
|
4
15
|
## [1.1.0] - 2026-09-18
|
|
5
16
|
|
|
6
17
|
### Added
|
package/dist/client.cjs
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
var togglyHooksTypes = require('@ops-ai/toggly-hooks-types');
|
|
4
4
|
require('@ops-ai/toggly-eval');
|
|
5
|
+
var togglySignedDefs = require('@ops-ai/toggly-signed-defs');
|
|
5
6
|
var react = require('react');
|
|
6
7
|
var togglyClientTelemetry = require('@ops-ai/toggly-client-telemetry');
|
|
7
8
|
var browser = require('@ops-ai/toggly-client-telemetry/browser');
|
|
@@ -52,12 +53,18 @@ function mergeConfig(config) {
|
|
|
52
53
|
};
|
|
53
54
|
}
|
|
54
55
|
function buildDefinitionsUrl(config, context) {
|
|
55
|
-
const { baseUrl, appKey, environment, groups, claims, evaluationMode } = mergeConfig(config);
|
|
56
|
+
const { baseUrl, appKey, environment, groups, claims, evaluationMode, enableVariants } = mergeConfig(config);
|
|
56
57
|
if (!appKey) {
|
|
57
58
|
throw new Error("appKey is required");
|
|
58
59
|
}
|
|
59
60
|
const mode = evaluationMode ?? "remote";
|
|
60
|
-
const
|
|
61
|
+
const useVariants = mode !== "local" && enableVariants === true;
|
|
62
|
+
let pathSegment = "evaluated-signed";
|
|
63
|
+
if (mode === "local") {
|
|
64
|
+
pathSegment = "definitions-signed";
|
|
65
|
+
} else if (useVariants) {
|
|
66
|
+
pathSegment = "evaluated-variants-signed";
|
|
67
|
+
}
|
|
61
68
|
const url = new URL(`${baseUrl}/${pathSegment}/${appKey}/${environment}`);
|
|
62
69
|
if (mode === "local") {
|
|
63
70
|
return url.toString();
|
|
@@ -70,10 +77,25 @@ function buildDefinitionsUrl(config, context) {
|
|
|
70
77
|
groups: fromParam?.groups ?? groups,
|
|
71
78
|
claims: fromParam?.claims ?? claims
|
|
72
79
|
},
|
|
73
|
-
"evaluated"
|
|
80
|
+
useVariants ? "variants" : "evaluated"
|
|
74
81
|
);
|
|
75
82
|
return url.toString();
|
|
76
83
|
}
|
|
84
|
+
function parseVariantDefsPayload(parsed) {
|
|
85
|
+
return togglySignedDefs.asVariantDefsRecord(togglySignedDefs.unwrapDefsPayload(parsed));
|
|
86
|
+
}
|
|
87
|
+
function variantDefsToFlags(defs) {
|
|
88
|
+
const out = {};
|
|
89
|
+
for (const key of Object.keys(defs)) {
|
|
90
|
+
out[key] = defs[key]?.enabled === true;
|
|
91
|
+
}
|
|
92
|
+
return out;
|
|
93
|
+
}
|
|
94
|
+
function decodeVariantValue(value, isT) {
|
|
95
|
+
if (value === null || value === void 0) return null;
|
|
96
|
+
if (isT) return isT(value) ? value : null;
|
|
97
|
+
return value;
|
|
98
|
+
}
|
|
77
99
|
function isFeatureEnabled(flags, featureKey, defaultValue = false, entityContext) {
|
|
78
100
|
if (!flags || Object.keys(flags).length === 0) {
|
|
79
101
|
return defaultValue;
|
|
@@ -138,6 +160,12 @@ var HEADERS = {
|
|
|
138
160
|
CACHE_CONTROL: "cache-control"
|
|
139
161
|
};
|
|
140
162
|
var TOGGLY_LOADER_KEY = "__toggly";
|
|
163
|
+
function checkVariantLabel(result) {
|
|
164
|
+
if (typeof result === "string") {
|
|
165
|
+
return result;
|
|
166
|
+
}
|
|
167
|
+
return result ? "enabled" : "disabled";
|
|
168
|
+
}
|
|
141
169
|
function createBrowserTelemetry(config) {
|
|
142
170
|
const enabled = globalThis.window !== void 0 && typeof document !== "undefined" && !!config.appKey?.trim() && config.enableTelemetry !== false;
|
|
143
171
|
const usage = enabled && config.enableUsageTracking !== false;
|
|
@@ -266,13 +294,13 @@ function createBrowserTelemetry(config) {
|
|
|
266
294
|
};
|
|
267
295
|
if (active) {
|
|
268
296
|
const record = getReporter().captureCheck();
|
|
269
|
-
return (key, result) => record(key, result
|
|
297
|
+
return (key, result) => record(key, checkVariantLabel(result));
|
|
270
298
|
}
|
|
271
299
|
const attribution = context;
|
|
272
300
|
const capturedPartition = partition;
|
|
273
|
-
return (key, result) => feature(key, result
|
|
301
|
+
return (key, result) => feature(key, checkVariantLabel(result), 0, attribution, capturedPartition);
|
|
274
302
|
},
|
|
275
|
-
recordCheck: (key, result) => feature(key, result
|
|
303
|
+
recordCheck: (key, result) => feature(key, checkVariantLabel(result), 0),
|
|
276
304
|
activate() {
|
|
277
305
|
if (disposed || active) return;
|
|
278
306
|
active = true;
|
|
@@ -292,7 +320,7 @@ function createBrowserTelemetry(config) {
|
|
|
292
320
|
|
|
293
321
|
// src/client/sdk-identity.ts
|
|
294
322
|
var SDK_ID = "react-router";
|
|
295
|
-
var SDK_VERSION = "1.
|
|
323
|
+
var SDK_VERSION = "1.2.0";
|
|
296
324
|
function appendSdkQueryParams(params) {
|
|
297
325
|
params.set("sdk", SDK_ID);
|
|
298
326
|
params.set("sdkVersion", SDK_VERSION);
|
|
@@ -346,6 +374,7 @@ function TogglyProviderOwner({
|
|
|
346
374
|
claims: { ...mergedConfig?.claims }
|
|
347
375
|
});
|
|
348
376
|
const flagsRef = react.useRef(serverContext?.flags ?? mergedConfig?.featureDefaults ?? {});
|
|
377
|
+
const variantsRef = react.useRef(null);
|
|
349
378
|
const generationRef = react.useRef(0);
|
|
350
379
|
const updateRevisionRef = react.useRef(0);
|
|
351
380
|
const [telemetry] = react.useState(() => createBrowserTelemetry({ ...config, ...contextRef.current }));
|
|
@@ -483,6 +512,14 @@ function TogglyProviderOwner({
|
|
|
483
512
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
484
513
|
}
|
|
485
514
|
const payload = await response.json();
|
|
515
|
+
if (mergedConfig.enableVariants === true) {
|
|
516
|
+
const defs = parseVariantDefsPayload(payload);
|
|
517
|
+
variantsRef.current = defs;
|
|
518
|
+
const newFlags2 = variantDefsToFlags(defs);
|
|
519
|
+
logger.debug(`Fetched ${Object.keys(newFlags2).length} flags (variants).`);
|
|
520
|
+
return newFlags2;
|
|
521
|
+
}
|
|
522
|
+
variantsRef.current = null;
|
|
486
523
|
const newFlags = payload && typeof payload === "object" ? payload : {};
|
|
487
524
|
logger.debug(`Fetched ${Object.keys(newFlags).length} flags.`);
|
|
488
525
|
return newFlags;
|
|
@@ -542,10 +579,34 @@ function TogglyProviderOwner({
|
|
|
542
579
|
},
|
|
543
580
|
[captureEvaluation]
|
|
544
581
|
);
|
|
582
|
+
const getVariant = react.useCallback(
|
|
583
|
+
(featureKey) => {
|
|
584
|
+
if (!mergedConfig?.enableVariants) return null;
|
|
585
|
+
const entry = variantsRef.current?.[featureKey];
|
|
586
|
+
const enabled = togglyLocalGates.applyLocalGate(
|
|
587
|
+
entry?.enabled === true,
|
|
588
|
+
featureKey,
|
|
589
|
+
localGatesRef.current,
|
|
590
|
+
localGateIndexRef.current
|
|
591
|
+
);
|
|
592
|
+
const record = telemetry.captureCheck();
|
|
593
|
+
record(featureKey, enabled && entry?.variant ? entry.variant : "disabled");
|
|
594
|
+
if (!enabled || !entry?.variant) return null;
|
|
595
|
+
return { name: entry.variant, configurationValue: entry.configurationValue };
|
|
596
|
+
},
|
|
597
|
+
[mergedConfig?.enableVariants, localGatesRevision, telemetry, flags]
|
|
598
|
+
);
|
|
599
|
+
const getVariantValue = react.useCallback(
|
|
600
|
+
(featureKey, isT) => {
|
|
601
|
+
return decodeVariantValue(getVariant(featureKey)?.configurationValue, isT);
|
|
602
|
+
},
|
|
603
|
+
[getVariant]
|
|
604
|
+
);
|
|
545
605
|
const installContext = react.useCallback((target) => {
|
|
546
606
|
contextRef.current = target;
|
|
547
607
|
telemetry.setContext(target);
|
|
548
608
|
flagsRef.current = mergedConfig?.featureDefaults ?? {};
|
|
609
|
+
variantsRef.current = null;
|
|
549
610
|
setFlags(flagsRef.current);
|
|
550
611
|
setIdentity(target.identity);
|
|
551
612
|
}, [mergedConfig?.featureDefaults, telemetry]);
|
|
@@ -816,6 +877,8 @@ function TogglyProviderOwner({
|
|
|
816
877
|
isDisabled,
|
|
817
878
|
evaluateGate,
|
|
818
879
|
registerContext: registerContext2,
|
|
880
|
+
getVariant,
|
|
881
|
+
getVariantValue,
|
|
819
882
|
identify,
|
|
820
883
|
reset,
|
|
821
884
|
refresh,
|
|
@@ -834,6 +897,8 @@ function TogglyProviderOwner({
|
|
|
834
897
|
isDisabled,
|
|
835
898
|
evaluateGate,
|
|
836
899
|
registerContext2,
|
|
900
|
+
getVariant,
|
|
901
|
+
getVariantValue,
|
|
837
902
|
identify,
|
|
838
903
|
reset,
|
|
839
904
|
refresh,
|