@pack/hydrogen 1.0.6-ab-test.9 → 1.0.6-ab-test.11

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.
@@ -1,4 +1,5 @@
1
1
  export declare const PACK_COOKIE_ID = "__pack";
2
2
  export declare const PACK_USER_CONSENT_COOKIE_ID = "__pack_user_consent";
3
+ export declare const PACK_TEST_COOKIE_ID = "__pack_test";
3
4
  export declare const PACK_COOKIE_MAX_AGE: number;
4
5
  //# sourceMappingURL=constants.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,cAAc,WAAW,CAAC;AACvC,eAAO,MAAM,2BAA2B,wBAAwB,CAAC;AACjE,eAAO,MAAM,mBAAmB,QAAqB,CAAC"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,cAAc,WAAW,CAAC;AACvC,eAAO,MAAM,2BAA2B,wBAAwB,CAAC;AACjE,eAAO,MAAM,mBAAmB,gBAAgB,CAAC;AACjD,eAAO,MAAM,mBAAmB,QAAqB,CAAC"}
package/dist/constants.js CHANGED
@@ -1,3 +1,4 @@
1
1
  export const PACK_COOKIE_ID = "__pack";
2
2
  export const PACK_USER_CONSENT_COOKIE_ID = "__pack_user_consent";
3
+ export const PACK_TEST_COOKIE_ID = "__pack_test";
3
4
  export const PACK_COOKIE_MAX_AGE = 60 * 60 * 24 * 360; // 1 year
@@ -2,6 +2,7 @@
2
2
  import { PackClient } from "@pack/client";
3
3
  import { CacheCustom } from "@shopify/hydrogen";
4
4
  import { PackSession } from "./session/session";
5
+ import { PackTestSession } from "./session/test-session";
5
6
  import { Test, TestInput } from "./tests/test";
6
7
  /** @see https://shopify.dev/docs/custom-storefronts/hydrogen/data-fetching/cache#caching-strategies */
7
8
  type CachingStrategy = ReturnType<typeof CacheCustom>;
