@drip-apex/sdk 0.1.0 → 0.3.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.
Files changed (246) hide show
  1. package/README.md +5 -0
  2. package/dist/bundle-report.json +584 -226
  3. package/dist/define.cjs +123 -0
  4. package/dist/define.cjs.map +7 -0
  5. package/dist/define.mjs +102 -0
  6. package/dist/define.mjs.map +7 -0
  7. package/dist/drip.cjs +19283 -0
  8. package/dist/drip.cjs.map +7 -0
  9. package/dist/drip.debug.js +1 -1
  10. package/dist/drip.debug.js.map +1 -1
  11. package/dist/drip.heatmap.js +1 -1
  12. package/dist/drip.heatmap.js.map +1 -1
  13. package/dist/drip.js +1 -1
  14. package/dist/drip.js.map +1 -1
  15. package/dist/drip.live.js +1 -1
  16. package/dist/drip.live.js.map +1 -1
  17. package/dist/drip.mjs +19267 -0
  18. package/dist/drip.mjs.map +7 -0
  19. package/dist/drip.replay-recorder.js +2 -0
  20. package/dist/drip.replay-recorder.js.map +1 -0
  21. package/dist/editor.js +147 -142
  22. package/dist/editor.js.map +4 -4
  23. package/dist/flags.cjs +1788 -0
  24. package/dist/flags.cjs.map +7 -0
  25. package/dist/flags.mjs +1765 -0
  26. package/dist/flags.mjs.map +7 -0
  27. package/dist/index.mjs +12 -0
  28. package/dist/index.mjs.map +2 -2
  29. package/dist/metafiles/define.cjs.meta.json +28 -0
  30. package/dist/metafiles/define.mjs.meta.json +32 -0
  31. package/dist/metafiles/drip.cjs.meta.json +3193 -0
  32. package/dist/metafiles/drip.debug.meta.json +2859 -574
  33. package/dist/metafiles/drip.heatmap.meta.json +2732 -460
  34. package/dist/metafiles/drip.live.meta.json +2732 -460
  35. package/dist/metafiles/drip.meta.json +2859 -574
  36. package/dist/metafiles/drip.mjs.meta.json +3235 -0
  37. package/dist/metafiles/drip.replay-recorder.meta.json +82 -0
  38. package/dist/metafiles/editor.meta.json +67 -27
  39. package/dist/metafiles/flags.cjs.meta.json +220 -0
  40. package/dist/metafiles/flags.mjs.meta.json +225 -0
  41. package/dist/metafiles/index.meta.json +2717 -491
  42. package/dist/metafiles/index.mjs.meta.json +14 -4
  43. package/dist/react/index.js +16636 -13533
  44. package/dist/react/index.js.map +4 -4
  45. package/dist/sri.json +20 -15
  46. package/dist/types/activation-runtime.d.ts +91 -0
  47. package/dist/types/analytics-runtime.d.ts +166 -0
  48. package/dist/types/antiflicker.d.ts +9 -3
  49. package/dist/types/assignment-cookie-sync.d.ts +31 -0
  50. package/dist/types/assignment-runtime.d.ts +257 -0
  51. package/dist/types/assignment-store.d.ts +4 -8
  52. package/dist/types/bucket.d.ts +1 -2
  53. package/dist/types/compact-store.d.ts +1 -0
  54. package/dist/types/config-runtime.d.ts +313 -0
  55. package/dist/types/consent-evaluation.d.ts +28 -0
  56. package/dist/types/css.d.ts +5 -3
  57. package/dist/types/debug-loader.d.ts +2 -0
  58. package/dist/types/decide-assignments.d.ts +31 -0
  59. package/dist/types/delivery-timing.d.ts +8 -0
  60. package/dist/types/early-interaction-capture.d.ts +79 -0
  61. package/dist/types/early-reassert.d.ts +32 -0
  62. package/dist/types/edge-runtime.d.ts +11 -0
  63. package/dist/types/editor/api.d.ts +3 -0
  64. package/dist/types/editor/controller.d.ts +3 -1
  65. package/dist/types/editor/publication-result.d.ts +11 -0
  66. package/dist/types/editor/types.d.ts +2 -1
  67. package/dist/types/editor/view-kit.d.ts +1 -1
  68. package/dist/types/endpoint.d.ts +8 -0
  69. package/dist/types/exclusion-groups.d.ts +4 -7
  70. package/dist/types/flags.d.ts +2 -0
  71. package/dist/types/goal-eligibility.d.ts +4 -0
  72. package/dist/types/goals.d.ts +10 -1
  73. package/dist/types/guards.d.ts +7 -7
  74. package/dist/types/handover.d.ts +12 -1
  75. package/dist/types/hash.d.ts +8 -4
  76. package/dist/types/hydration.d.ts +2 -1
  77. package/dist/types/index.d.ts +24 -128
  78. package/dist/types/init-test-helpers.d.ts +48 -0
  79. package/dist/types/inline-bootstrap.d.ts +26 -0
  80. package/dist/types/js.d.ts +3 -1
  81. package/dist/types/money.d.ts +12 -0
  82. package/dist/types/mutate.d.ts +45 -6
  83. package/dist/types/performance.d.ts +2 -0
  84. package/dist/types/persistence-decoders.d.ts +12 -0
  85. package/dist/types/public-api.d.ts +209 -0
  86. package/dist/types/qa-assistant-state.d.ts +34 -0
  87. package/dist/types/react/index.d.ts +28 -3
  88. package/dist/types/reassert-sentinel.d.ts +6 -0
  89. package/dist/types/redirect.d.ts +48 -0
  90. package/dist/types/replay-recorder-entry.d.ts +1 -0
  91. package/dist/types/replay-signal-state.d.ts +6 -0
  92. package/dist/types/replay-signals.d.ts +13 -0
  93. package/dist/types/replay.d.ts +110 -0
  94. package/dist/types/router.d.ts +12 -2
  95. package/dist/types/runtime-state.d.ts +166 -0
  96. package/dist/types/sanitize.d.ts +1 -0
  97. package/dist/types/shadow.d.ts +3 -0
  98. package/dist/types/shopify-cart.d.ts +25 -1
  99. package/dist/types/storefront-vitals.d.ts +24 -0
  100. package/dist/types/targeting-diagnostics.d.ts +1 -0
  101. package/dist/types/targeting.d.ts +16 -2
  102. package/dist/types/track.d.ts +15 -1
  103. package/dist/types/types.d.ts +115 -9
  104. package/dist/types/wait.d.ts +14 -1
  105. package/dist/types/weekday-schedule.d.ts +3 -0
  106. package/package.json +17 -8
  107. package/src/activation-runtime.ts +2737 -0
  108. package/src/analytics-runtime.ts +2175 -0
  109. package/src/antiflicker.test.ts +216 -44
  110. package/src/antiflicker.ts +103 -28
  111. package/src/artifact-concurrency.test.ts +1091 -0
  112. package/src/assignment-cookie-sync.test.ts +202 -0
  113. package/src/assignment-cookie-sync.ts +189 -0
  114. package/src/assignment-runtime.ts +3204 -0
  115. package/src/assignment-store.test.ts +56 -0
  116. package/src/assignment-store.ts +7 -38
  117. package/src/bucket.ts +3 -18
  118. package/src/commerce-analytics-lifecycle.test.ts +75 -15
  119. package/src/commerce-config-lifecycle.test.ts +81 -1
  120. package/src/commerce-price-preserve.test.ts +133 -0
  121. package/src/commerce-price.test.ts +97 -0
  122. package/src/commerce-price.ts +74 -20
  123. package/src/commerce-shipping.test.ts +53 -0
  124. package/src/commerce-shipping.ts +5 -11
  125. package/src/compact-store.ts +15 -0
  126. package/src/config-fetch.test.ts +480 -9
  127. package/src/config-runtime.ts +2071 -0
  128. package/src/consent-evaluation-borlabs.test.ts +41 -0
  129. package/src/consent-evaluation-cookiescript.test.ts +50 -0
  130. package/src/consent-evaluation.ts +151 -0
  131. package/src/css.test.ts +30 -0
  132. package/src/css.ts +120 -6
  133. package/src/cwv.test.ts +60 -77
  134. package/src/debug-loader.test.ts +214 -0
  135. package/src/debug-loader.ts +41 -0
  136. package/src/decide-assignments.test.ts +196 -0
  137. package/src/decide-assignments.ts +204 -0
  138. package/src/delivery-timing.test.ts +104 -0
  139. package/src/delivery-timing.ts +89 -0
  140. package/src/early-interaction-capture.test.ts +451 -0
  141. package/src/early-interaction-capture.ts +230 -0
  142. package/src/early-reassert.test.ts +207 -0
  143. package/src/early-reassert.ts +152 -0
  144. package/src/edge-adoption.test.ts +435 -0
  145. package/src/edge-runtime.ts +30 -0
  146. package/src/editor/api.test.ts +109 -0
  147. package/src/editor/api.ts +34 -3
  148. package/src/editor/controller.test.ts +297 -14
  149. package/src/editor/controller.ts +69 -5
  150. package/src/editor/index.test.ts +2 -0
  151. package/src/editor/index.ts +1 -0
  152. package/src/editor/publication-result.ts +58 -0
  153. package/src/editor/types.ts +10 -1
  154. package/src/editor/view-kit.ts +2 -1
  155. package/src/editor/view.test.ts +108 -0
  156. package/src/editor/view.ts +49 -7
  157. package/src/endpoint.test.ts +15 -0
  158. package/src/endpoint.ts +10 -0
  159. package/src/exclusion-groups.ts +8 -18
  160. package/src/flags.test.ts +88 -1
  161. package/src/flags.ts +29 -29
  162. package/src/global-runtime-reconciler.test.ts +271 -0
  163. package/src/goal-eligibility.test.ts +29 -0
  164. package/src/goal-eligibility.ts +17 -0
  165. package/src/goals.test.ts +144 -1
  166. package/src/goals.ts +97 -18
  167. package/src/guards.test.ts +99 -1
  168. package/src/guards.ts +34 -15
  169. package/src/handover-cache-order.test.ts +6 -3
  170. package/src/handover.test.ts +154 -8
  171. package/src/handover.ts +122 -12
  172. package/src/hash.ts +29 -54
  173. package/src/holdout.test.ts +26 -0
  174. package/src/hydration.test.ts +73 -2
  175. package/src/hydration.ts +18 -9
  176. package/src/index.ts +130 -7287
  177. package/src/init-analytics.test.ts +1694 -0
  178. package/src/init-anti-flicker.test.ts +1211 -0
  179. package/src/init-cart-attributes.test.ts +2003 -0
  180. package/src/init-config-resolution.test.ts +999 -0
  181. package/src/init-consent.test.ts +971 -0
  182. package/src/init-empty.test.ts +11 -5406
  183. package/src/init-integrations.test.ts +846 -0
  184. package/src/init-page-activation.test.ts +5064 -0
  185. package/src/init-qa-loading.test.ts +740 -0
  186. package/src/init-redirects.test.ts +2156 -0
  187. package/src/init-test-helpers.ts +287 -0
  188. package/src/inline-bootstrap.test.ts +184 -0
  189. package/src/inline-bootstrap.ts +73 -0
  190. package/src/js.test.ts +51 -1
  191. package/src/js.ts +86 -2
  192. package/src/money.test.ts +55 -1
  193. package/src/money.ts +104 -2
  194. package/src/money.web-parity.contract.test.ts +85 -0
  195. package/src/mutate.test.ts +474 -10
  196. package/src/mutate.ts +739 -230
  197. package/src/page-trigger.test.ts +218 -8
  198. package/src/performance.ts +10 -0
  199. package/src/persistence-decoders.ts +18 -0
  200. package/src/public-api.ts +1828 -0
  201. package/src/qa-assistant-state.test.ts +249 -3
  202. package/src/qa-assistant-state.ts +146 -6
  203. package/src/qa-assistant-view.test.ts +220 -2
  204. package/src/qa-assistant-view.ts +102 -5
  205. package/src/react/index.test.tsx +37 -0
  206. package/src/react/index.tsx +19 -3
  207. package/src/reassert-sentinel.test.ts +155 -1
  208. package/src/reassert-sentinel.ts +168 -8
  209. package/src/redirect.test.ts +541 -0
  210. package/src/redirect.ts +572 -0
  211. package/src/replay-recorder-entry.ts +115 -0
  212. package/src/replay-signal-state.ts +100 -0
  213. package/src/replay-signals.test.ts +249 -0
  214. package/src/replay-signals.ts +151 -0
  215. package/src/replay.test.ts +764 -0
  216. package/src/replay.ts +514 -0
  217. package/src/router.test.ts +61 -2
  218. package/src/router.ts +50 -12
  219. package/src/runtime-state.ts +507 -0
  220. package/src/runtime.test.ts +174 -7
  221. package/src/runtime.ts +88 -14
  222. package/src/sanitize.test.ts +34 -0
  223. package/src/sanitize.ts +23 -12
  224. package/src/shadow.test.ts +187 -0
  225. package/src/shadow.ts +99 -0
  226. package/src/shopify-cart.test.ts +733 -23
  227. package/src/shopify-cart.ts +402 -49
  228. package/src/storefront-vitals.test.ts +217 -0
  229. package/src/storefront-vitals.ts +206 -0
  230. package/src/surface-signals.ts +6 -0
  231. package/src/targeting-conformance.test.ts +9 -0
  232. package/src/targeting-diagnostics.ts +38 -8
  233. package/src/targeting-parity-corpus.json +559 -0
  234. package/src/targeting-parity-corpus.test.ts +18 -0
  235. package/src/targeting.detailed.test.ts +105 -0
  236. package/src/targeting.test.ts +211 -0
  237. package/src/targeting.ts +88 -207
  238. package/src/track.test.ts +147 -0
  239. package/src/track.ts +125 -26
  240. package/src/types.ts +120 -13
  241. package/src/wait.test.ts +23 -0
  242. package/src/wait.ts +129 -13
  243. package/src/weekday-delivery.test.ts +56 -0
  244. package/src/weekday-schedule-parity-corpus.json +92 -0
  245. package/src/weekday-schedule.test.ts +69 -0
  246. package/src/weekday-schedule.ts +20 -0
