@ops-ai/react-router-toggly 1.2.0 → 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/dist/client.d.cts CHANGED
@@ -3,6 +3,8 @@ import { EvaluatedDefinitions, TogglyEntityContext } from '@ops-ai/toggly-hooks-
3
3
  export { TogglyEntityContext, normalizeEntityContext, registerContext } from '@ops-ai/toggly-hooks-types';
4
4
  import * as react from 'react';
5
5
  import { ReactNode, ReactElement } from 'react';
6
+ import { FeatureUsageScope } from '@ops-ai/toggly-client-telemetry';
7
+ export { FeatureUsageScope } from '@ops-ai/toggly-client-telemetry';
6
8
 
7
9
  interface UsageSender {
8
10
  sendStats(request: Record<string, unknown>): Promise<unknown>;
@@ -267,6 +269,7 @@ declare const TOGGLY_LOADER_KEY: "__toggly";
267
269
 
268
270
  interface FrontendTelemetry {
269
271
  recordUsage(featureKey: string, variant?: string): void;
272
+ use: <T>(featureKey: string, fn: (scope: FeatureUsageScope) => Promise<T>, variant?: string) => Promise<T>;
270
273
  recordView(featureKey: string, variant?: string): void;
271
274
  incrementCounter(metricKey: string, value?: number): void;
272
275
  setGauge(metricKey: string, value: number): void;
@@ -569,8 +572,8 @@ declare function FeatureGate({ featureKeys, requirement, negate, children, conte
569
572
  /**
570
573
  * React Router-specific utilities for Toggly
571
574
  */
572
- type SerializeFrom<T> = T;
573
575
 
576
+ type SerializeFrom<T> = T;
574
577
  /**
575
578
  * Props for RouterTogglyProvider
576
579
  */
@@ -593,7 +596,7 @@ interface RouterTogglyProviderProps extends Omit<TogglyProviderProps, 'serverCon
593
596
  * );
594
597
  * }
595
598
  */
596
- declare function RouterTogglyProvider({ children, routeId, fallbackContext, config, ...props }: RouterTogglyProviderProps): ReactElement;
599
+ declare function RouterTogglyProvider({ children, routeId, fallbackContext, config, ...props }: Readonly<RouterTogglyProviderProps>): ReactElement;
597
600
  /**
598
601
  * Hook to get Toggly context from loader data
599
602
  */
package/dist/client.d.ts CHANGED
@@ -3,6 +3,8 @@ import { EvaluatedDefinitions, TogglyEntityContext } from '@ops-ai/toggly-hooks-
3
3
  export { TogglyEntityContext, normalizeEntityContext, registerContext } from '@ops-ai/toggly-hooks-types';
4
4
  import * as react from 'react';
5
5
  import { ReactNode, ReactElement } from 'react';
6
+ import { FeatureUsageScope } from '@ops-ai/toggly-client-telemetry';
7
+ export { FeatureUsageScope } from '@ops-ai/toggly-client-telemetry';
6
8
 
7
9
  interface UsageSender {
8
10
  sendStats(request: Record<string, unknown>): Promise<unknown>;
@@ -267,6 +269,7 @@ declare const TOGGLY_LOADER_KEY: "__toggly";
267
269
 
268
270
  interface FrontendTelemetry {
269
271
  recordUsage(featureKey: string, variant?: string): void;
272
+ use: <T>(featureKey: string, fn: (scope: FeatureUsageScope) => Promise<T>, variant?: string) => Promise<T>;
270
273
  recordView(featureKey: string, variant?: string): void;
271
274
  incrementCounter(metricKey: string, value?: number): void;
272
275
  setGauge(metricKey: string, value: number): void;
@@ -569,8 +572,8 @@ declare function FeatureGate({ featureKeys, requirement, negate, children, conte
569
572
  /**
570
573
  * React Router-specific utilities for Toggly
571
574
  */
572
- type SerializeFrom<T> = T;
573
575
 
576
+ type SerializeFrom<T> = T;
574
577
  /**
575
578
  * Props for RouterTogglyProvider
576
579
  */
@@ -593,7 +596,7 @@ interface RouterTogglyProviderProps extends Omit<TogglyProviderProps, 'serverCon
593
596
  * );
594
597
  * }
595
598
  */
596
- declare function RouterTogglyProvider({ children, routeId, fallbackContext, config, ...props }: RouterTogglyProviderProps): ReactElement;
599
+ declare function RouterTogglyProvider({ children, routeId, fallbackContext, config, ...props }: Readonly<RouterTogglyProviderProps>): ReactElement;
597
600
  /**
598
601
  * Hook to get Toggly context from loader data
599
602
  */
package/dist/client.js CHANGED
@@ -3,11 +3,11 @@ export { normalizeEntityContext, registerContext } from '@ops-ai/toggly-hooks-ty
3
3
  import '@ops-ai/toggly-eval';
4
4
  import { asVariantDefsRecord, unwrapDefsPayload } from '@ops-ai/toggly-signed-defs';
5
5
  import { createContext, useRef, useMemo, useState, useEffect, useCallback, useContext, useLayoutEffect } from 'react';
6
- import { createTelemetryReporter } from '@ops-ai/toggly-client-telemetry';
6
+ import { runFeatureUsageScope, createTelemetryReporter } from '@ops-ai/toggly-client-telemetry';
7
7
  import { attachBrowserLifecycle } from '@ops-ai/toggly-client-telemetry/browser';
8
8
  import { buildFlagGateIndex, applyLocalGate } from '@ops-ai/toggly-local-gates';
9
9
  import { jsx, Fragment } from 'react/jsx-runtime';
10
- import { useRouteLoaderData, useLoaderData } from 'react-router';
10
+ import { useInRouterContext, useLoaderData, useRouteLoaderData } from 'react-router';
11
11
 
12
12
  // src/core/index.ts
13
13
 
@@ -185,6 +185,7 @@ function createBrowserTelemetry(config) {
185
185
  }
186
186
  };
187
187
  const getReporter = () => {
188
+ if (disposed) return void 0;
188
189
  if (!reporter) {
189
190
  reporter = createTelemetryReporter({
190
191
  appKey: config.appKey,
@@ -206,6 +207,7 @@ function createBrowserTelemetry(config) {
206
207
  };
207
208
  function replay(entry) {
208
209
  const target = getReporter();
210
+ if (!target) return;
209
211
  target.setContext(entry.context);
210
212
  if (entry.kind === "feature") {
211
213
  const methods = ["recordCheck", "recordUsage", "recordView"];
@@ -273,6 +275,28 @@ function createBrowserTelemetry(config) {
273
275
  }
274
276
  const api = {
275
277
  recordUsage: (key, variant = "enabled") => feature(key, variant, 1),
278
+ use: (key, fn, variant = "enabled") => {
279
+ if (!usage || disposed) {
280
+ return runFeatureUsageScope(
281
+ { recordAttempt: () => {
282
+ }, recordSuccessDuration: () => {
283
+ }, recordFailure: () => {
284
+ } },
285
+ fn
286
+ );
287
+ }
288
+ const target = getReporter();
289
+ if (!target) {
290
+ return runFeatureUsageScope(
291
+ { recordAttempt: () => {
292
+ }, recordSuccessDuration: () => {
293
+ }, recordFailure: () => {
294
+ } },
295
+ fn
296
+ );
297
+ }
298
+ return target.use(key, fn, variant);
299
+ },
276
300
  recordView: (key, variant = "enabled") => feature(key, variant, 2),
277
301
  incrementCounter: (key, value = 1) => event({ partition, context, key, kind: "counter", values: [value] }),
278
302
  setGauge: (key, value) => event({ partition, context, key, kind: "gauge", values: [value] }),
@@ -292,7 +316,10 @@ function createBrowserTelemetry(config) {
292
316
  if (disposed || !usage) return () => {
293
317
  };
294
318
  if (active) {
295
- const record = getReporter().captureCheck();
319
+ const target = getReporter();
320
+ if (!target) return () => {
321
+ };
322
+ const record = target.captureCheck();
296
323
  return (key, result) => record(key, checkVariantLabel(result));
297
324
  }
298
325
  const attribution = context;
@@ -319,7 +346,7 @@ function createBrowserTelemetry(config) {
319
346
 
320
347
  // src/client/sdk-identity.ts
321
348
  var SDK_ID = "react-router";
322
- var SDK_VERSION = "1.2.0";
349
+ var SDK_VERSION = "1.5.0";
323
350
  function appendSdkQueryParams(params) {
324
351
  params.set("sdk", SDK_ID);
325
352
  params.set("sdkVersion", SDK_VERSION);
@@ -1052,40 +1079,97 @@ function FeatureGate({
1052
1079
  const enabled = useFeatureGate(featureKeys, requirement, negate, context, contextKind);
1053
1080
  return enabled ? /* @__PURE__ */ jsx(Fragment, { children }) : null;
1054
1081
  }
1055
- function RouterTogglyProvider({
1082
+ function resolveServerContext(data, fallbackContext) {
1083
+ const serverContext = data?.[TOGGLY_LOADER_KEY];
1084
+ return serverContext ?? fallbackContext;
1085
+ }
1086
+ function buildMergedConfig(config, serverContext) {
1087
+ return config ?? (serverContext ? {
1088
+ appKey: serverContext.appKey,
1089
+ environment: serverContext.environment
1090
+ } : void 0);
1091
+ }
1092
+ function RouterTogglyProviderInner({
1093
+ children,
1094
+ serverContext,
1095
+ config,
1096
+ ...props
1097
+ }) {
1098
+ const mergedConfig = buildMergedConfig(config, serverContext);
1099
+ return /* @__PURE__ */ jsx(TogglyProvider, { serverContext, config: mergedConfig, ...props, children });
1100
+ }
1101
+ function RouterTogglyProviderWithData({
1102
+ children,
1103
+ data,
1104
+ fallbackContext,
1105
+ config,
1106
+ ...props
1107
+ }) {
1108
+ const serverContext = resolveServerContext(data, fallbackContext);
1109
+ return /* @__PURE__ */ jsx(RouterTogglyProviderInner, { serverContext, config, ...props, children });
1110
+ }
1111
+ function RouterTogglyProviderFromRoute({
1056
1112
  children,
1057
1113
  routeId,
1058
1114
  fallbackContext,
1059
1115
  config,
1060
1116
  ...props
1061
1117
  }) {
1062
- let serverContext;
1063
- try {
1064
- if (routeId) {
1065
- const routeData = useRouteLoaderData(routeId);
1066
- serverContext = routeData?.[TOGGLY_LOADER_KEY];
1067
- } else {
1068
- const loaderData = useLoaderData();
1069
- serverContext = loaderData?.[TOGGLY_LOADER_KEY];
1118
+ const routeData = useRouteLoaderData(routeId);
1119
+ return /* @__PURE__ */ jsx(
1120
+ RouterTogglyProviderWithData,
1121
+ {
1122
+ data: routeData,
1123
+ fallbackContext,
1124
+ config,
1125
+ ...props,
1126
+ children
1070
1127
  }
1071
- } catch {
1072
- serverContext = fallbackContext;
1073
- }
1074
- serverContext = serverContext ?? fallbackContext;
1075
- const mergedConfig = config ?? (serverContext ? {
1076
- appKey: serverContext.appKey,
1077
- environment: serverContext.environment
1078
- } : void 0);
1128
+ );
1129
+ }
1130
+ function RouterTogglyProviderFromLoader({
1131
+ children,
1132
+ fallbackContext,
1133
+ config,
1134
+ ...props
1135
+ }) {
1136
+ const loaderData = useLoaderData();
1079
1137
  return /* @__PURE__ */ jsx(
1080
- TogglyProvider,
1138
+ RouterTogglyProviderWithData,
1081
1139
  {
1082
- serverContext,
1083
- config: mergedConfig,
1140
+ data: loaderData,
1141
+ fallbackContext,
1142
+ config,
1084
1143
  ...props,
1085
1144
  children
1086
1145
  }
1087
1146
  );
1088
1147
  }
1148
+ function RouterTogglyProviderStatic({
1149
+ children,
1150
+ fallbackContext,
1151
+ config,
1152
+ ...props
1153
+ }) {
1154
+ const serverContext = fallbackContext ?? getWindowTogglyData();
1155
+ return /* @__PURE__ */ jsx(RouterTogglyProviderInner, { serverContext, config, ...props, children });
1156
+ }
1157
+ function RouterTogglyProvider({
1158
+ children,
1159
+ routeId,
1160
+ fallbackContext,
1161
+ config,
1162
+ ...props
1163
+ }) {
1164
+ const inRouter = useInRouterContext();
1165
+ if (routeId && inRouter) {
1166
+ return /* @__PURE__ */ jsx(RouterTogglyProviderFromRoute, { routeId, fallbackContext, config, ...props, children });
1167
+ }
1168
+ if (inRouter) {
1169
+ return /* @__PURE__ */ jsx(RouterTogglyProviderFromLoader, { fallbackContext, config, ...props, children });
1170
+ }
1171
+ return /* @__PURE__ */ jsx(RouterTogglyProviderStatic, { fallbackContext, config, ...props, children });
1172
+ }
1089
1173
  function useTogglyLoaderData() {
1090
1174
  const loaderData = useLoaderData();
1091
1175
  return {