@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
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @traffical/svelte - Hooks
|
|
3
|
+
*
|
|
4
|
+
* Svelte 5 hooks for parameter resolution and decision tracking.
|
|
5
|
+
* Uses runes ($derived, $effect) for reactive, fine-grained updates.
|
|
6
|
+
*/
|
|
7
|
+
import type { ParameterValue } from "@traffical/core";
|
|
8
|
+
import type { TrafficalPlugin, TrafficalClient } from "@traffical/js-client";
|
|
9
|
+
import type { UseTrafficalOptions, UseTrafficalResult, TrackRewardOptions, TrackEventOptions } from "./types.js";
|
|
10
|
+
/**
|
|
11
|
+
* Primary hook for Traffical parameter resolution and decision tracking.
|
|
12
|
+
*
|
|
13
|
+
* Returns reactive values that automatically update when the config bundle changes.
|
|
14
|
+
* On first render, returns defaults immediately (no blocking).
|
|
15
|
+
* When the config bundle loads, recomputes and returns resolved values.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```svelte
|
|
19
|
+
* <script>
|
|
20
|
+
* import { useTraffical } from '@traffical/svelte';
|
|
21
|
+
*
|
|
22
|
+
* // Full tracking (default) - decision + exposure events
|
|
23
|
+
* const { params, decision, ready } = useTraffical({
|
|
24
|
+
* defaults: { "checkout.ctaText": "Buy Now" },
|
|
25
|
+
* });
|
|
26
|
+
* </script>
|
|
27
|
+
*
|
|
28
|
+
* {#if ready}
|
|
29
|
+
* <button>{params['checkout.ctaText']}</button>
|
|
30
|
+
* {:else}
|
|
31
|
+
* <button disabled>Loading...</button>
|
|
32
|
+
* {/if}
|
|
33
|
+
* ```
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```svelte
|
|
37
|
+
* <script>
|
|
38
|
+
* // Decision tracking only - manual exposure control
|
|
39
|
+
* const { params, decision, trackExposure } = useTraffical({
|
|
40
|
+
* defaults: { "checkout.ctaText": "Buy Now" },
|
|
41
|
+
* tracking: "decision",
|
|
42
|
+
* });
|
|
43
|
+
*
|
|
44
|
+
* // Track exposure when element becomes visible
|
|
45
|
+
* function handleVisible() {
|
|
46
|
+
* trackExposure();
|
|
47
|
+
* }
|
|
48
|
+
* </script>
|
|
49
|
+
* ```
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```svelte
|
|
53
|
+
* <script>
|
|
54
|
+
* // No tracking - for SSR, tests, or internal logic
|
|
55
|
+
* const { params, ready } = useTraffical({
|
|
56
|
+
* defaults: { "ui.hero.title": "Welcome" },
|
|
57
|
+
* tracking: "none",
|
|
58
|
+
* });
|
|
59
|
+
* </script>
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
export declare function useTraffical<T extends Record<string, ParameterValue>>(options: UseTrafficalOptions<T>): UseTrafficalResult<T>;
|
|
63
|
+
/**
|
|
64
|
+
* Hook to track user events.
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```svelte
|
|
68
|
+
* <script>
|
|
69
|
+
* import { useTrafficalTrack } from '@traffical/svelte';
|
|
70
|
+
*
|
|
71
|
+
* const track = useTrafficalTrack();
|
|
72
|
+
*
|
|
73
|
+
* function handlePurchase(amount: number) {
|
|
74
|
+
* track({
|
|
75
|
+
* event: 'purchase',
|
|
76
|
+
* properties: { value: amount, orderId: 'ord_123' },
|
|
77
|
+
* });
|
|
78
|
+
* }
|
|
79
|
+
* </script>
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
export declare function useTrafficalTrack(): (options: TrackEventOptions) => void;
|
|
83
|
+
/**
|
|
84
|
+
* @deprecated Use useTrafficalTrack() instead.
|
|
85
|
+
*
|
|
86
|
+
* Hook to track rewards (conversions, revenue, etc.).
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* ```svelte
|
|
90
|
+
* <script>
|
|
91
|
+
* import { useTraffical, useTrafficalReward } from '@traffical/svelte';
|
|
92
|
+
*
|
|
93
|
+
* const { params, decision } = useTraffical({
|
|
94
|
+
* defaults: { 'checkout.ctaText': 'Buy Now' },
|
|
95
|
+
* });
|
|
96
|
+
*
|
|
97
|
+
* const trackReward = useTrafficalReward();
|
|
98
|
+
*
|
|
99
|
+
* function handlePurchase(amount: number) {
|
|
100
|
+
* trackReward({
|
|
101
|
+
* reward: amount,
|
|
102
|
+
* rewardType: 'revenue',
|
|
103
|
+
* });
|
|
104
|
+
* }
|
|
105
|
+
* </script>
|
|
106
|
+
* ```
|
|
107
|
+
*/
|
|
108
|
+
export declare function useTrafficalReward(): (options: TrackRewardOptions) => void;
|
|
109
|
+
/**
|
|
110
|
+
* Hook to access the Traffical client directly.
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* ```svelte
|
|
114
|
+
* <script>
|
|
115
|
+
* import { useTrafficalClient } from '@traffical/svelte';
|
|
116
|
+
*
|
|
117
|
+
* const { client, ready, error } = useTrafficalClient();
|
|
118
|
+
*
|
|
119
|
+
* $effect(() => {
|
|
120
|
+
* if (ready && client) {
|
|
121
|
+
* const version = client.getConfigVersion();
|
|
122
|
+
* const stableId = client.getStableId();
|
|
123
|
+
* console.log('Config version:', version, 'Stable ID:', stableId);
|
|
124
|
+
* }
|
|
125
|
+
* });
|
|
126
|
+
* </script>
|
|
127
|
+
* ```
|
|
128
|
+
*/
|
|
129
|
+
export declare function useTrafficalClient(): {
|
|
130
|
+
readonly client: TrafficalClient | null;
|
|
131
|
+
readonly ready: boolean;
|
|
132
|
+
readonly error: Error | null;
|
|
133
|
+
};
|
|
134
|
+
/**
|
|
135
|
+
* Hook to access a registered plugin by name.
|
|
136
|
+
*
|
|
137
|
+
* @example
|
|
138
|
+
* ```svelte
|
|
139
|
+
* <script>
|
|
140
|
+
* import { useTrafficalPlugin } from '@traffical/svelte';
|
|
141
|
+
* import type { DOMBindingPlugin } from '@traffical/js-client';
|
|
142
|
+
*
|
|
143
|
+
* const domPlugin = useTrafficalPlugin<DOMBindingPlugin>('dom-binding');
|
|
144
|
+
*
|
|
145
|
+
* // Re-apply bindings after dynamic content changes
|
|
146
|
+
* $effect(() => {
|
|
147
|
+
* if (contentLoaded) {
|
|
148
|
+
* domPlugin?.applyBindings();
|
|
149
|
+
* }
|
|
150
|
+
* });
|
|
151
|
+
* </script>
|
|
152
|
+
* ```
|
|
153
|
+
*/
|
|
154
|
+
export declare function useTrafficalPlugin<T extends TrafficalPlugin = TrafficalPlugin>(name: string): T | undefined;
|
|
155
|
+
//# sourceMappingURL=hooks.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.svelte.d.ts","sourceRoot":"","sources":["../src/hooks.svelte.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EACV,cAAc,EAGf,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EACV,eAAe,EACf,eAAe,EAChB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,KAAK,EACV,mBAAmB,EACnB,kBAAkB,EAElB,kBAAkB,EAClB,iBAAiB,EAClB,MAAM,YAAY,CAAC;AAcpB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,EACnE,OAAO,EAAE,mBAAmB,CAAC,CAAC,CAAC,GAC9B,kBAAkB,CAAC,CAAC,CAAC,CAsKvB;AAMD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,iBAAiB,IAAI,CAAC,OAAO,EAAE,iBAAiB,KAAK,IAAI,CAiBxE;AAMD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,kBAAkB,IAAI,CAAC,OAAO,EAAE,kBAAkB,KAAK,IAAI,CA4B1E;AAMD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,kBAAkB,IAAI;IACpC,QAAQ,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI,CAAC;IACxC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CAC9B,CAcA;AAMD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,kBAAkB,CAChC,CAAC,SAAS,eAAe,GAAG,eAAe,EAC3C,IAAI,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAY7B"}
|
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @traffical/svelte - Hooks
|
|
3
|
+
*
|
|
4
|
+
* Svelte 5 hooks for parameter resolution and decision tracking.
|
|
5
|
+
* Uses runes ($derived, $effect) for reactive, fine-grained updates.
|
|
6
|
+
*/
|
|
7
|
+
import { resolveParameters, decide as coreDecide } from "@traffical/core";
|
|
8
|
+
import { getTrafficalContext } from "./context.svelte.js";
|
|
9
|
+
// =============================================================================
|
|
10
|
+
// Browser Detection
|
|
11
|
+
// =============================================================================
|
|
12
|
+
function isBrowser() {
|
|
13
|
+
return typeof window !== "undefined" && typeof document !== "undefined";
|
|
14
|
+
}
|
|
15
|
+
// =============================================================================
|
|
16
|
+
// useTraffical - Primary Hook
|
|
17
|
+
// =============================================================================
|
|
18
|
+
/**
|
|
19
|
+
* Primary hook for Traffical parameter resolution and decision tracking.
|
|
20
|
+
*
|
|
21
|
+
* Returns reactive values that automatically update when the config bundle changes.
|
|
22
|
+
* On first render, returns defaults immediately (no blocking).
|
|
23
|
+
* When the config bundle loads, recomputes and returns resolved values.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```svelte
|
|
27
|
+
* <script>
|
|
28
|
+
* import { useTraffical } from '@traffical/svelte';
|
|
29
|
+
*
|
|
30
|
+
* // Full tracking (default) - decision + exposure events
|
|
31
|
+
* const { params, decision, ready } = useTraffical({
|
|
32
|
+
* defaults: { "checkout.ctaText": "Buy Now" },
|
|
33
|
+
* });
|
|
34
|
+
* </script>
|
|
35
|
+
*
|
|
36
|
+
* {#if ready}
|
|
37
|
+
* <button>{params['checkout.ctaText']}</button>
|
|
38
|
+
* {:else}
|
|
39
|
+
* <button disabled>Loading...</button>
|
|
40
|
+
* {/if}
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```svelte
|
|
45
|
+
* <script>
|
|
46
|
+
* // Decision tracking only - manual exposure control
|
|
47
|
+
* const { params, decision, trackExposure } = useTraffical({
|
|
48
|
+
* defaults: { "checkout.ctaText": "Buy Now" },
|
|
49
|
+
* tracking: "decision",
|
|
50
|
+
* });
|
|
51
|
+
*
|
|
52
|
+
* // Track exposure when element becomes visible
|
|
53
|
+
* function handleVisible() {
|
|
54
|
+
* trackExposure();
|
|
55
|
+
* }
|
|
56
|
+
* </script>
|
|
57
|
+
* ```
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```svelte
|
|
61
|
+
* <script>
|
|
62
|
+
* // No tracking - for SSR, tests, or internal logic
|
|
63
|
+
* const { params, ready } = useTraffical({
|
|
64
|
+
* defaults: { "ui.hero.title": "Welcome" },
|
|
65
|
+
* tracking: "none",
|
|
66
|
+
* });
|
|
67
|
+
* </script>
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
export function useTraffical(options) {
|
|
71
|
+
const ctx = getTrafficalContext();
|
|
72
|
+
const trackingMode = options.tracking ?? "full";
|
|
73
|
+
const shouldTrackDecision = trackingMode !== "none";
|
|
74
|
+
const shouldAutoTrackExposure = trackingMode === "full";
|
|
75
|
+
// Track whether we've already tracked exposure for this decision
|
|
76
|
+
let hasTrackedExposure = $state(false);
|
|
77
|
+
let currentDecisionId = $state(null);
|
|
78
|
+
// Derive params reactively using $derived.by
|
|
79
|
+
// This is synchronous and provides fine-grained reactivity
|
|
80
|
+
const params = $derived.by(() => {
|
|
81
|
+
// Priority 1: Resolve from bundle if available
|
|
82
|
+
if (ctx.bundle) {
|
|
83
|
+
const context = {
|
|
84
|
+
...ctx.getContext(),
|
|
85
|
+
...options.context,
|
|
86
|
+
};
|
|
87
|
+
return resolveParameters(ctx.bundle, context, options.defaults);
|
|
88
|
+
}
|
|
89
|
+
// Priority 2: Use server-provided initial params
|
|
90
|
+
if (ctx.initialParams) {
|
|
91
|
+
return { ...options.defaults, ...ctx.initialParams };
|
|
92
|
+
}
|
|
93
|
+
// Priority 3: Fall back to defaults
|
|
94
|
+
return options.defaults;
|
|
95
|
+
});
|
|
96
|
+
// Derive decision reactively
|
|
97
|
+
const decision = $derived.by(() => {
|
|
98
|
+
if (!shouldTrackDecision) {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
if (!ctx.bundle) {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
const context = {
|
|
105
|
+
...ctx.getContext(),
|
|
106
|
+
...options.context,
|
|
107
|
+
};
|
|
108
|
+
// Use client's decide if available (handles tracking internally)
|
|
109
|
+
if (ctx.client) {
|
|
110
|
+
return ctx.client.decide({
|
|
111
|
+
context,
|
|
112
|
+
defaults: options.defaults,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
// Fall back to core decide (SSR or no client)
|
|
116
|
+
return coreDecide(ctx.bundle, context, options.defaults);
|
|
117
|
+
});
|
|
118
|
+
// Reset exposure tracking when decision changes
|
|
119
|
+
$effect(() => {
|
|
120
|
+
const decisionId = decision?.decisionId ?? null;
|
|
121
|
+
if (decisionId !== currentDecisionId) {
|
|
122
|
+
currentDecisionId = decisionId;
|
|
123
|
+
hasTrackedExposure = false;
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
// Auto-track exposure when tracking is "full" and decision is available
|
|
127
|
+
$effect(() => {
|
|
128
|
+
if (shouldAutoTrackExposure &&
|
|
129
|
+
decision &&
|
|
130
|
+
!hasTrackedExposure &&
|
|
131
|
+
isBrowser()) {
|
|
132
|
+
trackExposureInternal();
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
// Internal exposure tracking function
|
|
136
|
+
function trackExposureInternal() {
|
|
137
|
+
if (!isBrowser() || !ctx.client || !decision || hasTrackedExposure) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
ctx.client.trackExposure(decision);
|
|
141
|
+
hasTrackedExposure = true;
|
|
142
|
+
}
|
|
143
|
+
// Public exposure tracking function
|
|
144
|
+
function trackExposure() {
|
|
145
|
+
if (trackingMode === "none") {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
trackExposureInternal();
|
|
149
|
+
}
|
|
150
|
+
// Track user events - decisionId is automatically included
|
|
151
|
+
function track(event, properties) {
|
|
152
|
+
if (!isBrowser() || !ctx.client) {
|
|
153
|
+
if (!isBrowser()) {
|
|
154
|
+
return; // Silent no-op during SSR
|
|
155
|
+
}
|
|
156
|
+
console.warn("[Traffical] Client not initialized, cannot track event");
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
// Access the reactive decision value
|
|
160
|
+
const currentDecision = decision;
|
|
161
|
+
if (!currentDecision) {
|
|
162
|
+
console.warn("[Traffical] No decision available, cannot track event. Did you use tracking: 'none'?");
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
ctx.client.track(event, properties, {
|
|
166
|
+
decisionId: currentDecision.decisionId,
|
|
167
|
+
unitKey: ctx.getUnitKey(),
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
// Deprecated: Bound reward tracking - decisionId is automatically included
|
|
171
|
+
function trackReward(options) {
|
|
172
|
+
if (!isBrowser() || !ctx.client) {
|
|
173
|
+
if (!isBrowser()) {
|
|
174
|
+
return; // Silent no-op during SSR
|
|
175
|
+
}
|
|
176
|
+
console.warn("[Traffical] Client not initialized, cannot track reward");
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
// Access the reactive decision value
|
|
180
|
+
const currentDecision = decision;
|
|
181
|
+
if (!currentDecision) {
|
|
182
|
+
console.warn("[Traffical] No decision available, cannot track reward. Did you use tracking: 'none'?");
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
// Map old API to new track() API
|
|
186
|
+
track(options.rewardType || "reward", {
|
|
187
|
+
value: options.reward,
|
|
188
|
+
...(options.rewards ? { rewards: options.rewards } : {}),
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
return {
|
|
192
|
+
get params() {
|
|
193
|
+
return params;
|
|
194
|
+
},
|
|
195
|
+
get decision() {
|
|
196
|
+
return decision;
|
|
197
|
+
},
|
|
198
|
+
get ready() {
|
|
199
|
+
return ctx.ready;
|
|
200
|
+
},
|
|
201
|
+
get error() {
|
|
202
|
+
return ctx.error;
|
|
203
|
+
},
|
|
204
|
+
trackExposure,
|
|
205
|
+
track,
|
|
206
|
+
trackReward,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
// =============================================================================
|
|
210
|
+
// useTrafficalTrack
|
|
211
|
+
// =============================================================================
|
|
212
|
+
/**
|
|
213
|
+
* Hook to track user events.
|
|
214
|
+
*
|
|
215
|
+
* @example
|
|
216
|
+
* ```svelte
|
|
217
|
+
* <script>
|
|
218
|
+
* import { useTrafficalTrack } from '@traffical/svelte';
|
|
219
|
+
*
|
|
220
|
+
* const track = useTrafficalTrack();
|
|
221
|
+
*
|
|
222
|
+
* function handlePurchase(amount: number) {
|
|
223
|
+
* track({
|
|
224
|
+
* event: 'purchase',
|
|
225
|
+
* properties: { value: amount, orderId: 'ord_123' },
|
|
226
|
+
* });
|
|
227
|
+
* }
|
|
228
|
+
* </script>
|
|
229
|
+
* ```
|
|
230
|
+
*/
|
|
231
|
+
export function useTrafficalTrack() {
|
|
232
|
+
const ctx = getTrafficalContext();
|
|
233
|
+
return function track(options) {
|
|
234
|
+
if (!isBrowser() || !ctx.client) {
|
|
235
|
+
if (!isBrowser()) {
|
|
236
|
+
return; // Silent no-op during SSR
|
|
237
|
+
}
|
|
238
|
+
console.warn("[Traffical] Client not initialized, cannot track event");
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
ctx.client.track(options.event, options.properties, {
|
|
242
|
+
decisionId: options.decisionId,
|
|
243
|
+
unitKey: ctx.getUnitKey(),
|
|
244
|
+
});
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
// =============================================================================
|
|
248
|
+
// useTrafficalReward (deprecated)
|
|
249
|
+
// =============================================================================
|
|
250
|
+
/**
|
|
251
|
+
* @deprecated Use useTrafficalTrack() instead.
|
|
252
|
+
*
|
|
253
|
+
* Hook to track rewards (conversions, revenue, etc.).
|
|
254
|
+
*
|
|
255
|
+
* @example
|
|
256
|
+
* ```svelte
|
|
257
|
+
* <script>
|
|
258
|
+
* import { useTraffical, useTrafficalReward } from '@traffical/svelte';
|
|
259
|
+
*
|
|
260
|
+
* const { params, decision } = useTraffical({
|
|
261
|
+
* defaults: { 'checkout.ctaText': 'Buy Now' },
|
|
262
|
+
* });
|
|
263
|
+
*
|
|
264
|
+
* const trackReward = useTrafficalReward();
|
|
265
|
+
*
|
|
266
|
+
* function handlePurchase(amount: number) {
|
|
267
|
+
* trackReward({
|
|
268
|
+
* reward: amount,
|
|
269
|
+
* rewardType: 'revenue',
|
|
270
|
+
* });
|
|
271
|
+
* }
|
|
272
|
+
* </script>
|
|
273
|
+
* ```
|
|
274
|
+
*/
|
|
275
|
+
export function useTrafficalReward() {
|
|
276
|
+
const ctx = getTrafficalContext();
|
|
277
|
+
return function trackReward(options) {
|
|
278
|
+
if (!isBrowser() || !ctx.client) {
|
|
279
|
+
if (!isBrowser()) {
|
|
280
|
+
return; // Silent no-op during SSR
|
|
281
|
+
}
|
|
282
|
+
console.warn("[Traffical] Client not initialized, cannot track reward");
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
// We need to ensure decisionId is provided for the core TrackRewardOptions
|
|
286
|
+
// If not provided, we skip tracking (can't attribute without decision)
|
|
287
|
+
if (!options.decisionId) {
|
|
288
|
+
console.warn("[Traffical] trackReward called without decisionId, skipping");
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
// Map old API to new track() API
|
|
292
|
+
ctx.client.track(options.rewardType || "reward", {
|
|
293
|
+
value: options.reward,
|
|
294
|
+
...(options.rewards ? { rewards: options.rewards } : {}),
|
|
295
|
+
}, {
|
|
296
|
+
decisionId: options.decisionId,
|
|
297
|
+
unitKey: ctx.getUnitKey(),
|
|
298
|
+
});
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
// =============================================================================
|
|
302
|
+
// useTrafficalClient
|
|
303
|
+
// =============================================================================
|
|
304
|
+
/**
|
|
305
|
+
* Hook to access the Traffical client directly.
|
|
306
|
+
*
|
|
307
|
+
* @example
|
|
308
|
+
* ```svelte
|
|
309
|
+
* <script>
|
|
310
|
+
* import { useTrafficalClient } from '@traffical/svelte';
|
|
311
|
+
*
|
|
312
|
+
* const { client, ready, error } = useTrafficalClient();
|
|
313
|
+
*
|
|
314
|
+
* $effect(() => {
|
|
315
|
+
* if (ready && client) {
|
|
316
|
+
* const version = client.getConfigVersion();
|
|
317
|
+
* const stableId = client.getStableId();
|
|
318
|
+
* console.log('Config version:', version, 'Stable ID:', stableId);
|
|
319
|
+
* }
|
|
320
|
+
* });
|
|
321
|
+
* </script>
|
|
322
|
+
* ```
|
|
323
|
+
*/
|
|
324
|
+
export function useTrafficalClient() {
|
|
325
|
+
const ctx = getTrafficalContext();
|
|
326
|
+
return {
|
|
327
|
+
get client() {
|
|
328
|
+
return ctx.client;
|
|
329
|
+
},
|
|
330
|
+
get ready() {
|
|
331
|
+
return ctx.ready;
|
|
332
|
+
},
|
|
333
|
+
get error() {
|
|
334
|
+
return ctx.error;
|
|
335
|
+
},
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
// =============================================================================
|
|
339
|
+
// useTrafficalPlugin
|
|
340
|
+
// =============================================================================
|
|
341
|
+
/**
|
|
342
|
+
* Hook to access a registered plugin by name.
|
|
343
|
+
*
|
|
344
|
+
* @example
|
|
345
|
+
* ```svelte
|
|
346
|
+
* <script>
|
|
347
|
+
* import { useTrafficalPlugin } from '@traffical/svelte';
|
|
348
|
+
* import type { DOMBindingPlugin } from '@traffical/js-client';
|
|
349
|
+
*
|
|
350
|
+
* const domPlugin = useTrafficalPlugin<DOMBindingPlugin>('dom-binding');
|
|
351
|
+
*
|
|
352
|
+
* // Re-apply bindings after dynamic content changes
|
|
353
|
+
* $effect(() => {
|
|
354
|
+
* if (contentLoaded) {
|
|
355
|
+
* domPlugin?.applyBindings();
|
|
356
|
+
* }
|
|
357
|
+
* });
|
|
358
|
+
* </script>
|
|
359
|
+
* ```
|
|
360
|
+
*/
|
|
361
|
+
export function useTrafficalPlugin(name) {
|
|
362
|
+
const ctx = getTrafficalContext();
|
|
363
|
+
// Derive plugin access reactively
|
|
364
|
+
const plugin = $derived.by(() => {
|
|
365
|
+
if (!ctx.client || !ctx.ready) {
|
|
366
|
+
return undefined;
|
|
367
|
+
}
|
|
368
|
+
return ctx.client.getPlugin(name);
|
|
369
|
+
});
|
|
370
|
+
return plugin;
|
|
371
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @traffical/svelte
|
|
3
|
+
*
|
|
4
|
+
* Traffical SDK for Svelte 5 applications.
|
|
5
|
+
* Provides Provider component and hooks for parameter resolution and decision tracking.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Full SSR/hydration support for SvelteKit
|
|
9
|
+
* - Svelte 5 runes for reactive, fine-grained updates
|
|
10
|
+
* - Browser-optimized with sendBeacon, localStorage persistence
|
|
11
|
+
* - Automatic stable ID for anonymous users
|
|
12
|
+
* - Plugin system support (DecisionTrackingPlugin enabled by default)
|
|
13
|
+
* - Decision and exposure deduplication
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```svelte
|
|
17
|
+
* <!-- +layout.svelte -->
|
|
18
|
+
* <script>
|
|
19
|
+
* import { TrafficalProvider } from '@traffical/svelte';
|
|
20
|
+
*
|
|
21
|
+
* let { data, children } = $props();
|
|
22
|
+
* </script>
|
|
23
|
+
*
|
|
24
|
+
* <TrafficalProvider
|
|
25
|
+
* config={{
|
|
26
|
+
* orgId: 'org_123',
|
|
27
|
+
* projectId: 'proj_456',
|
|
28
|
+
* env: 'production',
|
|
29
|
+
* apiKey: 'pk_...',
|
|
30
|
+
* initialBundle: data.traffical?.bundle,
|
|
31
|
+
* }}
|
|
32
|
+
* >
|
|
33
|
+
* {@render children()}
|
|
34
|
+
* </TrafficalProvider>
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```svelte
|
|
39
|
+
* <!-- MyComponent.svelte -->
|
|
40
|
+
* <script>
|
|
41
|
+
* import { useTraffical } from '@traffical/svelte';
|
|
42
|
+
*
|
|
43
|
+
* const { params, ready } = useTraffical({
|
|
44
|
+
* defaults: { 'ui.hero.title': 'Welcome' },
|
|
45
|
+
* });
|
|
46
|
+
* </script>
|
|
47
|
+
*
|
|
48
|
+
* {#if ready}
|
|
49
|
+
* <h1>{params['ui.hero.title']}</h1>
|
|
50
|
+
* {:else}
|
|
51
|
+
* <h1>Loading...</h1>
|
|
52
|
+
* {/if}
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export { resolveParameters, decide, evaluateCondition, evaluateConditions, fnv1a, computeBucket, isInBucketRange, generateEventId, generateDecisionId, generateExposureId, generateRewardId, } from "@traffical/core";
|
|
56
|
+
export { TrafficalClient, createTrafficalClient, createTrafficalClientSync, LocalStorageProvider, MemoryStorageProvider, createStorageProvider, createDOMBindingPlugin, } from "@traffical/js-client";
|
|
57
|
+
export { initTraffical, getTrafficalContext, hasTrafficalContext, } from "./context.svelte.js";
|
|
58
|
+
export { useTraffical, useTrafficalTrack, useTrafficalReward, useTrafficalClient, useTrafficalPlugin, } from "./hooks.svelte.js";
|
|
59
|
+
export { default as TrafficalProvider } from "./TrafficalProvider.svelte";
|
|
60
|
+
export type { TrafficalProviderConfig, TrafficalContextValue, UseTrafficalOptions, UseTrafficalResult, BoundTrackOptions, TrackEventOptions, BoundTrackRewardOptions, TrackRewardOptions, LoadTrafficalBundleOptions, LoadTrafficalBundleResult, ConfigBundle, Context, DecisionResult, ParameterValue, TrafficalClient as TrafficalClientType, TrafficalPlugin, } from "./types.js";
|
|
61
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AAMH,OAAO,EAEL,iBAAiB,EACjB,MAAM,EACN,iBAAiB,EACjB,kBAAkB,EAElB,KAAK,EACL,aAAa,EACb,eAAe,EAEf,eAAe,EACf,kBAAkB,EAClB,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,iBAAiB,CAAC;AAMzB,OAAO,EAEL,eAAe,EACf,qBAAqB,EACrB,yBAAyB,EAEzB,oBAAoB,EACpB,qBAAqB,EACrB,qBAAqB,EAErB,sBAAsB,GACvB,MAAM,sBAAsB,CAAC;AAO9B,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAM1E,YAAY,EAEV,uBAAuB,EACvB,qBAAqB,EAErB,mBAAmB,EACnB,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EAEjB,uBAAuB,EACvB,kBAAkB,EAElB,0BAA0B,EAC1B,yBAAyB,EAEzB,YAAY,EACZ,OAAO,EACP,cAAc,EACd,cAAc,EACd,eAAe,IAAI,mBAAmB,EACtC,eAAe,GAChB,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @traffical/svelte
|
|
3
|
+
*
|
|
4
|
+
* Traffical SDK for Svelte 5 applications.
|
|
5
|
+
* Provides Provider component and hooks for parameter resolution and decision tracking.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Full SSR/hydration support for SvelteKit
|
|
9
|
+
* - Svelte 5 runes for reactive, fine-grained updates
|
|
10
|
+
* - Browser-optimized with sendBeacon, localStorage persistence
|
|
11
|
+
* - Automatic stable ID for anonymous users
|
|
12
|
+
* - Plugin system support (DecisionTrackingPlugin enabled by default)
|
|
13
|
+
* - Decision and exposure deduplication
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```svelte
|
|
17
|
+
* <!-- +layout.svelte -->
|
|
18
|
+
* <script>
|
|
19
|
+
* import { TrafficalProvider } from '@traffical/svelte';
|
|
20
|
+
*
|
|
21
|
+
* let { data, children } = $props();
|
|
22
|
+
* </script>
|
|
23
|
+
*
|
|
24
|
+
* <TrafficalProvider
|
|
25
|
+
* config={{
|
|
26
|
+
* orgId: 'org_123',
|
|
27
|
+
* projectId: 'proj_456',
|
|
28
|
+
* env: 'production',
|
|
29
|
+
* apiKey: 'pk_...',
|
|
30
|
+
* initialBundle: data.traffical?.bundle,
|
|
31
|
+
* }}
|
|
32
|
+
* >
|
|
33
|
+
* {@render children()}
|
|
34
|
+
* </TrafficalProvider>
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```svelte
|
|
39
|
+
* <!-- MyComponent.svelte -->
|
|
40
|
+
* <script>
|
|
41
|
+
* import { useTraffical } from '@traffical/svelte';
|
|
42
|
+
*
|
|
43
|
+
* const { params, ready } = useTraffical({
|
|
44
|
+
* defaults: { 'ui.hero.title': 'Welcome' },
|
|
45
|
+
* });
|
|
46
|
+
* </script>
|
|
47
|
+
*
|
|
48
|
+
* {#if ready}
|
|
49
|
+
* <h1>{params['ui.hero.title']}</h1>
|
|
50
|
+
* {:else}
|
|
51
|
+
* <h1>Loading...</h1>
|
|
52
|
+
* {/if}
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
// =============================================================================
|
|
56
|
+
// Re-export from @traffical/core
|
|
57
|
+
// =============================================================================
|
|
58
|
+
export {
|
|
59
|
+
// Resolution functions
|
|
60
|
+
resolveParameters, decide, evaluateCondition, evaluateConditions,
|
|
61
|
+
// Hashing utilities
|
|
62
|
+
fnv1a, computeBucket, isInBucketRange,
|
|
63
|
+
// ID generation
|
|
64
|
+
generateEventId, generateDecisionId, generateExposureId, generateRewardId, } from "@traffical/core";
|
|
65
|
+
// =============================================================================
|
|
66
|
+
// Re-export from @traffical/js-client
|
|
67
|
+
// =============================================================================
|
|
68
|
+
export {
|
|
69
|
+
// Client
|
|
70
|
+
TrafficalClient, createTrafficalClient, createTrafficalClientSync,
|
|
71
|
+
// Storage providers
|
|
72
|
+
LocalStorageProvider, MemoryStorageProvider, createStorageProvider,
|
|
73
|
+
// Plugins
|
|
74
|
+
createDOMBindingPlugin, } from "@traffical/js-client";
|
|
75
|
+
// =============================================================================
|
|
76
|
+
// Svelte-specific exports
|
|
77
|
+
// =============================================================================
|
|
78
|
+
// Context
|
|
79
|
+
export { initTraffical, getTrafficalContext, hasTrafficalContext, } from "./context.svelte.js";
|
|
80
|
+
// Hooks
|
|
81
|
+
export { useTraffical, useTrafficalTrack, useTrafficalReward, useTrafficalClient, useTrafficalPlugin, } from "./hooks.svelte.js";
|
|
82
|
+
// Provider component
|
|
83
|
+
export { default as TrafficalProvider } from "./TrafficalProvider.svelte";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../src/index.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
|