@ops-ai/react-router-toggly 1.2.1 → 1.5.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/CHANGELOG.md CHANGED
@@ -1,5 +1,38 @@
1
+ ## 1.5.1
2
+
3
+ 2026-10-15
4
+
5
+ ### Fixed
6
+ - After `dispose()`, `use()` and `captureCheck()` no longer create an
7
+ unmanaged reporter or attach leftover lifecycle listeners [OPS-1758].
8
+
9
+ ## 1.5.0
10
+
11
+ 2026-10-11
12
+
13
+ ### Added
14
+ - `use()` for timed feature usage scopes (attempt, duration, errors) via `@ops-ai/toggly-client-telemetry` [OPS-1758].
15
+
1
16
  # Changelog
2
17
 
18
+ ## [1.4.0] - 2026-10-11
19
+
20
+ ### Added
21
+ - Usage scope metrics on `VariantStats` (`errorCount`, `durationSumMs`,
22
+ `errorDurationSumMs`) and batcher APIs `recordUsageError`, `recordUsageDuration`,
23
+ `recordUsageScopeOutcome` (proto fields 5–7).
24
+
25
+ ## [1.3.1] - 2026-10-11
26
+
27
+ ### Fixed
28
+ - Only call `useRouteLoaderData` when `routeId` is set and use `useInRouterContext` so SSR and non-data-router apps hydrate from `fallbackContext` / `TogglyScript` without invoking data-router hooks [OPS-1756].
29
+
30
+ ## [1.3.0] - 2026-10-04
31
+
32
+ ### Added
33
+ - Server export of `createSegmentMembershipClient` for Backend-key targeting-list updates.
34
+
35
+
3
36
  ## [1.2.1] - 2026-09-27
4
37
 
5
38
  ### Fixed
package/dist/client.cjs CHANGED
@@ -186,6 +186,7 @@ function createBrowserTelemetry(config) {
186
186
  }
187
187
  };
