@ops-ai/react-router-toggly 1.2.1 → 1.5.2

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
 
@@ -83,6 +83,13 @@ function buildDefinitionsUrl(config, context) {
83
83
  function parseVariantDefsPayload(parsed) {
84
84
  return asVariantDefsRecord(unwrapDefsPayload(parsed));
85
85
  }
86
+ function parseEvaluatedFlagsPayload(parsed) {
87
+ const body = unwrapDefsPayload(parsed);
88
+ if (!body || typeof body !== "object" || Array.isArray(body)) {
89
+ return {};
90
+ }
91
+ return body;
92
+ }
86
93
  function variantDefsToFlags(defs) {
87
94
  const out = {};
88
95
  for (const key of Object.keys(defs)) {
@@ -185,6 +192,7 @@ function createBrowserTelemetry(config) {
185
192
  }
186
193
  };
187
194
  const getReporter = () => {
195
+ if (disposed) return void 0;
188
196
  if (!reporter) {
189
197
  reporter = createTelemetryReporter({
190
198
  appKey: config.appKey,
@@ -206,6 +214,7 @@ function createBrowserTelemetry(config) {
206
214
  };
207
215
  function replay(entry) {
208
216
  const target = getReporter();
217
+ if (!target) return;
209
218
  target.setContext(entry.context);
210
219
  if (entry.kind === "feature") {
211
220
  const methods = ["recordCheck", "recordUsage", "recordView"];
@@ -273,6 +282,28 @@ function createBrowserTelemetry(config) {
273
282
  }
274
283
  const api = {
275
284
  recordUsage: (key, variant = "enabled") => feature(key, variant, 1),
285
+ use: (key, fn, variant = "enabled") => {
286
+ if (!usage || disposed) {
287
+ return runFeatureUsageScope(
288
+ { recordAttempt: () => {
289
+ }, recordSuccessDuration: () => {
290
+ }, recordFailure: () => {
291
+ } },
292
+ fn
293
+ );
294
+ }
295
+ const target = getReporter();
296
+ if (!target) {
297
+ return runFeatureUsageScope(
298
+ { recordAttempt: () => {
299
+ }, recordSuccessDuration: () => {
300
+ }, recordFailure: () => {
301
+ } },
302
+ fn
303
+ );
304
+ }
305
+ return target.use(key, fn, variant);
306
+ },
276
307
  recordView: (key, variant = "enabled") => feature(key, variant, 2),
277
308
  incrementCounter: (key, value = 1) => event({ partition, context, key, kind: "counter", values: [value] }),
278
309
  setGauge: (key, value) => event({ partition, context, key, kind: "gauge", values: [value] }),
@@ -292,7 +323,10 @@ function createBrowserTelemetry(config) {
292
323
  if (disposed || !usage) return () => {
293
324
  };
294
325
  if (active) {
295
- const record = getReporter().captureCheck();
326
+ const target = getReporter();
327
+ if (!target) return () => {
328
+ };
329
+ const record = target.captureCheck();
296
330
  return (key, result) => record(key, checkVariantLabel(result));
297
331
  }
298
332
  const attribution = context;
@@ -319,7 +353,7 @@ function createBrowserTelemetry(config) {
319
353
 
320
354
  // src/client/sdk-identity.ts
321
355
  var SDK_ID = "react-router";
322
- var SDK_VERSION = "1.2.1";
356
+ var SDK_VERSION = "1.5.0";
323
357
  function appendSdkQueryParams(params) {
324
358
  params.set("sdk", SDK_ID);
325
359
  params.set("sdkVersion", SDK_VERSION);
@@ -519,7 +553,7 @@ function TogglyProviderOwner({
519
553
  return newFlags2;
520
554
  }
521
555
  variantsRef.current = null;
522
- const newFlags = payload && typeof payload === "object" ? payload : {};
556
+ const newFlags = parseEvaluatedFlagsPayload(payload);
523
557
  logger.debug(`Fetched ${Object.keys(newFlags).length} flags.`);
524
558
  return newFlags;
525
559
  } catch (error) {
@@ -1052,32 +1086,97 @@ function FeatureGate({
1052
1086
  const enabled = useFeatureGate(featureKeys, requirement, negate, context, contextKind);
1053
1087
  return enabled ? /* @__PURE__ */ jsx(Fragment, { children }) : null;
1054
1088
  }
1055
- function RouterTogglyProvider({
1089
+ function resolveServerContext(data, fallbackContext) {
1090
+ const serverContext = data?.[TOGGLY_LOADER_KEY];
1091
+ return serverContext ?? fallbackContext;
1092
+ }
1093
+ function buildMergedConfig(config, serverContext) {
1094
+ return config ?? (serverContext ? {
1095
+ appKey: serverContext.appKey,
1096
+ environment: serverContext.environment
1097
+ } : void 0);
1098
+ }
1099
+ function RouterTogglyProviderInner({
1100
+ children,
1101
+ serverContext,
1102
+ config,
1103
+ ...props
1104
+ }) {
1105
+ const mergedConfig = buildMergedConfig(config, serverContext);
1106
+ return /* @__PURE__ */ jsx(TogglyProvider, { serverContext, config: mergedConfig, ...props, children });
1107
+ }
1108
+ function RouterTogglyProviderWithData({
1109
+ children,
1110
+ data,
1111
+ fallbackContext,
1112
+ config,
1113
+ ...props
1114
+ }) {
1115
+ const serverContext = resolveServerContext(data, fallbackContext);
1116
+ return /* @__PURE__ */ jsx(RouterTogglyProviderInner, { serverContext, config, ...props, children });
1117
+ }
1118
+ function RouterTogglyProviderFromRoute({
1056
1119
  children,
1057
1120
  routeId,
1058
1121
  fallbackContext,
1059
1122
  config,
1060
1123
  ...props
1061
1124
  }) {
1062
- const routeData = useRouteLoaderData(routeId ?? "");
1125
+ const routeData = useRouteLoaderData(routeId);
1126
+ return /* @__PURE__ */ jsx(
1127
+ RouterTogglyProviderWithData,
1128
+ {
1129
+ data: routeData,
1130
+ fallbackContext,
1131
+ config,
1132
+ ...props,
1133
+ children
1134
+ }
1135
+ );
1136
+ }
1137
+ function RouterTogglyProviderFromLoader({
1138
+ children,
1139
+ fallbackContext,
1140
+ config,
1141
+ ...props
1142
+ }) {
1063
1143
  const loaderData = useLoaderData();
1064
- const data = routeId ? routeData : loaderData;
1065
- const serverContext = data?.[TOGGLY_LOADER_KEY];
1066
- const resolvedServerContext = serverContext ?? fallbackContext;
1067
- const mergedConfig = config ?? (resolvedServerContext ? {
1068
- appKey: resolvedServerContext.appKey,
1069
- environment: resolvedServerContext.environment
1070
- } : void 0);
1071
1144
  return /* @__PURE__ */ jsx(
1072
- TogglyProvider,
1145
+ RouterTogglyProviderWithData,
1073
1146
  {
1074
- serverContext: resolvedServerContext,
1075
- config: mergedConfig,
1147
+ data: loaderData,
1148
+ fallbackContext,
1149
+ config,
1076
1150
  ...props,
1077
1151
  children
1078
1152
  }
1079
1153
  );
1080
1154
  }
1155
+ function RouterTogglyProviderStatic({
1156
+ children,
1157
+ fallbackContext,
1158
+ config,
1159
+ ...props
1160
+ }) {
1161
+ const serverContext = fallbackContext ?? getWindowTogglyData();
1162
+ return /* @__PURE__ */ jsx(RouterTogglyProviderInner, { serverContext, config, ...props, children });
1163
+ }
1164
+ function RouterTogglyProvider({
1165
+ children,
1166
+ routeId,
1167
+ fallbackContext,
1168
+ config,
1169
+ ...props
1170
+ }) {
1171
+ const inRouter = useInRouterContext();
1172
+ if (routeId && inRouter) {
1173
+ return /* @__PURE__ */ jsx(RouterTogglyProviderFromRoute, { routeId, fallbackContext, config, ...props, children });
1174
+ }
1175
+ if (inRouter) {
1176
+ return /* @__PURE__ */ jsx(RouterTogglyProviderFromLoader, { fallbackContext, config, ...props, children });
1177
+ }
1178
+ return /* @__PURE__ */ jsx(RouterTogglyProviderStatic, { fallbackContext, config, ...props, children });
1179
+ }
1081
1180
  function useTogglyLoaderData() {
1082
1181
  const loaderData = useLoaderData();
1083
1182
  return {