@traffical/react 0.1.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/README.md +1011 -0
- package/dist/context.d.ts +90 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +23 -0
- package/dist/context.js.map +1 -0
- package/dist/hooks.d.ts +273 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +424 -0
- package/dist/hooks.js.map +1 -0
- package/dist/index.d.ts +48 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +58 -0
- package/dist/index.js.map +1 -0
- package/dist/provider.d.ts +48 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +144 -0
- package/dist/provider.js.map +1 -0
- package/package.json +55 -0
- package/src/context.ts +144 -0
- package/src/hooks.ts +656 -0
- package/src/index.ts +93 -0
- package/src/provider.tsx +194 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @traffical/react
|
|
3
|
+
*
|
|
4
|
+
* Traffical SDK for React applications.
|
|
5
|
+
* Provides Provider and hooks for parameter resolution and decision tracking.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Browser-optimized with sendBeacon, localStorage persistence
|
|
9
|
+
* - Automatic stable ID for anonymous users
|
|
10
|
+
* - Plugin system support (DecisionTrackingPlugin enabled by default)
|
|
11
|
+
* - Decision and exposure deduplication
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```tsx
|
|
15
|
+
* import { TrafficalProvider, useTraffical } from '@traffical/react';
|
|
16
|
+
*
|
|
17
|
+
* function App() {
|
|
18
|
+
* return (
|
|
19
|
+
* <TrafficalProvider
|
|
20
|
+
* config={{
|
|
21
|
+
* orgId: 'org_123',
|
|
22
|
+
* projectId: 'proj_456',
|
|
23
|
+
* env: 'production',
|
|
24
|
+
* apiKey: 'pk_...',
|
|
25
|
+
* }}
|
|
26
|
+
* >
|
|
27
|
+
* <MyComponent />
|
|
28
|
+
* </TrafficalProvider>
|
|
29
|
+
* );
|
|
30
|
+
* }
|
|
31
|
+
*
|
|
32
|
+
* function MyComponent() {
|
|
33
|
+
* const { params, ready } = useTraffical({
|
|
34
|
+
* defaults: { 'ui.hero.title': 'Welcome' },
|
|
35
|
+
* });
|
|
36
|
+
*
|
|
37
|
+
* if (!ready) return <div>Loading...</div>;
|
|
38
|
+
* return <h1>{params['ui.hero.title']}</h1>;
|
|
39
|
+
* }
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
// Re-export everything from core
|
|
44
|
+
export * from "@traffical/core";
|
|
45
|
+
|
|
46
|
+
// Re-export client and utilities from JS Client
|
|
47
|
+
export {
|
|
48
|
+
TrafficalClient,
|
|
49
|
+
createTrafficalClient,
|
|
50
|
+
createTrafficalClientSync,
|
|
51
|
+
type TrafficalClientOptions,
|
|
52
|
+
} from "@traffical/js-client";
|
|
53
|
+
|
|
54
|
+
// Re-export plugin utilities from JS Client
|
|
55
|
+
export {
|
|
56
|
+
type TrafficalPlugin,
|
|
57
|
+
type PluginOptions,
|
|
58
|
+
createDOMBindingPlugin,
|
|
59
|
+
type DOMBindingPlugin,
|
|
60
|
+
type DOMBindingPluginOptions,
|
|
61
|
+
} from "@traffical/js-client";
|
|
62
|
+
|
|
63
|
+
// Export React-specific components and hooks
|
|
64
|
+
export { TrafficalProvider, type TrafficalProviderProps } from "./provider.js";
|
|
65
|
+
|
|
66
|
+
export {
|
|
67
|
+
TrafficalContext,
|
|
68
|
+
useTrafficalContext,
|
|
69
|
+
type TrafficalProviderConfig,
|
|
70
|
+
type TrafficalContextValue,
|
|
71
|
+
} from "./context.js";
|
|
72
|
+
|
|
73
|
+
export {
|
|
74
|
+
// Primary hook
|
|
75
|
+
useTraffical,
|
|
76
|
+
type UseTrafficalOptions,
|
|
77
|
+
type UseTrafficalResult,
|
|
78
|
+
type BoundTrackOptions,
|
|
79
|
+
// Track hook
|
|
80
|
+
useTrafficalTrack,
|
|
81
|
+
// Other hooks
|
|
82
|
+
useTrafficalPlugin,
|
|
83
|
+
useTrafficalClient,
|
|
84
|
+
// Deprecated (kept for backward compatibility)
|
|
85
|
+
type BoundTrackRewardOptions,
|
|
86
|
+
useTrafficalReward,
|
|
87
|
+
useTrafficalParams,
|
|
88
|
+
useTrafficalDecision,
|
|
89
|
+
type UseTrafficalParamsOptions,
|
|
90
|
+
type UseTrafficalParamsResult,
|
|
91
|
+
type UseTrafficalDecisionOptions,
|
|
92
|
+
type UseTrafficalDecisionResult,
|
|
93
|
+
} from "./hooks.js";
|
package/src/provider.tsx
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Traffical React Provider
|
|
3
|
+
*
|
|
4
|
+
* Initializes the Traffical client (browser-optimized) and provides it to child components.
|
|
5
|
+
* Supports plugins, automatic stable ID, and decision tracking out of the box.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React, {
|
|
9
|
+
useEffect,
|
|
10
|
+
useState,
|
|
11
|
+
useCallback,
|
|
12
|
+
useMemo,
|
|
13
|
+
useRef,
|
|
14
|
+
type ReactNode,
|
|
15
|
+
} from "react";
|
|
16
|
+
import {
|
|
17
|
+
TrafficalClient,
|
|
18
|
+
createTrafficalClientSync,
|
|
19
|
+
} from "@traffical/js-client";
|
|
20
|
+
import type { Context } from "@traffical/core";
|
|
21
|
+
import {
|
|
22
|
+
TrafficalContext,
|
|
23
|
+
type TrafficalProviderConfig,
|
|
24
|
+
type TrafficalContextValue,
|
|
25
|
+
} from "./context.js";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Props for the TrafficalProvider component.
|
|
29
|
+
*/
|
|
30
|
+
export interface TrafficalProviderProps {
|
|
31
|
+
/** Configuration for the Traffical client */
|
|
32
|
+
config: TrafficalProviderConfig;
|
|
33
|
+
/** Child components */
|
|
34
|
+
children: ReactNode;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* TrafficalProvider - initializes and provides the Traffical client to React components.
|
|
39
|
+
*
|
|
40
|
+
* Features:
|
|
41
|
+
* - Browser-optimized with sendBeacon, localStorage persistence
|
|
42
|
+
* - Automatic stable ID for anonymous users (unless unitKeyFn provided)
|
|
43
|
+
* - Plugin system support (DecisionTrackingPlugin enabled by default)
|
|
44
|
+
* - Decision and exposure deduplication
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```tsx
|
|
48
|
+
* <TrafficalProvider
|
|
49
|
+
* config={{
|
|
50
|
+
* orgId: "org_123",
|
|
51
|
+
* projectId: "proj_456",
|
|
52
|
+
* env: "production",
|
|
53
|
+
* apiKey: "pk_...",
|
|
54
|
+
* // Optional: provide unitKeyFn for logged-in users
|
|
55
|
+
* unitKeyFn: () => getUserId(),
|
|
56
|
+
* // Optional: add context
|
|
57
|
+
* contextFn: () => ({ locale: "en-US" }),
|
|
58
|
+
* // Optional: add custom plugins
|
|
59
|
+
* plugins: [createDOMBindingPlugin()],
|
|
60
|
+
* }}
|
|
61
|
+
* >
|
|
62
|
+
* <App />
|
|
63
|
+
* </TrafficalProvider>
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
export function TrafficalProvider({
|
|
67
|
+
config,
|
|
68
|
+
children,
|
|
69
|
+
}: TrafficalProviderProps): React.ReactElement {
|
|
70
|
+
const [client, setClient] = useState<TrafficalClient | null>(null);
|
|
71
|
+
const [ready, setReady] = useState(false);
|
|
72
|
+
const [error, setError] = useState<Error | null>(null);
|
|
73
|
+
|
|
74
|
+
// Keep a ref to the client for cleanup
|
|
75
|
+
const clientRef = useRef<TrafficalClient | null>(null);
|
|
76
|
+
|
|
77
|
+
// Memoize the unit key function
|
|
78
|
+
// If no unitKeyFn is provided, use the client's stable ID
|
|
79
|
+
const getUnitKey = useCallback(() => {
|
|
80
|
+
if (config.unitKeyFn) {
|
|
81
|
+
return config.unitKeyFn();
|
|
82
|
+
}
|
|
83
|
+
// Fall back to the client's auto-generated stable ID
|
|
84
|
+
return clientRef.current?.getStableId() ?? "";
|
|
85
|
+
}, [config.unitKeyFn]);
|
|
86
|
+
|
|
87
|
+
const getContext = useCallback((): Context => {
|
|
88
|
+
const unitKey = getUnitKey();
|
|
89
|
+
const additionalContext = config.contextFn?.() ?? {};
|
|
90
|
+
|
|
91
|
+
// The unit key field name comes from the bundle's hashing config
|
|
92
|
+
// For now, we use common conventions and let the SDK handle it
|
|
93
|
+
return {
|
|
94
|
+
...additionalContext,
|
|
95
|
+
// Include common unit key field names
|
|
96
|
+
userId: unitKey,
|
|
97
|
+
deviceId: unitKey,
|
|
98
|
+
anonymousId: unitKey,
|
|
99
|
+
};
|
|
100
|
+
}, [getUnitKey, config.contextFn]);
|
|
101
|
+
|
|
102
|
+
// Initialize client on mount
|
|
103
|
+
useEffect(() => {
|
|
104
|
+
let mounted = true;
|
|
105
|
+
|
|
106
|
+
const initClient = async () => {
|
|
107
|
+
try {
|
|
108
|
+
// Create client synchronously so it's available immediately
|
|
109
|
+
const newClient = createTrafficalClientSync({
|
|
110
|
+
orgId: config.orgId,
|
|
111
|
+
projectId: config.projectId,
|
|
112
|
+
env: config.env,
|
|
113
|
+
apiKey: config.apiKey,
|
|
114
|
+
baseUrl: config.baseUrl,
|
|
115
|
+
localConfig: config.localConfig,
|
|
116
|
+
refreshIntervalMs: config.refreshIntervalMs,
|
|
117
|
+
trackDecisions: config.trackDecisions,
|
|
118
|
+
decisionDeduplicationTtlMs: config.decisionDeduplicationTtlMs,
|
|
119
|
+
exposureSessionTtlMs: config.exposureSessionTtlMs,
|
|
120
|
+
eventBatchSize: config.eventBatchSize,
|
|
121
|
+
eventFlushIntervalMs: config.eventFlushIntervalMs,
|
|
122
|
+
plugins: config.plugins,
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
clientRef.current = newClient;
|
|
126
|
+
|
|
127
|
+
if (mounted) {
|
|
128
|
+
setClient(newClient);
|
|
129
|
+
// If localConfig was provided, mark as ready immediately
|
|
130
|
+
if (config.localConfig) {
|
|
131
|
+
setReady(true);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Initialize asynchronously (fetches/refreshes config bundle)
|
|
136
|
+
await newClient.initialize();
|
|
137
|
+
|
|
138
|
+
if (mounted) {
|
|
139
|
+
setReady(true);
|
|
140
|
+
}
|
|
141
|
+
} catch (err) {
|
|
142
|
+
if (mounted) {
|
|
143
|
+
setError(
|
|
144
|
+
err instanceof Error ? err : new Error(String(err))
|
|
145
|
+
);
|
|
146
|
+
// Still mark as ready - we'll use defaults
|
|
147
|
+
setReady(true);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
initClient();
|
|
153
|
+
|
|
154
|
+
return () => {
|
|
155
|
+
mounted = false;
|
|
156
|
+
clientRef.current?.destroy();
|
|
157
|
+
clientRef.current = null;
|
|
158
|
+
};
|
|
159
|
+
}, [
|
|
160
|
+
config.orgId,
|
|
161
|
+
config.projectId,
|
|
162
|
+
config.env,
|
|
163
|
+
config.apiKey,
|
|
164
|
+
config.baseUrl,
|
|
165
|
+
config.localConfig,
|
|
166
|
+
config.refreshIntervalMs,
|
|
167
|
+
config.trackDecisions,
|
|
168
|
+
config.decisionDeduplicationTtlMs,
|
|
169
|
+
config.exposureSessionTtlMs,
|
|
170
|
+
config.eventBatchSize,
|
|
171
|
+
config.eventFlushIntervalMs,
|
|
172
|
+
config.plugins,
|
|
173
|
+
]);
|
|
174
|
+
|
|
175
|
+
// Memoize context value
|
|
176
|
+
const contextValue = useMemo<TrafficalContextValue>(
|
|
177
|
+
() => ({
|
|
178
|
+
client,
|
|
179
|
+
ready,
|
|
180
|
+
error,
|
|
181
|
+
getUnitKey,
|
|
182
|
+
getContext,
|
|
183
|
+
initialParams: config.initialParams,
|
|
184
|
+
localConfig: config.localConfig,
|
|
185
|
+
}),
|
|
186
|
+
[client, ready, error, getUnitKey, getContext, config.initialParams, config.localConfig]
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
return (
|
|
190
|
+
<TrafficalContext.Provider value={contextValue}>
|
|
191
|
+
{children}
|
|
192
|
+
</TrafficalContext.Provider>
|
|
193
|
+
);
|
|
194
|
+
}
|