@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/CHANGELOG.md +54 -0
- package/README.md +1 -1
- package/dist/client.cjs +114 -15
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +5 -2
- package/dist/client.d.ts +5 -2
- package/dist/client.js +116 -17
- package/dist/client.js.map +1 -1
- package/dist/server.cjs +50 -4
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +1 -0
- package/dist/server.d.ts +1 -0
- package/dist/server.js +42 -4
- 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 +24 -23
- package/proto/usage.proto +3 -0
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 {
|
|
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
|
|
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.
|
|
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
|
|
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
|
|
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
|
-
|
|
1145
|
+
RouterTogglyProviderWithData,
|
|
1073
1146
|
{
|
|
1074
|
-
|
|
1075
|
-
|
|
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 {
|