@@ -22,6 +23,7 @@ export interface CreatePackClientOptions extends EnvironmentOptions {
22
23
  token?: string;
23
24
  storeId?: string;
24
25
  session: PackSession;
26
+ testSession: PackTestSession;
25
27
  contentEnvironment?: string;
26
28
  /** Default theme data to use when no token is provided */
27
29
  defaultThemeData?: DefaultThemeData;
@@ -75,6 +77,7 @@ export interface Pack {
75
77
  isValidEditToken: PackClient["isValidEditToken"];
76
78
  query: <T = any>(query: string, options?: QueryOptions) => Promise<QueryResponse<T>>;
77
79
  session: PackSession;
80
+ testSession: PackTestSession;
78
81
  }
79
82
  interface DefaultThemeData {
80
83
  data: Record<string, any>;
@@ -1 +1 @@
1
- {"version":3,"file":"create-pack-client.d.ts","sourceRoot":"","sources":["../src/create-pack-client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"create-pack-client.d.ts","sourceRoot":"","sources":["../src/create-pack-client.ts"],"names":[],"mappings":"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"}
@@ -111,7 +111,7 @@ function extractTopLevelField(query) {
111
111
  return "";
112
112
  }
113
113
  export function createPackClient(options) {
114
- const { cache, waitUntil, session, contentEnvironment, storeId, token, apiUrl, defaultThemeData, } = options;
114
+ const { cache, waitUntil, session, testSession, contentEnvironment, storeId, token, apiUrl, defaultThemeData, } = options;
115
115
  const previewEnabled = !!session.get("previewEnabled");
116
116
  const previewEnvironment = session.get("environment");
117
117
  const clientContentEnvironment = previewEnvironment || contentEnvironment;
@@ -132,7 +132,7 @@ export function createPackClient(options) {
132
132
  request,
133
133
  testTargetAudienceAttributes,
134
134
  packClient,
135
- session,
135
+ testSession, // Use dedicated test session instead
136
136
  token,
137
137
  });
138
138
  }
@@ -142,8 +142,10 @@ export function createPackClient(options) {
142
142
  ?.includes("exposedTest");
143
143
  if (hasExposedTestCookie) {
144
144
  // Clear the exposedTest cookie
145
- response.headers.set("Set-Cookie", cookie.serialize("exposedTest", "", {
145
+ response.headers.append("Set-Cookie", cookie.serialize("exposedTest", "", {
146
146
  maxAge: 0,
147
+ expires: new Date(0),
148
+ path: "/",
147
149
  }));
148
150
  }
149
151
  };
@@ -158,13 +160,13 @@ export function createPackClient(options) {
158
160
  };
159
161
  if (!token) {
160
162
  return {
161
- abTest: getTestSession(session, testFromQueryParams, previewEnabled),
163
+ abTest: getTestSession(testSession, testFromQueryParams, previewEnabled),
162
164
  handleRequest,
163
165
  getPackSessionData: () => {
164
166
  return {
165
167
  storeId: storeId,
166
168
  sessionId: session.id,
167
- abTest: getTestSession(session, testFromQueryParams, previewEnabled),
169
+ abTest: getTestSession(testSession, testFromQueryParams, previewEnabled),
168
170
  isPreviewModeEnabled: previewEnabled,
169
171
  customizerMeta: session.get("customizerMeta"),
170
172
  };
@@ -173,7 +175,7 @@ export function createPackClient(options) {
173
175
  return {
174
176
  packStoreId: storeId,
175
177
  packSessionId: session.id,
176
- packAbTest: getTestSession(session, testFromQueryParams, previewEnabled),
178
+ packAbTest: getTestSession(testSession, testFromQueryParams, previewEnabled),
177
179
  packIsPreviewMode: previewEnabled,
178
180
  packCustomizerMeta: session.get("customizerMeta"),
179
181
  };
@@ -189,6 +191,7 @@ export function createPackClient(options) {
189
191
  return { data: data, error: null };
190
192
  },
191
193
  session,
194
+ testSession,
192
195
  };
193
196
  }
194
197
  packClient = new PackClient({
@@ -207,12 +210,12 @@ export function createPackClient(options) {
207
210
  clientName: "HydrogenClient",
208
211
  });
209
212
  return {
210
- abTest: getTestSession(session, testFromQueryParams, previewEnabled),
213
+ abTest: getTestSession(testSession, testFromQueryParams, previewEnabled),
211
214
  getPackSessionData: () => {
212
215
  return {
213
216
  storeId: storeId,
214
217
  sessionId: session.id,
215
- abTest: getTestSession(session, testFromQueryParams, previewEnabled),
218
+ abTest: getTestSession(testSession, testFromQueryParams, previewEnabled),
216
219
  isPreviewModeEnabled: previewEnabled,
217
220
  customizerMeta: session.get("customizerMeta"),
218
221
  };
@@ -221,7 +224,7 @@ export function createPackClient(options) {
221
224
  return {
222
225
  packStoreId: storeId,
223
226
  packSessionId: session.id,
224
- packAbTest: getTestSession(session, testFromQueryParams, previewEnabled),
227
+ packAbTest: getTestSession(testSession, testFromQueryParams, previewEnabled),
225
228
  packIsPreviewMode: previewEnabled,
226
229
  packCustomizerMeta: session.get("customizerMeta"),
227
230
  };
@@ -283,5 +286,6 @@ export function createPackClient(options) {
283
286
  : { ...response, packTestInfo: testInfoForLoader };
284
287
  },
285
288
  session,
289
+ testSession,
286
290
  };
287
291
  }
@@ -1 +1 @@
1
- {"version":3,"file":"handle-request.d.ts","sourceRoot":"","sources":["../src/handle-request.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAG5C,wBAAsB,aAAa,CACjC,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,OAAO,EAChB,aAAa,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,GACrD,OAAO,CAAC,QAAQ,CAAC,CAanB"}
1
+ {"version":3,"file":"handle-request.d.ts","sourceRoot":"","sources":["../src/handle-request.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAG5C,wBAAsB,aAAa,CACjC,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,OAAO,EAChB,aAAa,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,GACrD,OAAO,CAAC,QAAQ,CAAC,CAqBnB"}
@@ -6,6 +6,12 @@ export async function handleRequest(pack, request, handleRequest) {
6
6
  });
7
7
  packHandleResponse(response);
8
8
  response.headers.append("powered-by", "Shopify, Hydrogen + Pack Digital");
9
+ // Always commit main session
9
10
  response.headers.append("Set-Cookie", await pack.session.commit());
11
+ // Only commit test session if there are actual changes
12
+ // This reduces the number of Set-Cookie headers and potential race conditions
13
+ if (pack.testSession.hasChanges()) {
14
+ response.headers.append("Set-Cookie", await pack.testSession.commit());
15
+ }
10
16
  return response;
11
17
  }
package/dist/index.d.ts CHANGED
@@ -1,9 +1,10 @@
1
1
  import { handleRequest } from "./handle-request";
2
2
  import { usePackCookies } from "./session/usePackCookies";
3
3
  import { PackSession } from "./session/session";
4
+ import { PackTestSession } from "./session/test-session";
4
5
  import { Pack, createPackClient } from "./create-pack-client";
5
6
  import { action as previewModeAction, loader as previewModeLoader } from "./preview/preview-mode";
6
7
  import { PackTestContext, usePackTestContext, PackTestProvider, PackTestRoute, useAbTest, useAbTestId, useAbTestHandle, useAbTestSessionId, useAbTestVariantId, useAbTestVariantHandle } from "./tests";
7
8
  export type { Pack };
8
- export { PackSession, createPackClient, handleRequest, previewModeAction, previewModeLoader, usePackCookies, PackTestContext, usePackTestContext, PackTestProvider, PackTestRoute, useAbTest, useAbTestId, useAbTestHandle, useAbTestSessionId, useAbTestVariantId, useAbTestVariantHandle, };
9
+ export { PackSession, PackTestSession, createPackClient, handleRequest, previewModeAction, previewModeLoader, usePackCookies, PackTestContext, usePackTestContext, PackTestProvider, PackTestRoute, useAbTest, useAbTestId, useAbTestHandle, useAbTestSessionId, useAbTestVariantId, useAbTestVariantHandle, };
9
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,IAAI,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EACL,MAAM,IAAI,iBAAiB,EAC3B,MAAM,IAAI,iBAAiB,EAC5B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,gBAAgB,EAChB,aAAa,EACb,SAAS,EACT,WAAW,EACX,eAAe,EACf,kBAAkB,EAClB,kBAAkB,EAClB,sBAAsB,EACvB,MAAM,SAAS,CAAC;AAEjB,YAAY,EAAE,IAAI,EAAE,CAAC;AAErB,OAAO,EACL,WAAW,EACX,gBAAgB,EAChB,aAAa,EACb,iBAAiB,EACjB,iBAAiB,EACjB,cAAc,EACd,eAAe,EACf,kBAAkB,EAClB,gBAAgB,EAChB,aAAa,EACb,SAAS,EACT,WAAW,EACX,eAAe,EACf,kBAAkB,EAClB,kBAAkB,EAClB,sBAAsB,GACvB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,IAAI,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EACL,MAAM,IAAI,iBAAiB,EAC3B,MAAM,IAAI,iBAAiB,EAC5B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,gBAAgB,EAChB,aAAa,EACb,SAAS,EACT,WAAW,EACX,eAAe,EACf,kBAAkB,EAClB,kBAAkB,EAClB,sBAAsB,EACvB,MAAM,SAAS,CAAC;AAEjB,YAAY,EAAE,IAAI,EAAE,CAAC;AAErB,OAAO,EACL,WAAW,EACX,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,iBAAiB,EACjB,iBAAiB,EACjB,cAAc,EACd,eAAe,EACf,kBAAkB,EAClB,gBAAgB,EAChB,aAAa,EACb,SAAS,EACT,WAAW,EACX,eAAe,EACf,kBAAkB,EAClB,kBAAkB,EAClB,sBAAsB,GACvB,CAAC"}
package/dist/index.js CHANGED
@@ -1,7 +1,8 @@
1
1
  import { handleRequest } from "./handle-request";
2
2
  import { usePackCookies } from "./session/usePackCookies";
3
3
  import { PackSession } from "./session/session";
4
+ import { PackTestSession } from "./session/test-session";
4
5
  import { createPackClient } from "./create-pack-client";
5
6
  import { action as previewModeAction, loader as previewModeLoader, } from "./preview/preview-mode";
6
7
  import { PackTestContext, usePackTestContext, PackTestProvider, PackTestRoute, useAbTest, useAbTestId, useAbTestHandle, useAbTestSessionId, useAbTestVariantId, useAbTestVariantHandle, } from "./tests";
7
- export { PackSession, createPackClient, handleRequest, previewModeAction, previewModeLoader, usePackCookies, PackTestContext, usePackTestContext, PackTestProvider, PackTestRoute, useAbTest, useAbTestId, useAbTestHandle, useAbTestSessionId, useAbTestVariantId, useAbTestVariantHandle, };
8
+ export { PackSession, PackTestSession, createPackClient, handleRequest, previewModeAction, previewModeLoader, usePackCookies, PackTestContext, usePackTestContext, PackTestProvider, PackTestRoute, useAbTest, useAbTestId, useAbTestHandle, useAbTestSessionId, useAbTestVariantId, useAbTestVariantHandle, };
@@ -0,0 +1,18 @@
1
+ import { type Session, type SessionStorage } from "@shopify/remix-oxygen";
2
+ import { Test } from "../tests/test";
3
+ export declare class PackTestSession {
4
+ #private;
5
+ readonly id: string;
6
+ constructor(id: string, sessionStorage: SessionStorage, session: Session);
7
+ static init(request: Request, secrets: string[]): Promise<PackTestSession>;
8
+ getTestData(): Test | undefined;
9
+ getExpireAt(): string | undefined;
10
+ setTestData(testData: Test | undefined): void;
11
+ setExpireAt(expireAt: string | undefined): void;
12
+ clearTestData(): void;
13
+ hasTestData(): boolean;
14
+ hasChanges(): boolean;
15
+ commit(): Promise<string>;
16
+ destroy(): Promise<string>;
17
+ }
18
+ //# sourceMappingURL=test-session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-session.d.ts","sourceRoot":"","sources":["../../src/session/test-session.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,OAAO,EACZ,KAAK,cAAc,EACpB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAerC,qBAAa,eAAe;;IAC1B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;gBAMR,EAAE,EAAE,MAAM,EAAE,cAAc,EAAE,cAAc,EAAE,OAAO,EAAE,OAAO;WAY3D,IAAI,CACf,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,MAAM,EAAE,GAChB,OAAO,CAAC,eAAe,CAAC;IA2B3B,WAAW,IAAI,IAAI,GAAG,SAAS;IAI/B,WAAW,IAAI,MAAM,GAAG,SAAS;IAIjC,WAAW,CAAC,QAAQ,EAAE,IAAI,GAAG,SAAS,GAAG,IAAI;IAK7C,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IAK/C,aAAa,IAAI,IAAI;IAMrB,WAAW,IAAI,OAAO;IAItB,UAAU,IAAI,OAAO;IAerB,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC;IAazB,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC;CAM3B"}
@@ -0,0 +1,97 @@
1
+ import { createCookie, createCookieSessionStorage, } from "@shopify/remix-oxygen";
2
+ import { PACK_COOKIE_MAX_AGE, PACK_TEST_COOKIE_ID } from "../constants";
3
+ function isSafari(userAgent) {
4
+ if (!userAgent)
5
+ return false;
6
+ return (/Safari\//.test(userAgent) &&
7
+ !/Chrome\//.test(userAgent) &&
8
+ !/Chromium\//.test(userAgent) &&
9
+ !/Edg\//.test(userAgent) &&
10
+ !/Firefox\//.test(userAgent));
11
+ }
12
+ export class PackTestSession {
13
+ id;
14
+ #session;
15
+ #sessionStorage;
16
+ #isDirty = false;
17
+ #initialData = {};
18
+ constructor(id, sessionStorage, session) {
19
+ this.id = id;
20
+ this.#session = session;
21
+ this.#sessionStorage = sessionStorage;
22
+ // Store initial state to track changes
23
+ this.#initialData = {
24
+ test_data: this.#session.get("test_data"),
25
+ test_expire_at: this.#session.get("test_expire_at"),
26
+ };
27
+ }
28
+ static async init(request, secrets) {
29
+ const userAgent = request.headers.get("User-Agent");
30
+ const storage = createCookieSessionStorage({
31
+ cookie: createCookie(PACK_TEST_COOKIE_ID, {
32
+ secure: isSafari(userAgent) && process.env.NODE_ENV === "development"
33
+ ? false
34
+ : true,
35
+ secrets,
36
+ sameSite: "lax",
37
+ maxAge: PACK_COOKIE_MAX_AGE,
38
+ path: "/",
39
+ }),
40
+ });
41
+ const session = await storage.getSession(request.headers.get("Cookie"));
42
+ let sessionId = session.get("test_session_id");
43
+ if (!sessionId) {
44
+ sessionId = crypto.randomUUID();
45
+ session.set("test_session_id", sessionId);
46
+ }
47
+ return new this(sessionId, storage, session);
48
+ }
49
+ getTestData() {
50
+ return this.#session.get("test_data");
51
+ }
52
+ getExpireAt() {
53
+ return this.#session.get("test_expire_at");
54
+ }
55
+ setTestData(testData) {
56
+ this.#isDirty = true;
57
+ this.#session.set("test_data", testData);
58
+ }
59
+ setExpireAt(expireAt) {
60
+ this.#isDirty = true;
61
+ this.#session.set("test_expire_at", expireAt);
62
+ }
63
+ clearTestData() {
64
+ this.#isDirty = true;
65
+ this.#session.unset("test_data");
66
+ this.#session.unset("test_expire_at");
67
+ }
68
+ hasTestData() {
69
+ return this.#session.has("test_data");
70
+ }
71
+ hasChanges() {
72
+ // Check if data has actually changed
73
+ const currentTestData = this.getTestData();
74
+ const currentExpireAt = this.getExpireAt();
75
+ // Compare with initial state
76
+ const testDataChanged = JSON.stringify(currentTestData) !==
77
+ JSON.stringify(this.#initialData.test_data);
78
+ const expireAtChanged = currentExpireAt !== this.#initialData.test_expire_at;
79
+ return testDataChanged || expireAtChanged || this.#isDirty;
80
+ }
81
+ commit() {
82
+ // Reset dirty flag after commit
83
+ this.#isDirty = false;
84
+ // Update initial data to current state
85
+ this.#initialData = {
86
+ test_data: this.getTestData(),
87
+ test_expire_at: this.getExpireAt(),
88
+ };
89
+ return this.#sessionStorage.commitSession(this.#session);
90
+ }
91
+ destroy() {
92
+ this.#isDirty = true;
93
+ this.#session.unset("test_data");
94
+ this.#session.unset("test_expire_at");
95
+ return this.#sessionStorage.destroySession(this.#session);
96
+ }
97
+ }
@@ -1,5 +1,5 @@
1
1
  import { PackClient } from "@pack/client";
2
- import { PackSession } from "../session/session";
2
+ import { PackTestSession } from "../session/test-session";
3
3
  export interface TestInput {
4
4
  testId?: string;
5
5
  testHandle?: string;
@@ -28,14 +28,14 @@ export interface GetTestInfoOptions {
28
28
  request: Request;
29
29
  testTargetAudienceAttributes: TestTargetAudienceAttributes | null;
30
30
  packClient: PackClient;
31
- session: PackSession | undefined;
31
+ testSession: PackTestSession;
32
32
  token: string;
33
33
  }
34
34
  export interface TestInfo extends Test {
35
35
  isFirstExposure?: boolean;
36
36
  }
37
- export declare function getTestInfo({ request, testTargetAudienceAttributes, packClient, session, token, }: GetTestInfoOptions): Promise<TestInfo | undefined>;
38
- export declare function getTestSession(session: PackSession | undefined, testFromQueryParams: TestInput | null, previewEnabled: boolean): Test | null | undefined;
37
+ export declare function getTestInfo({ request, testTargetAudienceAttributes, packClient, testSession, token, }: GetTestInfoOptions): Promise<TestInfo | undefined>;
38
+ export declare function getTestSession(testSession?: PackTestSession, testFromQueryParams?: TestInput | null, previewEnabled?: boolean): Test | null | undefined;
39
39
  export declare function getTestTargetingAttributesFromRequest(request: Request): TestTargetAudienceAttributes;
40
40
  export declare function getTestFromQueryParams(request: Request): TestInput | null;
41
41
  export declare function setTestHeaders(headers: any, options: {
@@ -1 +1 @@
1
- {"version":3,"file":"test.d.ts","sourceRoot":"","sources":["../../src/tests/test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAGjD,MAAM,WAAW,SAAS;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,IAAI;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE;QACX,EAAE,EAAE,MAAM,CAAC;QACX,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;CACH;AAED,MAAM,WAAW,4BAA4B;IAC3C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAQD,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,4BAA4B,EAAE,4BAA4B,GAAG,IAAI,CAAC;IAClE,UAAU,EAAE,UAAU,CAAC;IACvB,OAAO,EAAE,WAAW,GAAG,SAAS,CAAC;IACjC,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,QAAS,SAAQ,IAAI;IACpC,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AA2FD,wBAAsB,WAAW,CAAC,EAChC,OAAO,EACP,4BAA4B,EAC5B,UAAU,EACV,OAAO,EACP,KAAK,GACN,EAAE,kBAAkB,GAAG,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAgGpD;AAED,wBAAgB,cAAc,CAC5B,OAAO,EAAE,WAAW,GAAG,SAAS,EAChC,mBAAmB,EAAE,SAAS,GAAG,IAAI,EACrC,cAAc,EAAE,OAAO,GACtB,IAAI,GAAG,IAAI,GAAG,SAAS,CA8BzB;AAED,wBAAgB,qCAAqC,CACnD,OAAO,EAAE,OAAO,GACf,4BAA4B,CA0B9B;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,GAAG,IAAI,CA2CzE;AAED,wBAAgB,cAAc,CAC5B,OAAO,EAAE,GAAG,EACZ,OAAO,EAAE;IACP,cAAc,EAAE,OAAO,CAAC;IACxB,kBAAkB,CAAC,EAAE,QAAQ,CAAC;IAC9B,mBAAmB,CAAC,EAAE,SAAS,CAAC;CACjC,OA+BF"}
1
+ {"version":3,"file":"test.d.ts","sourceRoot":"","sources":["../../src/tests/test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAG1D,MAAM,WAAW,SAAS;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,IAAI;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE;QACX,EAAE,EAAE,MAAM,CAAC;QACX,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;CACH;AAED,MAAM,WAAW,4BAA4B;IAC3C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAQD,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,4BAA4B,EAAE,4BAA4B,GAAG,IAAI,CAAC;IAClE,UAAU,EAAE,UAAU,CAAC;IACvB,WAAW,EAAE,eAAe,CAAC;IAC7B,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,QAAS,SAAQ,IAAI;IACpC,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AA2FD,wBAAsB,WAAW,CAAC,EAChC,OAAO,EACP,4BAA4B,EAC5B,UAAU,EACV,WAAW,EACX,KAAK,GACN,EAAE,kBAAkB,GAAG,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CA2GpD;AAED,wBAAgB,cAAc,CAC5B,WAAW,CAAC,EAAE,eAAe,EAC7B,mBAAmB,GAAE,SAAS,GAAG,IAAW,EAC5C,cAAc,GAAE,OAAe,GAC9B,IAAI,GAAG,IAAI,GAAG,SAAS,CA+BzB;AAED,wBAAgB,qCAAqC,CACnD,OAAO,EAAE,OAAO,GACf,4BAA4B,CA0B9B;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,GAAG,IAAI,CA2CzE;AAED,wBAAgB,cAAc,CAC5B,OAAO,EAAE,GAAG,EACZ,OAAO,EAAE;IACP,cAAc,EAAE,OAAO,CAAC;IACxB,kBAAkB,CAAC,EAAE,QAAQ,CAAC;IAC9B,mBAAmB,CAAC,EAAE,SAAS,CAAC;CACjC,OA+BF"}
@@ -69,18 +69,41 @@ function getExpireAtDate() {
69
69
  expireAt.setHours(expireAt.getHours() + 4); // 4 hours
70
70
  return expireAt;
71
71
  }
72
- export async function getTestInfo({ request, testTargetAudienceAttributes, packClient, session, token, }) {
72
+ export async function getTestInfo({ request, testTargetAudienceAttributes, packClient, testSession, token, }) {
73
73
  let testInfo = undefined;
74
74
  let exposedTest = undefined;
75
- if (!session) {
75
+ if (!testSession) {
76
76
  return testInfo;
77
77
  }
78
- const testSession = session.get("test");
78
+ // Retrieve simplified test data and expiry
79
+ const testSessionData = testSession.getTestData();
80
+ const testSessionExpireAt = testSession.getExpireAt();
79
81
  const exposedTestCookieString = cookie.parse(request.headers.get("cookie") || "")?.exposedTest;
82
+ // If we already have a valid test in the session, use it without setting isFirstExposure
83
+ if (testSessionData?.id && testSessionData?.testVariant) {
84
+ // First check if test hasn't expired by time
85
+ if (!testSessionExpireAt || new Date(testSessionExpireAt) >= new Date()) {
86
+ // Then verify the test is still running in the system
87
+ try {
88
+ const testStillRunning = await packClientCheckTestIsRunning(packClient, testSessionData);
89
+ if (testStillRunning) {
90
+ return testSessionData;
91
+ }
92
+ }
93
+ catch (error) {
94
+ // Don't clear session on API error, just log it
95
+ console.error("Error checking if test is running, using cached data", error);
96
+ return testSessionData; // Still use the cached data if API fails
97
+ }
98
+ }
99
+ // If we reach here, the test is either expired or not running
100
+ // Clear the session data
101
+ testSession.clearTestData();
102
+ }
80
103
  if (exposedTestCookieString) {
81
104
  exposedTest = JSON.parse(exposedTestCookieString);
82
105
  }
83
- if (exposedTest && !testSession) {
106
+ if (exposedTest && !testSessionData) {
84
107
  // If there is no assigned test on the session and an exposed test in the
85
108
  // incoming request cookie it means that the user was exposed to a test previously.
86
109
  // Verify test is still running
@@ -93,51 +116,34 @@ export async function getTestInfo({ request, testTargetAudienceAttributes, packC
93
116
  handle,
94
117
  testVariant,
95
118
  };
119
+ // Store simplified data
96
120
  // Only set the test to the session on exposure
97
- session.set("test", {
98
- data: testInfo,
99
- expireAt: getExpireAtDate(),
100
- });
121
+ testSession.setTestData(testInfo);
122
+ testSession.setExpireAt(getExpireAtDate().toISOString());
101
123
  }
102
124
  else {
103
- // If the test is not running, fetch a new test for the client
125
+ // If the test is not running, clear session and fetch a new test for the client
126
+ testSession.clearTestData();
127
+ // Then fetch new test
104
128
  testInfo = await packClientFetchTestByRules(packClient, testTargetAudienceAttributes, token);
105
129
  }
106
130
  }
131
+ else if (testSessionData) {
132
+ // This case handles if testSessionData exists but might be expired (handled above)
133
+ // or if there was no exposedTest cookie. We already returned if it was valid.
134
+ // If logic reaches here and testSessionData exists, it implies it was expired.
135
+ // First clear the session
136
+ testSession.clearTestData();
137
+ // Then fetch new test
138
+ testInfo = await packClientFetchTestByRules(packClient, testTargetAudienceAttributes, token);
139
+ }
107
140
  else {
108
- // If there is no exposed test in the incoming request cookie
109
- if (testSession && testSession.data?.id && testSession.data?.testVariant) {
110
- // If there is a test on session, check if the test on session has expired
111
- if (!testSession.expireAt ||
112
- new Date(testSession.expireAt) < new Date()) {
113
- // If there is test on session, check if the test on session is still running
114
- const testVariantIsRunning = await packClientCheckTestIsRunning(packClient, testSession.data);
115
- // If true, set testInfo to the testSession assigned test.
116
- if (testVariantIsRunning) {
117
- testInfo = testSession.data;
118
- session.set("test", {
119
- data: testInfo,
120
- expireAt: getExpireAtDate(),
121
- });
122
- }
123
- else {
124
- // If not running, clear test session and fetch a new test for the client
125
- session.set("test", undefined);
126
- testInfo = await packClientFetchTestByRules(packClient, testTargetAudienceAttributes, token);
127
- }
128
- }
129
- else {
130
- testInfo = testSession.data;
131
- }
132
- }
133
- else {
134
- // If there is no test on session and no exposed test, check for a new test for client
135
- testInfo = await packClientFetchTestByRules(packClient, testTargetAudienceAttributes, token);
136
- }
141
+ // If there is no test on session and no exposed test, check for a new test for client
142
+ testInfo = await packClientFetchTestByRules(packClient, testTargetAudienceAttributes, token);
137
143
  }
138
144
  return testInfo;
139
145
  }
140
- export function getTestSession(session, testFromQueryParams, previewEnabled) {
146
+ export function getTestSession(testSession, testFromQueryParams = null, previewEnabled = false) {
141
147
  if (testFromQueryParams) {
142
148
  return {
143
149
  id: testFromQueryParams.testId || "",
@@ -158,11 +164,12 @@ export function getTestSession(session, testFromQueryParams, previewEnabled) {
158
164
  },
159
165
  };
160
166
  }
161
- if (!session) {
167
+ if (!testSession) {
162
168
  return undefined;
163
169
  }
164
- const testSession = session.get("test");
165
- return testSession?.data || null;
170
+ // Read simplified test data from the dedicated test session
171
+ const testSessionData = testSession.getTestData();
172
+ return testSessionData || null;
166
173
  }
167
174
  export function getTestTargetingAttributesFromRequest(request) {
168
175
  let testTargetingAttributes = {};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pack/hydrogen",
3
3
  "description": "Pack Hydrogen",
4
- "version": "1.0.6-ab-test.9",
4
+ "version": "1.0.6-ab-test.11",
5
5
  "exports": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
7
7
  "engines": {