@traffical/svelte 0.1.0
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 +365 -0
- package/dist/TrafficalProvider.svelte +34 -0
- package/dist/TrafficalProvider.svelte.d.ts +12 -0
- package/dist/TrafficalProvider.svelte.d.ts.map +1 -0
- package/dist/context.svelte.d.ts +44 -0
- package/dist/context.svelte.d.ts.map +1 -0
- package/dist/context.svelte.js +192 -0
- package/dist/hooks.svelte.d.ts +155 -0
- package/dist/hooks.svelte.d.ts.map +1 -0
- package/dist/hooks.svelte.js +371 -0
- package/dist/index.d.ts +61 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +83 -0
- package/dist/index.test.d.ts +7 -0
- package/dist/index.test.d.ts.map +1 -0
- package/dist/index.test.js +184 -0
- package/dist/sveltekit.d.ts +73 -0
- package/dist/sveltekit.d.ts.map +1 -0
- package/dist/sveltekit.js +111 -0
- package/dist/sveltekit.test.d.ts +5 -0
- package/dist/sveltekit.test.d.ts.map +1 -0
- package/dist/sveltekit.test.js +170 -0
- package/dist/types.d.ts +206 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/package.json +67 -0
- package/src/TrafficalProvider.svelte +34 -0
- package/src/context.svelte.ts +232 -0
- package/src/hooks.svelte.ts +445 -0
- package/src/index.test.ts +221 -0
- package/src/index.ts +144 -0
- package/src/sveltekit.test.ts +218 -0
- package/src/sveltekit.ts +139 -0
- package/src/types.ts +296 -0
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @traffical/svelte - Type Definitions
|
|
3
|
+
*
|
|
4
|
+
* TypeScript types for the Svelte 5 SDK.
|
|
5
|
+
*/
|
|
6
|
+
import type { TrafficalClient, TrafficalPlugin } from "@traffical/js-client";
|
|
7
|
+
import type { ConfigBundle, Context, DecisionResult, ParameterValue } from "@traffical/core";
|
|
8
|
+
/**
|
|
9
|
+
* Configuration for the TrafficalProvider component.
|
|
10
|
+
*/
|
|
11
|
+
export interface TrafficalProviderConfig {
|
|
12
|
+
/** Organization ID */
|
|
13
|
+
orgId: string;
|
|
14
|
+
/** Project ID */
|
|
15
|
+
projectId: string;
|
|
16
|
+
/** Environment (e.g., "production", "staging") */
|
|
17
|
+
env: string;
|
|
18
|
+
/** API key for authentication */
|
|
19
|
+
apiKey: string;
|
|
20
|
+
/** Base URL for the SDK API (edge worker) */
|
|
21
|
+
baseUrl?: string;
|
|
22
|
+
/** Local config bundle for offline fallback */
|
|
23
|
+
localConfig?: ConfigBundle;
|
|
24
|
+
/** Refresh interval in milliseconds (default: 60000) */
|
|
25
|
+
refreshIntervalMs?: number;
|
|
26
|
+
/**
|
|
27
|
+
* Function to get the unit key value.
|
|
28
|
+
* If not provided, the SDK will use automatic stable ID generation.
|
|
29
|
+
*/
|
|
30
|
+
unitKeyFn?: () => string;
|
|
31
|
+
/** Function to get additional context (optional) */
|
|
32
|
+
contextFn?: () => Context;
|
|
33
|
+
/**
|
|
34
|
+
* Whether to automatically track decision events (default: true).
|
|
35
|
+
* When enabled, every call to decide() automatically sends a DecisionEvent
|
|
36
|
+
* to the control plane, enabling intent-to-treat analysis.
|
|
37
|
+
*/
|
|
38
|
+
trackDecisions?: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Decision deduplication TTL in milliseconds (default: 1 hour).
|
|
41
|
+
* Same user+assignment combination won't be tracked again within this window.
|
|
42
|
+
*/
|
|
43
|
+
decisionDeduplicationTtlMs?: number;
|
|
44
|
+
/**
|
|
45
|
+
* Exposure deduplication session TTL in milliseconds (default: 30 minutes).
|
|
46
|
+
* Same user seeing same variant won't trigger multiple exposure events.
|
|
47
|
+
*/
|
|
48
|
+
exposureSessionTtlMs?: number;
|
|
49
|
+
/**
|
|
50
|
+
* Plugins to register with the client.
|
|
51
|
+
* The DecisionTrackingPlugin is included by default unless trackDecisions is false.
|
|
52
|
+
*/
|
|
53
|
+
plugins?: TrafficalPlugin[];
|
|
54
|
+
/** Max events before auto-flush (default: 10) */
|
|
55
|
+
eventBatchSize?: number;
|
|
56
|
+
/** Auto-flush interval in ms (default: 30000) */
|
|
57
|
+
eventFlushIntervalMs?: number;
|
|
58
|
+
/**
|
|
59
|
+
* Pre-fetched config bundle from server-side load function.
|
|
60
|
+
* When provided, the SDK is immediately ready without fetching.
|
|
61
|
+
*/
|
|
62
|
+
initialBundle?: ConfigBundle | null;
|
|
63
|
+
/**
|
|
64
|
+
* Pre-resolved params from SSR for immediate hydration.
|
|
65
|
+
* Used to prevent FOOC (Flash of Original Content).
|
|
66
|
+
*/
|
|
67
|
+
initialParams?: Record<string, unknown>;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Internal context value shared via Svelte's setContext/getContext.
|
|
71
|
+
*/
|
|
72
|
+
export interface TrafficalContextValue {
|
|
73
|
+
/** The Traffical client instance (null during SSR) */
|
|
74
|
+
readonly client: TrafficalClient | null;
|
|
75
|
+
/** Whether the client is ready (config loaded) */
|
|
76
|
+
readonly ready: boolean;
|
|
77
|
+
/** Any initialization error */
|
|
78
|
+
readonly error: Error | null;
|
|
79
|
+
/** The current config bundle */
|
|
80
|
+
readonly bundle: ConfigBundle | null;
|
|
81
|
+
/** Function to get the unit key */
|
|
82
|
+
getUnitKey: () => string;
|
|
83
|
+
/** Function to get the full context */
|
|
84
|
+
getContext: () => Context;
|
|
85
|
+
/** Initial params from SSR for hydration */
|
|
86
|
+
initialParams?: Record<string, unknown>;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Options for the useTraffical hook.
|
|
90
|
+
*/
|
|
91
|
+
export interface UseTrafficalOptions<T extends Record<string, ParameterValue> = Record<string, ParameterValue>> {
|
|
92
|
+
/** Default parameter values (required for type inference) */
|
|
93
|
+
defaults: T;
|
|
94
|
+
/** Additional context to merge (optional) */
|
|
95
|
+
context?: Context;
|
|
96
|
+
/**
|
|
97
|
+
* Tracking mode (default: "full")
|
|
98
|
+
* - "full": Track decision + exposure (default, recommended for UI)
|
|
99
|
+
* - "decision": Track decision only, manual exposure control
|
|
100
|
+
* - "none": No tracking (SSR, internal logic, tests)
|
|
101
|
+
*/
|
|
102
|
+
tracking?: "full" | "decision" | "none";
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Options for the bound track function returned by useTraffical.
|
|
106
|
+
*/
|
|
107
|
+
export interface BoundTrackOptions {
|
|
108
|
+
/** Additional event properties */
|
|
109
|
+
properties?: Record<string, unknown>;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* @deprecated Use BoundTrackOptions instead.
|
|
113
|
+
* Options for the bound trackReward function returned by useTraffical.
|
|
114
|
+
*/
|
|
115
|
+
export interface BoundTrackRewardOptions {
|
|
116
|
+
/** The reward value (e.g., revenue amount, conversion count) */
|
|
117
|
+
reward: number;
|
|
118
|
+
/** Type of reward (e.g., "revenue", "conversion", "engagement") */
|
|
119
|
+
rewardType?: string;
|
|
120
|
+
/** Multiple reward values keyed by type */
|
|
121
|
+
rewards?: Record<string, number>;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Return value from the useTraffical hook.
|
|
125
|
+
* All properties are reactive via Svelte 5 runes.
|
|
126
|
+
*/
|
|
127
|
+
export interface UseTrafficalResult<T extends Record<string, ParameterValue> = Record<string, ParameterValue>> {
|
|
128
|
+
/** Resolved parameter values (reactive) */
|
|
129
|
+
readonly params: T;
|
|
130
|
+
/** The full decision result (null when tracking="none") */
|
|
131
|
+
readonly decision: DecisionResult | null;
|
|
132
|
+
/** Whether the client is ready (config loaded) */
|
|
133
|
+
readonly ready: boolean;
|
|
134
|
+
/** Any error that occurred */
|
|
135
|
+
readonly error: Error | null;
|
|
136
|
+
/** Function to manually track exposure (no-op when tracking="none") */
|
|
137
|
+
trackExposure: () => void;
|
|
138
|
+
/**
|
|
139
|
+
* Track a user event. The decisionId is automatically bound.
|
|
140
|
+
* No-op if tracking="none" or no decision is available.
|
|
141
|
+
*
|
|
142
|
+
* @example
|
|
143
|
+
* track('purchase', { value: 99.99, orderId: 'ord_123' });
|
|
144
|
+
* track('add_to_cart', { itemId: 'sku_456' });
|
|
145
|
+
*/
|
|
146
|
+
track: (event: string, properties?: Record<string, unknown>) => void;
|
|
147
|
+
/**
|
|
148
|
+
* @deprecated Use track() instead.
|
|
149
|
+
* Track a reward for this decision. The decisionId is automatically bound.
|
|
150
|
+
* No-op if tracking="none" or no decision is available.
|
|
151
|
+
*/
|
|
152
|
+
trackReward: (options: BoundTrackRewardOptions) => void;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Options for tracking an event with the standalone hook.
|
|
156
|
+
*/
|
|
157
|
+
export interface TrackEventOptions {
|
|
158
|
+
/** Event name (e.g., 'purchase', 'add_to_cart') */
|
|
159
|
+
event: string;
|
|
160
|
+
/** Additional event properties */
|
|
161
|
+
properties?: Record<string, unknown>;
|
|
162
|
+
/** Reference to the decision (optional, for attribution) */
|
|
163
|
+
decisionId?: string;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* @deprecated Use TrackEventOptions instead.
|
|
167
|
+
* Options for tracking a reward.
|
|
168
|
+
*/
|
|
169
|
+
export interface TrackRewardOptions {
|
|
170
|
+
/** Reference to the decision (optional, will use last decision if not provided) */
|
|
171
|
+
decisionId?: string;
|
|
172
|
+
/** The reward value (e.g., revenue amount, conversion count) */
|
|
173
|
+
reward: number;
|
|
174
|
+
/** Type of reward (e.g., "revenue", "conversion", "engagement") */
|
|
175
|
+
rewardType?: string;
|
|
176
|
+
/** Multiple reward values keyed by type */
|
|
177
|
+
rewards?: Record<string, number>;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Options for loading the Traffical config bundle in a SvelteKit load function.
|
|
181
|
+
*/
|
|
182
|
+
export interface LoadTrafficalBundleOptions {
|
|
183
|
+
/** Organization ID */
|
|
184
|
+
orgId: string;
|
|
185
|
+
/** Project ID */
|
|
186
|
+
projectId: string;
|
|
187
|
+
/** Environment (e.g., "production", "staging") */
|
|
188
|
+
env: string;
|
|
189
|
+
/** API key for authentication */
|
|
190
|
+
apiKey: string;
|
|
191
|
+
/** SvelteKit's fetch function (from load context) */
|
|
192
|
+
fetch: typeof globalThis.fetch;
|
|
193
|
+
/** Base URL for the SDK API (optional, defaults to https://sdk.traffical.io) */
|
|
194
|
+
baseUrl?: string;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Result from loading the Traffical config bundle.
|
|
198
|
+
*/
|
|
199
|
+
export interface LoadTrafficalBundleResult {
|
|
200
|
+
/** The fetched config bundle, or null if fetch failed */
|
|
201
|
+
bundle: ConfigBundle | null;
|
|
202
|
+
/** Error message if fetch failed */
|
|
203
|
+
error?: string;
|
|
204
|
+
}
|
|
205
|
+
export type { ConfigBundle, Context, DecisionResult, ParameterValue, TrafficalClient, TrafficalPlugin, };
|
|
206
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@traffical/svelte",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Traffical SDK for Svelte 5 - Provider and hooks for parameter resolution with SSR support",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"svelte": "./src/index.ts",
|
|
7
|
+
"main": "./src/index.ts",
|
|
8
|
+
"module": "./src/index.ts",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"svelte": "./src/index.ts",
|
|
14
|
+
"bun": "./src/index.ts",
|
|
15
|
+
"import": "./src/index.ts"
|
|
16
|
+
},
|
|
17
|
+
"./sveltekit": {
|
|
18
|
+
"types": "./dist/sveltekit.d.ts",
|
|
19
|
+
"bun": "./src/sveltekit.ts",
|
|
20
|
+
"import": "./src/sveltekit.ts"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"src"
|
|
26
|
+
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "svelte-package --input src",
|
|
29
|
+
"dev": "svelte-package --input src --watch",
|
|
30
|
+
"test": "bun test",
|
|
31
|
+
"typecheck": "svelte-check --tsconfig ./tsconfig.json"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@traffical/core": "workspace:^",
|
|
35
|
+
"@traffical/js-client": "workspace:^"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@sveltejs/package": "^2.3.0",
|
|
39
|
+
"@types/bun": "latest",
|
|
40
|
+
"svelte": "^5.46.4",
|
|
41
|
+
"svelte-check": "^4.0.0",
|
|
42
|
+
"typescript": "^5.3.0"
|
|
43
|
+
},
|
|
44
|
+
"peerDependencies": {
|
|
45
|
+
"svelte": "^5.0.0"
|
|
46
|
+
},
|
|
47
|
+
"keywords": [
|
|
48
|
+
"traffical",
|
|
49
|
+
"feature-flags",
|
|
50
|
+
"experimentation",
|
|
51
|
+
"a/b-testing",
|
|
52
|
+
"svelte",
|
|
53
|
+
"svelte5",
|
|
54
|
+
"sveltekit",
|
|
55
|
+
"runes",
|
|
56
|
+
"ssr"
|
|
57
|
+
],
|
|
58
|
+
"license": "MIT",
|
|
59
|
+
"repository": {
|
|
60
|
+
"type": "git",
|
|
61
|
+
"url": "https://github.com/traffical/js-sdk"
|
|
62
|
+
},
|
|
63
|
+
"publishConfig": {
|
|
64
|
+
"access": "public"
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@traffical/svelte - TrafficalProvider Component
|
|
3
|
+
|
|
4
|
+
Wrapper component that initializes the Traffical context.
|
|
5
|
+
Alternative to calling initTraffical() directly in your layout.
|
|
6
|
+
-->
|
|
7
|
+
<script lang="ts">
|
|
8
|
+
import type { Snippet } from "svelte";
|
|
9
|
+
import { initTraffical } from "./context.svelte.js";
|
|
10
|
+
import type { TrafficalProviderConfig } from "./types.js";
|
|
11
|
+
|
|
12
|
+
interface Props {
|
|
13
|
+
/** Configuration for the Traffical client */
|
|
14
|
+
config: TrafficalProviderConfig;
|
|
15
|
+
/** Child content */
|
|
16
|
+
children: Snippet;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
let { config, children }: Props = $props();
|
|
20
|
+
|
|
21
|
+
// Initialize context - this sets up the client and makes it available to children
|
|
22
|
+
const context = initTraffical(config);
|
|
23
|
+
|
|
24
|
+
// Cleanup on component destroy
|
|
25
|
+
$effect(() => {
|
|
26
|
+
return () => {
|
|
27
|
+
// Destroy client when provider unmounts
|
|
28
|
+
context.client?.destroy();
|
|
29
|
+
};
|
|
30
|
+
});
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
{@render children()}
|
|
34
|
+
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @traffical/svelte - Context Layer
|
|
3
|
+
*
|
|
4
|
+
* SSR-safe context management using Svelte 5 runes and Svelte's context API.
|
|
5
|
+
* Initializes the TrafficalClient with environment-appropriate providers.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { getContext, setContext } from "svelte";
|
|
9
|
+
import {
|
|
10
|
+
TrafficalClient,
|
|
11
|
+
createTrafficalClientSync,
|
|
12
|
+
MemoryStorageProvider,
|
|
13
|
+
LocalStorageProvider,
|
|
14
|
+
} from "@traffical/js-client";
|
|
15
|
+
import type { Context as TrafficalContext } from "@traffical/core";
|
|
16
|
+
import type {
|
|
17
|
+
TrafficalProviderConfig,
|
|
18
|
+
TrafficalContextValue,
|
|
19
|
+
} from "./types.js";
|
|
20
|
+
|
|
21
|
+
// =============================================================================
|
|
22
|
+
// Constants
|
|
23
|
+
// =============================================================================
|
|
24
|
+
|
|
25
|
+
const TRAFFICAL_CONTEXT_KEY = Symbol("traffical");
|
|
26
|
+
|
|
27
|
+
// =============================================================================
|
|
28
|
+
// Browser Detection
|
|
29
|
+
// =============================================================================
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Check if we're running in a browser environment.
|
|
33
|
+
* SSR-safe - returns false during server-side rendering.
|
|
34
|
+
*/
|
|
35
|
+
function isBrowser(): boolean {
|
|
36
|
+
return typeof window !== "undefined" && typeof document !== "undefined";
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// =============================================================================
|
|
40
|
+
// Context State Factory
|
|
41
|
+
// =============================================================================
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Creates the reactive Traffical context state.
|
|
45
|
+
* Uses $state for reactive properties that work with SSR.
|
|
46
|
+
*/
|
|
47
|
+
function createTrafficalContextState(
|
|
48
|
+
config: TrafficalProviderConfig
|
|
49
|
+
): TrafficalContextValue {
|
|
50
|
+
// Reactive state using Svelte 5 runes
|
|
51
|
+
let client = $state<TrafficalClient | null>(null);
|
|
52
|
+
let ready = $state(!!config.initialBundle); // Ready immediately if we have initial bundle
|
|
53
|
+
let error = $state<Error | null>(null);
|
|
54
|
+
let bundle = $state(config.initialBundle ?? null);
|
|
55
|
+
|
|
56
|
+
// Initialize client only in browser
|
|
57
|
+
if (isBrowser()) {
|
|
58
|
+
// Use localStorage in browser, memory storage would lose data
|
|
59
|
+
const storage = new LocalStorageProvider();
|
|
60
|
+
|
|
61
|
+
const clientInstance = createTrafficalClientSync({
|
|
62
|
+
orgId: config.orgId,
|
|
63
|
+
projectId: config.projectId,
|
|
64
|
+
env: config.env,
|
|
65
|
+
apiKey: config.apiKey,
|
|
66
|
+
baseUrl: config.baseUrl,
|
|
67
|
+
localConfig: config.initialBundle ?? config.localConfig,
|
|
68
|
+
refreshIntervalMs: config.refreshIntervalMs,
|
|
69
|
+
storage,
|
|
70
|
+
// Decision tracking options
|
|
71
|
+
trackDecisions: config.trackDecisions,
|
|
72
|
+
decisionDeduplicationTtlMs: config.decisionDeduplicationTtlMs,
|
|
73
|
+
// Exposure options
|
|
74
|
+
exposureSessionTtlMs: config.exposureSessionTtlMs,
|
|
75
|
+
// Event batching options
|
|
76
|
+
eventBatchSize: config.eventBatchSize,
|
|
77
|
+
eventFlushIntervalMs: config.eventFlushIntervalMs,
|
|
78
|
+
// Plugins
|
|
79
|
+
plugins: config.plugins,
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
client = clientInstance;
|
|
83
|
+
|
|
84
|
+
// Initialize asynchronously (fetches fresh config if needed)
|
|
85
|
+
clientInstance
|
|
86
|
+
.initialize()
|
|
87
|
+
.then(() => {
|
|
88
|
+
ready = true;
|
|
89
|
+
// Update bundle if client fetched a newer one
|
|
90
|
+
const configVersion = clientInstance.getConfigVersion();
|
|
91
|
+
if (configVersion) {
|
|
92
|
+
// Bundle is internal, but we track ready state
|
|
93
|
+
}
|
|
94
|
+
})
|
|
95
|
+
.catch((err) => {
|
|
96
|
+
error = err instanceof Error ? err : new Error(String(err));
|
|
97
|
+
// Still mark as ready - we'll use defaults/initial bundle
|
|
98
|
+
ready = true;
|
|
99
|
+
});
|
|
100
|
+
} else {
|
|
101
|
+
// On server, use memory storage and mark as ready if we have initial data
|
|
102
|
+
// The client won't actually be used for tracking on server
|
|
103
|
+
if (config.initialBundle) {
|
|
104
|
+
const storage = new MemoryStorageProvider();
|
|
105
|
+
const clientInstance = createTrafficalClientSync({
|
|
106
|
+
orgId: config.orgId,
|
|
107
|
+
projectId: config.projectId,
|
|
108
|
+
env: config.env,
|
|
109
|
+
apiKey: config.apiKey,
|
|
110
|
+
localConfig: config.initialBundle,
|
|
111
|
+
storage,
|
|
112
|
+
// Disable background operations on server
|
|
113
|
+
refreshIntervalMs: 0,
|
|
114
|
+
});
|
|
115
|
+
client = clientInstance;
|
|
116
|
+
ready = true;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Unit key getter - uses config function or client's stable ID
|
|
121
|
+
function getUnitKey(): string {
|
|
122
|
+
if (config.unitKeyFn) {
|
|
123
|
+
return config.unitKeyFn();
|
|
124
|
+
}
|
|
125
|
+
// Fall back to client's auto-generated stable ID
|
|
126
|
+
return client?.getStableId() ?? "";
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Context getter - merges unit key with additional context
|
|
130
|
+
function getContext(): TrafficalContext {
|
|
131
|
+
const unitKey = getUnitKey();
|
|
132
|
+
const additionalContext = config.contextFn?.() ?? {};
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
...additionalContext,
|
|
136
|
+
// Include common unit key field names for compatibility
|
|
137
|
+
userId: unitKey,
|
|
138
|
+
deviceId: unitKey,
|
|
139
|
+
anonymousId: unitKey,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
get client() {
|
|
145
|
+
return client;
|
|
146
|
+
},
|
|
147
|
+
get ready() {
|
|
148
|
+
return ready;
|
|
149
|
+
},
|
|
150
|
+
get error() {
|
|
151
|
+
return error;
|
|
152
|
+
},
|
|
153
|
+
get bundle() {
|
|
154
|
+
return bundle;
|
|
155
|
+
},
|
|
156
|
+
getUnitKey,
|
|
157
|
+
getContext,
|
|
158
|
+
initialParams: config.initialParams,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// =============================================================================
|
|
163
|
+
// Public API
|
|
164
|
+
// =============================================================================
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Initializes the Traffical context.
|
|
168
|
+
* Must be called at the root of your application (e.g., in +layout.svelte).
|
|
169
|
+
*
|
|
170
|
+
* @example
|
|
171
|
+
* ```svelte
|
|
172
|
+
* <script>
|
|
173
|
+
* import { initTraffical } from '@traffical/svelte';
|
|
174
|
+
*
|
|
175
|
+
* let { data, children } = $props();
|
|
176
|
+
*
|
|
177
|
+
* initTraffical({
|
|
178
|
+
* orgId: 'org_123',
|
|
179
|
+
* projectId: 'proj_456',
|
|
180
|
+
* env: 'production',
|
|
181
|
+
* apiKey: 'pk_...',
|
|
182
|
+
* initialBundle: data.traffical?.bundle,
|
|
183
|
+
* });
|
|
184
|
+
* </script>
|
|
185
|
+
*
|
|
186
|
+
* {@render children()}
|
|
187
|
+
* ```
|
|
188
|
+
*/
|
|
189
|
+
export function initTraffical(
|
|
190
|
+
config: TrafficalProviderConfig
|
|
191
|
+
): TrafficalContextValue {
|
|
192
|
+
const contextValue = createTrafficalContextState(config);
|
|
193
|
+
setContext(TRAFFICAL_CONTEXT_KEY, contextValue);
|
|
194
|
+
return contextValue;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Gets the Traffical context value.
|
|
199
|
+
* Must be called within a component tree where initTraffical() has been called.
|
|
200
|
+
*
|
|
201
|
+
* @throws Error if called outside of Traffical context
|
|
202
|
+
*/
|
|
203
|
+
export function getTrafficalContext(): TrafficalContextValue {
|
|
204
|
+
const context = getContext<TrafficalContextValue | undefined>(
|
|
205
|
+
TRAFFICAL_CONTEXT_KEY
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
if (!context) {
|
|
209
|
+
throw new Error(
|
|
210
|
+
"getTrafficalContext() must be called within a component tree where initTraffical() has been called. " +
|
|
211
|
+
"Make sure to call initTraffical() in your root layout or wrap your app with <TrafficalProvider>."
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return context;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Checks if Traffical context is available.
|
|
220
|
+
* Useful for conditional rendering or optional Traffical integration.
|
|
221
|
+
*/
|
|
222
|
+
export function hasTrafficalContext(): boolean {
|
|
223
|
+
try {
|
|
224
|
+
const context = getContext<TrafficalContextValue | undefined>(
|
|
225
|
+
TRAFFICAL_CONTEXT_KEY
|
|
226
|
+
);
|
|
227
|
+
return context !== undefined;
|
|
228
|
+
} catch {
|
|
229
|
+
return false;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|