188
188
  const getReporter = () => {
189
+ if (disposed) return void 0;
189
190
  if (!reporter) {
190
191
  reporter = togglyClientTelemetry.createTelemetryReporter({
191
192
  appKey: config.appKey,
@@ -207,6 +208,7 @@ function createBrowserTelemetry(config) {
207
208
  };
208
209
  function replay(entry) {
209
210
  const target = getReporter();
211
+ if (!target) return;
210
212
  target.setContext(entry.context);
211
213
  if (entry.kind === "feature") {
212
214
  const methods = ["recordCheck", "recordUsage", "recordView"];
@@ -274,6 +276,28 @@ function createBrowserTelemetry(config) {
274
276
  }
275
277
  const api = {
276
278
  recordUsage: (key, variant = "enabled") => feature(key, variant, 1),
279
+ use: (key, fn, variant = "enabled") => {
280
+ if (!usage || disposed) {
281
+ return togglyClientTelemetry.runFeatureUsageScope(
282
+ { recordAttempt: () => {
283
+ }, recordSuccessDuration: () => {
284
+ }, recordFailure: () => {
285
+ } },
286
+ fn
287
+ );
288
+ }
289
+ const target = getReporter();
290
+ if (!target) {
291
+ return togglyClientTelemetry.runFeatureUsageScope(
292
+ { recordAttempt: () => {
293
+ }, recordSuccessDuration: () => {
294
+ }, recordFailure: () => {
295
+ } },
296
+ fn
297
+ );
298
+ }
299
+ return target.use(key, fn, variant);
300
+ },
277
301
  recordView: (key, variant = "enabled") => feature(key, variant, 2),
278
302
  incrementCounter: (key, value = 1) => event({ partition, context, key, kind: "counter", values: [value] }),
279
303
  setGauge: (key, value) => event({ partition, context, key, kind: "gauge", values: [value] }),
@@ -293,7 +317,10 @@ function createBrowserTelemetry(config) {
293
317
  if (disposed || !usage) return () => {
294
318
  };
295
319
  if (active) {
296
- const record = getReporter().captureCheck();
320
+ const target = getReporter();
321
+ if (!target) return () => {
322
+ };
323
+ const record = target.captureCheck();
297
324
  return (key, result) => record(key, checkVariantLabel(result));
298
325
  }
299
326
  const attribution = context;
@@ -320,7 +347,7 @@ function createBrowserTelemetry(config) {
320
347
 
321
348
  // src/client/sdk-identity.ts
322
349
  var SDK_ID = "react-router";
323
- var SDK_VERSION = "1.2.1";
350
+ var SDK_VERSION = "1.5.0";
324
351
  function appendSdkQueryParams(params) {
325
352
  params.set("sdk", SDK_ID);
326
353
  params.set("sdkVersion", SDK_VERSION);
@@ -1053,32 +1080,97 @@ function FeatureGate({
1053
1080
  const enabled = useFeatureGate(featureKeys, requirement, negate, context, contextKind);
1054
1081
  return enabled ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children }) : null;
1055
1082
  }
1056
- function RouterTogglyProvider({
1083
+ function resolveServerContext(data, fallbackContext) {
1084
+ const serverContext = data?.[TOGGLY_LOADER_KEY];
1085
+ return serverContext ?? fallbackContext;
1086
+ }
1087
+ function buildMergedConfig(config, serverContext) {
1088
+ return config ?? (serverContext ? {
1089
+ appKey: serverContext.appKey,
1090
+ environment: serverContext.environment
1091
+ } : void 0);
1092
+ }
1093
+ function RouterTogglyProviderInner({
1094
+ children,
1095
+ serverContext,
1096
+ config,
1097
+ ...props
1098
+ }) {
1099
+ const mergedConfig = buildMergedConfig(config, serverContext);
1100
+ return /* @__PURE__ */ jsxRuntime.jsx(TogglyProvider, { serverContext, config: mergedConfig, ...props, children });
1101
+ }
1102
+ function RouterTogglyProviderWithData({
1103
+ children,
1104
+ data,
1105
+ fallbackContext,
1106
+ config,
1107
+ ...props
1108
+ }) {
1109
+ const serverContext = resolveServerContext(data, fallbackContext);
1110
+ return /* @__PURE__ */ jsxRuntime.jsx(RouterTogglyProviderInner, { serverContext, config, ...props, children });
1111
+ }
1112
+ function RouterTogglyProviderFromRoute({
1057
1113
  children,
1058
1114
  routeId,
1059
1115
  fallbackContext,
1060
1116
  config,
1061
1117
  ...props
1062
1118
  }) {
1063
- const routeData = reactRouter.useRouteLoaderData(routeId ?? "");
1119
+ const routeData = reactRouter.useRouteLoaderData(routeId);
1120
+ return /* @__PURE__ */ jsxRuntime.jsx(
1121
+ RouterTogglyProviderWithData,
1122
+ {
1123
+ data: routeData,
1124
+ fallbackContext,
1125
+ config,
1126
+ ...props,
1127
+ children
1128
+ }
1129
+ );
1130
+ }
1131
+ function RouterTogglyProviderFromLoader({
1132
+ children,
1133
+ fallbackContext,
1134
+ config,
1135
+ ...props
1136
+ }) {
1064
1137
  const loaderData = reactRouter.useLoaderData();
1065
- const data = routeId ? routeData : loaderData;
1066
- const serverContext = data?.[TOGGLY_LOADER_KEY];
1067
- const resolvedServerContext = serverContext ?? fallbackContext;
1068
- const mergedConfig = config ?? (resolvedServerContext ? {
1069
- appKey: resolvedServerContext.appKey,
1070
- environment: resolvedServerContext.environment
1071
- } : void 0);
1072
1138
  return /* @__PURE__ */ jsxRuntime.jsx(
1073
- TogglyProvider,
1139
+ RouterTogglyProviderWithData,
1074
1140
  {
1075
- serverContext: resolvedServerContext,
1076
- config: mergedConfig,
1141
+ data: loaderData,
1142
+ fallbackContext,
1143
+ config,
1077
1144
  ...props,
1078
1145
  children
1079
1146
  }
1080
1147
  );
1081
1148
  }
1149
+ function RouterTogglyProviderStatic({
1150
+ children,
1151
+ fallbackContext,
1152
+ config,
1153
+ ...props
1154
+ }) {
1155
+ const serverContext = fallbackContext ?? getWindowTogglyData();
1156
+ return /* @__PURE__ */ jsxRuntime.jsx(RouterTogglyProviderInner, { serverContext, config, ...props, children });
1157
+ }
1158
+ function RouterTogglyProvider({
1159
+ children,
1160
+ routeId,
1161
+ fallbackContext,
1162
+ config,
1163
+ ...props
1164
+ }) {
1165
+ const inRouter = reactRouter.useInRouterContext();
1166
+ if (routeId && inRouter) {
1167
+ return /* @__PURE__ */ jsxRuntime.jsx(RouterTogglyProviderFromRoute, { routeId, fallbackContext, config, ...props, children });
1168
+ }
1169
+ if (inRouter) {
1170
+ return /* @__PURE__ */ jsxRuntime.jsx(RouterTogglyProviderFromLoader, { fallbackContext, config, ...props, children });
1171
+ }
1172
+ return /* @__PURE__ */ jsxRuntime.jsx(RouterTogglyProviderStatic, { fallbackContext, config, ...props, children });
1173
+ }
1082
1174
  function useTogglyLoaderData() {
1083
1175
  const loaderData = reactRouter.useLoaderData();
1084
1176
  return {