@@ -1,17 +1,21 @@
1
1
  import { afterEach, describe, expect, it, vi } from "vitest";
2
2
  import { __test } from "./index";
3
3
  import { clearRuntimeSignal, getSignals } from "./signals";
4
- import type { PageTargetingRule } from "./types";
4
+ import type { PageTargetingRule, SegmentCondition } from "./types";
5
5
 
6
6
  const signal = "page.trigger.pdp_overlay";
7
+ const segmentSignal = "custom.callback.ready";
7
8
 
8
- function installTrigger(triggerJs: string) {
9
+ function installTrigger(
10
+ triggerJs: string,
11
+ mode: "manual-callback" | "manual-api" = "manual-callback",
12
+ ) {
9
13
  const onChange = vi.fn();
10
14
  const cleanup = __test.installPageTriggerRule({
11
15
  id: "page_1",
12
16
  includeRules: [{ type: "simple", pattern: "/products/*", include: true }],
13
17
  trigger: {
14
- mode: "manual-callback",
18
+ mode,
15
19
  apiName: "pdp_overlay",
16
20
  signal,
17
21
  triggerJs,
@@ -20,26 +24,232 @@ function installTrigger(triggerJs: string) {
20
24
  return { cleanup, onChange };
21
25
  }
22
26
 
27
+ function installSegmentTrigger(triggerJs: string) {
28
+ const onChange = vi.fn();
29
+ const cleanup = __test.installActivateJsTrigger({
30
+ signal: segmentSignal,
31
+ operator: "equals",
32
+ value: true,
33
+ js: {
34
+ mode: "activate",
35
+ code: triggerJs,
36
+ },
37
+ } satisfies SegmentCondition, onChange);
38
+ return { cleanup, onChange };
39
+ }
40
+
41
+ function installRerunEnvironment() {
42
+ class FakeElement {
43
+ getAttribute() { return ""; }
44
+ closest() { return null; }
45
+ }
46
+
47
+ let mutationCallback: MutationCallback | null = null;
48
+ const disconnect = vi.fn();
49
+ const location = { href: "https://shop.test/products/example" };
50
+ vi.stubGlobal("Element", FakeElement);
51
+ vi.stubGlobal("location", location);
52
+ vi.stubGlobal("document", { documentElement: new FakeElement() });
53
+ vi.stubGlobal("window", {
54
+ clearInterval,
55
+ clearTimeout,
56
+ location,
57
+ setInterval,
58
+ setTimeout,
59
+ });
60
+ vi.stubGlobal("MutationObserver", class MutationObserver {
61
+ constructor(callback: MutationCallback) {
62
+ mutationCallback = callback;
63
+ }
64
+
65
+ observe = vi.fn();
66
+ disconnect = disconnect;
67
+ takeRecords = vi.fn(() => []);
68
+ });
69
+
70
+ return {
71
+ disconnect,
72
+ mutate() {
73
+ mutationCallback?.([
74
+ {
75
+ target: (globalThis as any).document.documentElement,
76
+ type: "attributes",
77
+ } as MutationRecord,
78
+ ], {} as MutationObserver);
79
+ },
80
+ };
81
+ }
82
+
23
83
  describe("Page trigger JS", () => {
24
84
  afterEach(() => {
85
+ vi.clearAllTimers();
86
+ vi.useRealTimers();
25
87
  clearRuntimeSignal(signal);
88
+ clearRuntimeSignal(segmentSignal);
26
89
  clearRuntimeSignal("page.trigger.segment_123_pdp_overlay");
27
90
  vi.unstubAllGlobals();
28
91
  });
29
92
 
30
- it("supports activate(), activate(true), and activate(false)", () => {
31
- let trigger = installTrigger("activate();");
93
+ it("re-runs an inactive manual callback after a DOM change and stops after activation", async () => {
94
+ vi.useFakeTimers();
95
+ const environment = installRerunEnvironment();
96
+ (globalThis as any).window.__eligible = false;
97
+ (globalThis as any).window.__attempts = 0;
98
+ const trigger = installTrigger(`
99
+ window.__attempts += 1;
100
+ if (window.__eligible) activate();
101
+ `);
102
+
103
+ expect((globalThis as any).window.__attempts).toBe(1);
104
+ expect(getSignals()[signal]).toBeUndefined();
105
+
106
+ (globalThis as any).window.__eligible = true;
107
+ environment.mutate();
108
+ await vi.advanceTimersByTimeAsync(250);
109
+
110
+ expect((globalThis as any).window.__attempts).toBe(2);
32
111
  expect(getSignals()[signal]).toBe(true);
33
112
  expect(trigger.onChange).toHaveBeenCalledTimes(1);
113
+ expect(environment.disconnect).toHaveBeenCalledTimes(1);
114
+
115
+ environment.mutate();
116
+ await vi.advanceTimersByTimeAsync(2_000);
117
+ expect((globalThis as any).window.__attempts).toBe(2);
34
118
  trigger.cleanup();
119
+ });
120
+
121
+ it("does not schedule re-runs when the initial callback activates", () => {
122
+ vi.useFakeTimers();
123
+ installRerunEnvironment();
124
+ const setIntervalSpy = vi.spyOn(globalThis, "setInterval");
125
+ const trigger = installTrigger("activate();");
35
126
 
36
- trigger = installTrigger("activate(true);");
37
127
  expect(getSignals()[signal]).toBe(true);
38
- expect(trigger.onChange).toHaveBeenCalledTimes(1);
128
+ expect(setIntervalSpy).not.toHaveBeenCalled();
129
+ expect(vi.getTimerCount()).toBe(0);
130
+ trigger.cleanup();
131
+ });
132
+
133
+ it("stops pending re-runs when the trigger lifecycle is cleaned up", async () => {
134
+ vi.useFakeTimers();
135
+ const environment = installRerunEnvironment();
136
+ (globalThis as any).window.__attempts = 0;
137
+ const trigger = installTrigger("window.__attempts += 1;");
138
+
139
+ expect((globalThis as any).window.__attempts).toBe(1);
140
+ expect(vi.getTimerCount()).toBe(2);
141
+ trigger.cleanup();
142
+
143
+ expect(environment.disconnect).toHaveBeenCalledTimes(1);
144
+ expect(vi.getTimerCount()).toBe(0);
145
+ await vi.advanceTimersByTimeAsync(10_000);
146
+ expect((globalThis as any).window.__attempts).toBe(1);
147
+ });
148
+
149
+ it("keeps legacy manual-api trigger JS one-shot", async () => {
150
+ vi.useFakeTimers();
151
+ installRerunEnvironment();
152
+ (globalThis as any).window.__attempts = 0;
153
+ const trigger = installTrigger("window.__attempts += 1;", "manual-api");
154
+
155
+ await vi.advanceTimersByTimeAsync(10_000);
156
+ expect((globalThis as any).window.__attempts).toBe(1);
157
+ expect(vi.getTimerCount()).toBe(0);
39
158
  trigger.cleanup();
159
+ });
160
+
161
+ it("stops a never-eligible callback after ten bounded re-runs", async () => {
162
+ vi.useFakeTimers();
163
+ const environment = installRerunEnvironment();
164
+ (globalThis as any).window.__attempts = 0;
165
+ const trigger = installTrigger("window.__attempts += 1;");
166
+
167
+ for (let attempt = 0; attempt < 10; attempt += 1) {
168
+ environment.mutate();
169
+ await vi.advanceTimersByTimeAsync(250);
170
+ }
171
+
172
+ expect((globalThis as any).window.__attempts).toBe(11);
173
+ expect(environment.disconnect).toHaveBeenCalledTimes(1);
174
+ expect(vi.getTimerCount()).toBe(0);
175
+
176
+ environment.mutate();
177
+ await vi.advanceTimersByTimeAsync(10_000);
178
+ expect((globalThis as any).window.__attempts).toBe(11);
179
+ trigger.cleanup();
180
+ });
181
+
182
+ it("counts throwing callbacks toward the re-run bound without an unhandled rejection", async () => {
183
+ vi.useFakeTimers();
184
+ const environment = installRerunEnvironment();
185
+ (globalThis as any).window.__attempts = 0;
186
+ const trigger = installTrigger(`
187
+ window.__attempts += 1;
188
+ throw new Error("not ready");
189
+ `);
190
+
191
+ for (let attempt = 0; attempt < 10; attempt += 1) {
192
+ environment.mutate();
193
+ await vi.advanceTimersByTimeAsync(250);
194
+ }
40
195
 
41
- trigger = installTrigger("activate(false);");
196
+ expect((globalThis as any).window.__attempts).toBe(11);
197
+ expect(environment.disconnect).toHaveBeenCalledTimes(1);
198
+ expect(vi.getTimerCount()).toBe(0);
199
+ trigger.cleanup();
200
+ });
201
+
202
+ it("continues bounded retries when a re-run throws without activating", async () => {
203
+ vi.useFakeTimers();
204
+ const environment = installRerunEnvironment();
205
+ (globalThis as any).window.__attempts = 0;
206
+ const trigger = installTrigger(`
207
+ window.__attempts += 1;
208
+ if (window.__attempts > 1) throw new Error("still not ready");
209
+ `);
210
+
211
+ for (let attempt = 0; attempt < 10; attempt += 1) {
212
+ environment.mutate();
213
+ await vi.advanceTimersByTimeAsync(250);
214
+ }
215
+
216
+ expect((globalThis as any).window.__attempts).toBe(11);
42
217
  expect(getSignals()[signal]).toBe(false);
218
+ expect(environment.disconnect).toHaveBeenCalledTimes(1);
219
+ expect(vi.getTimerCount()).toBe(0);
220
+ trigger.cleanup();
221
+ });
222
+
223
+ it.each([
224
+ ["no argument", "activate();", true],
225
+ ["true", "activate(true);", true],
226
+ ["one", "activate(1);", true],
227
+ ["non-empty string", 'activate("yes");', true],
228
+ ["false", "activate(false);", false],
229
+ ["zero", "activate(0);", false],
230
+ ["empty string", 'activate("");', false],
231
+ ["null", "activate(null);", false],
232
+ ["explicit undefined", "activate(undefined);", false],
233
+ ])("treats %s with documented truthy callback semantics", (_label, code, expected) => {
234
+ const trigger = installTrigger(code);
235
+ expect(getSignals()[signal]).toBe(expected);
236
+ expect(trigger.onChange).toHaveBeenCalledTimes(1);
237
+ trigger.cleanup();
238
+ });
239
+
240
+ it.each([
241
+ ["no argument", "activate();", true],
242
+ ["true", "activate(true);", true],
243
+ ["one", "activate(1);", true],
244
+ ["non-empty string", 'activate("yes");', true],
245
+ ["false", "activate(false);", false],
246
+ ["zero", "activate(0);", false],
247
+ ["empty string", 'activate("");', false],
248
+ ["null", "activate(null);", false],
249
+ ["explicit undefined", "activate(undefined);", false],
250
+ ])("uses the same %s semantics for segment callbacks", (_label, code, expected) => {
251
+ const trigger = installSegmentTrigger(code);
252
+ expect(getSignals()[segmentSignal]).toBe(expected);
43
253
  expect(trigger.onChange).toHaveBeenCalledTimes(1);
44
254
  trigger.cleanup();
45
255
  });
@@ -0,0 +1,10 @@
1
+ export function roundPerformance(value: number, precision = 1): number {
2
+ const factor = 10 ** precision;
3
+ return Math.round(value * factor) / factor;
4
+ }
5
+
6
+ export function navigationEntry(): PerformanceNavigationTiming | undefined {
7
+ return performance.getEntriesByType?.("navigation")?.[0] as
8
+ | PerformanceNavigationTiming
9
+ | undefined;
10
+ }
@@ -0,0 +1,18 @@
1
+ import { decodeCompactStore } from "./compact-store";
2
+
3
+ export type PersistedAssignmentRecord = { variationId: string; epoch: number; assignedAt: number };
4
+ export type PersistedGroupSelection = { experimentId: string; epoch: number };
5
+ export type CompactGroupSelections = Record<string, [string, number]>;
6
+
7
+ export function decodePersistedAssignments(local: string | null, cookie: string | null): Record<string, PersistedAssignmentRecord> {
8
+ const compact = { ...decodeCompactStore(cookie, 3), ...decodeCompactStore(local, 3) };
9
+ const out: Record<string, PersistedAssignmentRecord> = {};
10
+ for (const [id, value] of Object.entries(compact)) out[id] = {
11
+ variationId: value[0] as string, epoch: value[1] as number, assignedAt: value[2] as number,
12
+ };
13
+ return out;
14
+ }
15
+
16
+ export function decodePersistedGroupSelections(local: string | null, cookie: string | null): CompactGroupSelections {
17
+ return { ...decodeCompactStore(local, 2), ...decodeCompactStore(cookie, 2) } as CompactGroupSelections;
18
+ }