@pack/hydrogen 1.0.4 → 1.0.6-ab-test.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.
@@ -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 { Test, TestInput } from "./tests/test";
5
6
  /** @see https://shopify.dev/docs/custom-storefronts/hydrogen/data-fetching/cache#caching-strategies */
6
7
  type CachingStrategy = ReturnType<typeof CacheCustom>;
7
8
  interface EnvironmentOptions {
@@ -16,7 +17,7 @@ interface EnvironmentOptions {
16
17
  */
17
18
  waitUntil: ExecutionContext["waitUntil"];
18
19
  }
19
- interface CreatePackClientOptions extends EnvironmentOptions {
20
+ export interface CreatePackClientOptions extends EnvironmentOptions {
20
21
  apiUrl?: string;
21
22
  token?: string;
22
23
  storeId?: string;
@@ -29,6 +30,7 @@ type Variables = Record<string, any>;
29
30
  interface QueryOptions {
30
31
  variables?: Variables;
31
32
  cache?: CachingStrategy;
33
+ test?: TestInput;
32
34
  }
33
35
  interface QueryError {
34
36
  message: string;
@@ -39,18 +41,40 @@ interface QueryError {
39
41
  interface QueryResponse<T> {
40
42
  data: T | null;
41
43
  error: QueryError | null;
44
+ packTestInfo?: Test;
45
+ }
46
+ export interface PackCustomizerMeta {
47
+ environment?: string;
48
+ overlay?: {
49
+ src?: string;
50
+ version?: string;
51
+ };
52
+ [key: string]: any;
42
53
  }
43
54
  export interface Pack {
44
- isPreviewModeEnabled: () => boolean;
45
- session: PackSession;
46
- query: <T = any>(query: string, options?: QueryOptions) => Promise<QueryResponse<T>>;
47
- isValidEditToken: PackClient["isValidEditToken"];
55
+ abTest: Test | null | undefined;
56
+ /**
57
+ * @deprecated The method should not be used
58
+ */
48
59
  getPackSessionData(): {
49
60
  storeId: string;
50
61
  sessionId: string;
62
+ abTest: Test | null | undefined;
51
63
  isPreviewModeEnabled: boolean;
52
64
  customizerMeta: any;
53
65
  };
66
+ getPackContextData(): {
67
+ packStoreId: string;
68
+ packSessionId: string;
69
+ packAbTest: Test | null | undefined;
70
+ packIsPreviewMode: boolean;
71
+ packCustomizerMeta: PackCustomizerMeta | null;
72
+ };
73
+ handleRequest(request: Request): Promise<(response: Response) => void>;
74
+ isPreviewModeEnabled: () => boolean;
75
+ isValidEditToken: PackClient["isValidEditToken"];
76
+ query: <T = any>(query: string, options?: QueryOptions) => Promise<QueryResponse<T>>;
77
+ session: PackSession;
54
78
  }
55
79
  interface DefaultThemeData {
56
80
  data: Record<string, any>;
@@ -1 +1 @@
1
- {"version":3,"file":"create-pack-client.d.ts","sourceRoot":"","sources":["../src/create-pack-client.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAmB,MAAM,mBAAmB,CAAC;AACjE,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,uGAAuG;AACvG,KAAK,eAAe,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAEtD,UAAU,kBAAkB;IAC1B;;;OAGG;IACH,KAAK,EAAE,KAAK,CAAC;IACb;;;OAGG;IACH,SAAS,EAAE,gBAAgB,CAAC,WAAW,CAAC,CAAC;CAC1C;AAED,UAAU,uBAAwB,SAAQ,kBAAkB;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,WAAW,CAAC;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,0DAA0D;IAC1D,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;CACrC;AAED,KAAK,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAErC,UAAU,YAAY;IACpB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,KAAK,CAAC,EAAE,eAAe,CAAC;CACzB;AAED,UAAU,UAAU;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED,UAAU,aAAa,CAAC,CAAC;IACvB,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC;IACf,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,IAAI;IACnB,oBAAoB,EAAE,MAAM,OAAO,CAAC;IACpC,OAAO,EAAE,WAAW,CAAC;IACrB,KAAK,EAAE,CAAC,CAAC,GAAG,GAAG,EACb,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,YAAY,KACnB,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/B,gBAAgB,EAAE,UAAU,CAAC,kBAAkB,CAAC,CAAC;IAEjD,kBAAkB,IAAI;QACpB,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,EAAE,MAAM,CAAC;QAClB,oBAAoB,EAAE,OAAO,CAAC;QAC9B,cAAc,EAAE,GAAG,CAAC;KACrB,CAAC;CACH;AAED,UAAU,gBAAgB;IACxB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC3B;AAoJD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,IAAI,CAwIvE"}
1
+ {"version":3,"file":"create-pack-client.d.ts","sourceRoot":"","sources":["../src/create-pack-client.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,7 @@
1
1
  import { PackClient } from "@pack/client";
2
2
  import { CacheCustom, createWithCache } from "@shopify/hydrogen";
3
+ import cookie from "cookie";
4
+ import { getTestInfo, getTestFromQueryParams, getTestSession, getTestTargetingAttributesFromRequest, setTestHeaders, } from "./tests/test";
3
5
  /**
4
6
  * Create an SHA-256 hash as a hex string
5
7
  * @see https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest#converting_a_digest_to_a_hex_string
@@ -113,16 +115,70 @@ export function createPackClient(options) {
113
115
  const previewEnabled = !!session.get("previewEnabled");
114
116
  const previewEnvironment = session.get("environment");
115
117
  const clientContentEnvironment = previewEnvironment || contentEnvironment;
118
+ let packClient;
119
+ let testFromQueryParams = null;
120
+ let testInfoForRequest = undefined;
116
121
  if (!token && !defaultThemeData) {
117
122
  throw new Error("The Pack client token is missing or empty. Please provide a valid token or default theme data.");
118
123
  }
119
124
  if (!storeId) {
120
125
  throw new Error("The Pack Store ID is missing or empty. Please provide a valid Store ID.");
121
126
  }
127
+ const handleRequest = async (request) => {
128
+ testFromQueryParams = getTestFromQueryParams(request);
129
+ const testTargetAudienceAttributes = getTestTargetingAttributesFromRequest(request);
130
+ if (packClient) {
131
+ testInfoForRequest = await getTestInfo({
132
+ request,
133
+ testTargetAudienceAttributes,
134
+ packClient,
135
+ session,
136
+ });
137
+ }
138
+ return (response) => {
139
+ const hasExposedTestCookie = request.headers
140
+ .get("cookie")
141
+ ?.includes("exposedTest");
142
+ if (hasExposedTestCookie) {
143
+ // Clear the exposedTest cookie
144
+ response.headers.set("Set-Cookie", cookie.serialize("exposedTest", "", {
145
+ maxAge: 0,
146
+ }));
147
+ }
148
+ };
149
+ };
150
+ const getTestInfoForLoader = () => {
151
+ let testInfoForLoader = undefined;
152
+ if (testInfoForRequest?.isFirstExposure) {
153
+ const { isFirstExposure, ...testInfo } = testInfoForRequest;
154
+ testInfoForLoader = testInfo;
155
+ }
156
+ return testInfoForLoader;
157
+ };
122
158
  if (!token) {
123
159
  return {
124
- session,
160
+ abTest: getTestSession(session, testFromQueryParams, previewEnabled),
161
+ handleRequest,
162
+ getPackSessionData: () => {
163
+ return {
164
+ storeId: storeId,
165
+ sessionId: session.id,
166
+ abTest: getTestSession(session, testFromQueryParams, previewEnabled),
167
+ isPreviewModeEnabled: previewEnabled,
168
+ customizerMeta: session.get("customizerMeta"),
169
+ };
170
+ },
171
+ getPackContextData: () => {
172
+ return {
173
+ packStoreId: storeId,
174
+ packSessionId: session.id,
175
+ packAbTest: getTestSession(session, testFromQueryParams, previewEnabled),
176
+ packIsPreviewMode: previewEnabled,
177
+ packCustomizerMeta: session.get("customizerMeta"),
178
+ };
179
+ },
125
180
  isPreviewModeEnabled: () => previewEnabled,
181
+ isValidEditToken: () => new Promise(() => false),
126
182
  async query(query, { variables } = {}) {
127
183
  if (!defaultThemeData?.data) {
128
184
  console.warn("Invalid default theme data provided to Pack client.");
@@ -131,18 +187,10 @@ export function createPackClient(options) {
131
187
  const data = resolveQuery({ query, variables, defaultThemeData });
132
188
  return { data: data, error: null };
133
189
  },
134
- isValidEditToken: () => new Promise(() => false),
135
- getPackSessionData: () => {
136
- return {
137
- storeId: storeId,
138
- sessionId: session.id,
139
- isPreviewModeEnabled: previewEnabled,
140
- customizerMeta: session.get("customizerMeta"),
141
- };
142
- },
190
+ session,
143
191
  };
144
192
  }
145
- const packClient = new PackClient({
193
+ packClient = new PackClient({
146
194
  // Use apiUrl, it is configured
147
195
  // Use active API URL if preview mode is enabled
148
196
  // Otherwise, Live PackClient uses its internal configuration
@@ -157,16 +205,40 @@ export function createPackClient(options) {
157
205
  sessionId: session.id,
158
206
  clientName: "HydrogenClient",
159
207
  });
160
- console.log("*** packClient.apiUrl", packClient.apiUrl);
161
208
  return {
162
- session,
209
+ abTest: getTestSession(session, testFromQueryParams, previewEnabled),
210
+ getPackSessionData: () => {
211
+ return {
212
+ storeId: storeId,
213
+ sessionId: session.id,
214
+ abTest: getTestSession(session, testFromQueryParams, previewEnabled),
215
+ isPreviewModeEnabled: previewEnabled,
216
+ customizerMeta: session.get("customizerMeta"),
217
+ };
218
+ },
219
+ getPackContextData: () => {
220
+ return {
221
+ packStoreId: storeId,
222
+ packSessionId: session.id,
223
+ packAbTest: getTestSession(session, testFromQueryParams, previewEnabled),
224
+ packIsPreviewMode: previewEnabled,
225
+ packCustomizerMeta: session.get("customizerMeta"),
226
+ };
227
+ },
228
+ handleRequest,
163
229
  isPreviewModeEnabled: () => previewEnabled,
164
- async query(query, { variables, cache: strategy = cacheCustom } = {}) {
230
+ isValidEditToken: (token) => packClient.isValidEditToken(token),
231
+ async query(query, { variables, cache: strategy = cacheCustom, test } = {}) {
232
+ let headers = {};
165
233
  const withCache = createWithCache({
166
234
  cache,
167
235
  waitUntil,
168
236
  });
169
- let headers = {};
237
+ headers = setTestHeaders(headers, {
238
+ previewEnabled,
239
+ testInfoForRequest,
240
+ testFromQueryParams: testFromQueryParams || test,
241
+ });
170
242
  const queryVariables = variables ? { ...variables } : {};
171
243
  if (previewEnabled) {
172
244
  queryVariables.version = "CURRENT";
@@ -174,23 +246,27 @@ export function createPackClient(options) {
174
246
  else {
175
247
  queryVariables.version = "PUBLISHED";
176
248
  }
249
+ const testInfoForLoader = getTestInfoForLoader();
177
250
  // Preview mode always bypasses the cache
178
251
  if (previewEnabled) {
179
252
  try {
180
- return await packClient.fetch(query, {
181
- variables: queryVariables,
182
- headers: headers,
183
- });
253
+ return {
254
+ ...(await packClient.fetch(query, {
255
+ variables: queryVariables,
256
+ headers: headers,
257
+ })),
258
+ packTestInfo: testInfoForLoader,
259
+ };
184
260
  }
185
261
  catch (error) {
186
- return { error, data: {} };
262
+ return { error: error, data: null };
187
263
  }
188
264
  }
189
265
  const queryHash = await getCacheKey(withCache, query, token, {
190
266
  variables: queryVariables,
191
267
  headers,
192
268
  });
193
- return withCache(queryHash, strategy, async () => {
269
+ const response = await withCache(queryHash, strategy, async () => {
194
270
  try {
195
271
  return await packClient.fetch(query, {
196
272
  variables: queryVariables,
@@ -198,18 +274,13 @@ export function createPackClient(options) {
198
274
  });
199
275
  }
200
276
  catch (error) {
201
- return { error, data: {} };
277
+ return { error, data: null };
202
278
  }
203
279
  });
280
+ return response.error
281
+ ? response
282
+ : { ...response, packTestInfo: testInfoForLoader };
204
283
  },
205
- isValidEditToken: (token) => packClient.isValidEditToken(token),
206
- getPackSessionData: () => {
207
- return {
208
- storeId: storeId,
209
- sessionId: session.id,
210
- isPreviewModeEnabled: previewEnabled,
211
- customizerMeta: session.get("customizerMeta"),
212
- };
213
- },
284
+ session,
214
285
  };
215
286
  }
@@ -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,CASnB"}
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,8 +1,10 @@
1
1
  import { packlytics } from "@pack/packlytics";
2
2
  export async function handleRequest(pack, request, handleRequest) {
3
+ const packHandleResponse = await pack.handleRequest(request);
3
4
  const response = await packlytics(pack, request, () => {
4
5
  return handleRequest(request);
5
6
  });
7
+ packHandleResponse(response);
6
8
  response.headers.append("powered-by", "Shopify, Hydrogen + Pack Digital");
7
9
  response.headers.append("Set-Cookie", await pack.session.commit());
8
10
  return response;
package/dist/index.d.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  import { handleRequest } from "./handle-request";
2
2
  import { usePackCookies } from "./session/usePackCookies";
3
3
  import { PackSession } from "./session/session";
4
- import { createPackClient } from "./create-pack-client";
4
+ import { Pack, createPackClient } from "./create-pack-client";
5
5
  import { action as previewModeAction, loader as previewModeLoader } from "./preview/preview-mode";
6
- export { PackSession, createPackClient, handleRequest, previewModeAction, previewModeLoader, usePackCookies, };
6
+ import { PackTestContext, usePackTestContext, PackTestProvider, PackTestRoute, useAbTest, useAbTestId, useAbTestHandle, useAbTestSessionId, useAbTestVariantId, useAbTestVariantHandle } from "./tests";
7
+ export type { Pack };
8
+ export { PackSession, createPackClient, handleRequest, previewModeAction, previewModeLoader, usePackCookies, PackTestContext, usePackTestContext, PackTestProvider, PackTestRoute, useAbTest, useAbTestId, useAbTestHandle, useAbTestSessionId, useAbTestVariantId, useAbTestVariantHandle, };
7
9
  //# 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,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EACL,MAAM,IAAI,iBAAiB,EAC3B,MAAM,IAAI,iBAAiB,EAC5B,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACL,WAAW,EACX,gBAAgB,EAChB,aAAa,EACb,iBAAiB,EACjB,iBAAiB,EACjB,cAAc,GACf,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,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"}
package/dist/index.js CHANGED
@@ -3,4 +3,5 @@ import { usePackCookies } from "./session/usePackCookies";
3
3
  import { PackSession } from "./session/session";
4
4
  import { createPackClient } from "./create-pack-client";
5
5
  import { action as previewModeAction, loader as previewModeLoader, } from "./preview/preview-mode";
6
- export { PackSession, createPackClient, handleRequest, previewModeAction, previewModeLoader, usePackCookies, };
6
+ 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, };
@@ -1 +1 @@
1
- {"version":3,"file":"preview-mode.d.ts","sourceRoot":"","sources":["../../src/preview/preview-mode.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,cAAc,EACpB,MAAM,2BAA2B,CAAC;AAInC,KAAK,YAAY,GAAG,CAAC,IAAI,EACvB,IAAI,EAAE,IAAI,EACV,IAAI,CAAC,EAAE,MAAM,GAAG,YAAY,KACzB,aAAa,CAAC,IAAI,CAAC,CAAC;AAEzB,KAAK,gBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,YAAY,KAAK,QAAQ,CAAC;AAIhF,KAAK,aAAa,CAAC,CAAC,GAAG,OAAO,IAAI,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,GAAG;IACzD,IAAI,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC;CACpB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,IAAI,EAAE,YAYlB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,QAAQ,EAAE,gBAetB,CAAC;AAoBF;;;GAGG;AACH,eAAO,MAAM,MAAM,EAAE,cAkBpB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,MAAM,EAAE,cAwCpB,CAAC"}
1
+ {"version":3,"file":"preview-mode.d.ts","sourceRoot":"","sources":["../../src/preview/preview-mode.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,cAAc,EACpB,MAAM,2BAA2B,CAAC;AAKnC,KAAK,YAAY,GAAG,CAAC,IAAI,EACvB,IAAI,EAAE,IAAI,EACV,IAAI,CAAC,EAAE,MAAM,GAAG,YAAY,KACzB,aAAa,CAAC,IAAI,CAAC,CAAC;AAEzB,KAAK,gBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,YAAY,KAAK,QAAQ,CAAC;AAIhF,KAAK,aAAa,CAAC,CAAC,GAAG,OAAO,IAAI,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,GAAG;IACzD,IAAI,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC;CACpB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,IAAI,EAAE,YAYlB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,QAAQ,EAAE,gBAetB,CAAC;AAoBF;;;GAGG;AACH,eAAO,MAAM,MAAM,EAAE,cAkBpB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,MAAM,EAAE,cA0CpB,CAAC"}
@@ -88,7 +88,9 @@ export const loader = async function ({ request, context }) {
88
88
  }
89
89
  let customizerMetaJson;
90
90
  try {
91
- customizerMetaJson = customizerMeta ? JSON.parse(customizerMeta) : null;
91
+ customizerMetaJson = customizerMeta
92
+ ? JSON.parse(customizerMeta)
93
+ : null;
92
94
  }
93
95
  catch (_error) {
94
96
  customizerMetaJson = null;
@@ -1,5 +1,5 @@
1
1
  import { PACK_USER_CONSENT_COOKIE_ID } from "../constants";
2
- const cookie = require("cookie");
2
+ import cookie from "cookie";
3
3
  const tokenHash = "xxxx-4xxx-xxxx-xxxxxxxxxxxx";
4
4
  export function hasUserConsent(request) {
5
5
  const cookies = cookie.parse(request.headers.get("Cookie"));
@@ -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;AAqBD,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;CAClC;AAED,MAAM,WAAW,QAAS,SAAQ,IAAI;IACpC,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,wBAAsB,WAAW,CAAC,EAChC,OAAO,EACP,4BAA4B,EAC5B,UAAU,EACV,OAAO,GACR,EAAE,kBAAkB,GAAG,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAuHpD;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,OAgCF"}
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;AAqBD,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;CAClC;AAED,MAAM,WAAW,QAAS,SAAQ,IAAI;IACpC,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AA+DD,wBAAsB,WAAW,CAAC,EAChC,OAAO,EACP,4BAA4B,EAC5B,UAAU,EACV,OAAO,GACR,EAAE,kBAAkB,GAAG,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CA4DpD;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"}
@@ -16,99 +16,89 @@ const QUERY_TEST_VARIANT_IS_RUNNING = `#graphql
16
16
  testVariantIsRunning(handle: $handle, testId: $testId)
17
17
  }
18
18
  `;
19
- export async function getTestInfo({ request, testTargetAudienceAttributes, packClient, session, }) {
20
- let testInfo = undefined;
21
- if (session) {
22
- // Request directly to API if it is using CDN to reduce latency
23
- let isUsingCdn = false;
24
- if (packClient.apiUrl === "https://apicdn.packdigital.com/graphql") {
25
- packClient.apiUrl = "https://app.packdigital.com/graphql";
26
- isUsingCdn = true;
27
- }
28
- const packClientFetchTestByRules = packClient.fetch(QUERY_TEST_BY_RULES, {
19
+ async function packClientFetchTestByRules(packClient, testTargetAudienceAttributes) {
20
+ let resp = undefined;
21
+ // Check if are using CDN or a custom endpoint if so
22
+ // Request directly to API if it is using CDN to reduce latency
23
+ const isUsingCdn = packClient.apiUrl === "https://apicdn.packdigital.com/graphql";
24
+ if (isUsingCdn) {
25
+ packClient.apiUrl = "https://app.packdigital.com/graphql";
26
+ }
27
+ try {
28
+ const packClientFetchTestByRules = await packClient.fetch(QUERY_TEST_BY_RULES, {
29
29
  variables: {
30
30
  testTargetAudienceAttributes,
31
31
  },
32
32
  });
33
- const fetchTestByRulesSetTestInfoAsFirstExposure = async () => {
34
- const resp = await packClientFetchTestByRules;
35
- const responseTest = resp.data?.testByRules;
36
- if (responseTest) {
37
- testInfo = {
38
- ...responseTest,
39
- isFirstExposure: true,
40
- };
41
- }
42
- else {
43
- session.set("test", undefined);
44
- }
45
- };
46
- const testSession = session.get("test");
47
- let exposedTest = undefined;
48
- const exposedTestCookieString = cookie.parse(request.headers.get("cookie") || "")?.exposedTest;
49
- if (exposedTestCookieString) {
50
- exposedTest = JSON.parse(exposedTestCookieString);
51
- }
52
- // If there is no assigned test on the session and an exposed test in the
53
- // incoming request cookie it means that the user was exposed to a test
54
- // for the first time previously.
55
- if (!testSession && exposedTest) {
56
- // Verify test is still running
57
- const resp = await packClient.fetch(QUERY_TEST_VARIANT_IS_RUNNING, {
58
- variables: {
59
- handle: exposedTest.testVariant.handle,
60
- testId: exposedTest.id,
61
- },
62
- });
63
- // If the test is still running, set testInfo to the exposed test
64
- if (!!resp.data.testVariantIsRunning) {
65
- const { id, handle, testVariant } = exposedTest;
66
- testInfo = {
67
- id,
68
- handle,
69
- testVariant,
70
- };
71
- session.set("test", {
72
- data: testInfo,
73
- });
74
- }
75
- else {
76
- // If the test is not running anymore, fetch a new test for the user
77
- await fetchTestByRulesSetTestInfoAsFirstExposure();
78
- }
33
+ if (packClientFetchTestByRules.data?.testByRules) {
34
+ resp = {
35
+ ...packClientFetchTestByRules.data?.testByRules,
36
+ isFirstExposure: true,
37
+ };
79
38
  }
80
- else {
81
- if (!testSession ||
82
- !(testSession.data?.id && testSession.data?.testVariant)) {
83
- // If there is no assigned test on the session, check if there is a test
84
- // for attributes, then assign to session and return value.
85
- await fetchTestByRulesSetTestInfoAsFirstExposure();
86
- }
87
- else {
88
- // If there is test on session, check if the test on session is still running.
89
- const packClientFetchTestVariantIsRunning = packClient.fetch(QUERY_TEST_VARIANT_IS_RUNNING, {
90
- variables: {
91
- handle: testSession.data.testVariant.handle,
92
- testId: testSession.data.id,
93
- },
94
- });
95
- const resp = await packClientFetchTestVariantIsRunning;
96
- if (!!resp.data.testVariantIsRunning) {
97
- // If true, set testInfo to the testSession assigned test.
98
- testInfo = testSession.data;
99
- }
100
- else {
101
- // If not running, clear test session and fetch a new test for the user
102
- session.set("test", undefined);
103
- await fetchTestByRulesSetTestInfoAsFirstExposure();
104
- }
105
- }
39
+ }
40
+ catch (e) {
41
+ console.error(e);
42
+ }
43
+ // Return to CDN
44
+ if (isUsingCdn) {
45
+ packClient.apiUrl = "https://apicdn.packdigital.com/graphql";
46
+ }
47
+ return resp;
48
+ }
49
+ async function packClientCheckTestIsRunning(packClient, testSession) {
50
+ try {
51
+ const resp = await packClient.fetch(QUERY_TEST_VARIANT_IS_RUNNING, {
52
+ variables: {
53
+ handle: testSession.testVariant.handle,
54
+ testId: testSession.id,
55
+ },
56
+ });
57
+ return !!resp.data.testVariantIsRunning;
58
+ }
59
+ catch (e) {
60
+ console.error(e);
61
+ }
62
+ return false;
63
+ }
64
+ export async function getTestInfo({ request, testTargetAudienceAttributes, packClient, session, }) {
65
+ let testInfo = undefined;
66
+ let exposedTest = undefined;
67
+ if (!session) {
68
+ return testInfo;
69
+ }
70
+ const testSession = session.get("test");
71
+ const exposedTestCookieString = cookie.parse(request.headers.get("cookie") || "")?.exposedTest;
72
+ if (exposedTestCookieString) {
73
+ exposedTest = JSON.parse(exposedTestCookieString);
74
+ }
75
+ // If there is test on session, check if the test on session is still running.
76
+ if (testSession && testSession.data?.id && testSession.data?.testVariant) {
77
+ const testVariantIsRunning = await packClientCheckTestIsRunning(packClient, testSession.data);
78
+ // If true, set testInfo to the testSession assigned test.
79
+ if (testVariantIsRunning) {
80
+ testInfo = testSession.data;
106
81
  }
107
- if (isUsingCdn) {
108
- // Return to CDN
109
- packClient.apiUrl = "https://apicdn.packdigital.com/graphql";
82
+ }
83
+ else if (exposedTest) {
84
+ // If there is no assigned test on the session and an exposed test in the
85
+ // incoming request cookie it means that the user was exposed to a test previously.
86
+ // Verify test is still running
87
+ const testVariantIsRunning = await packClientCheckTestIsRunning(packClient, exposedTest);
88
+ // If the test is still running, set testInfo to the exposed test
89
+ if (testVariantIsRunning) {
90
+ testInfo = exposedTest;
110
91
  }
111
92
  }
93
+ // If the testInfo is not setup that means that is not a valid test
94
+ // on the session or on the cookie, then fetch a new test for the user
95
+ if (!testInfo) {
96
+ testInfo = await packClientFetchTestByRules(packClient, testTargetAudienceAttributes);
97
+ }
98
+ // save this test on the session
99
+ session.set("test", {
100
+ data: testInfo,
101
+ });
112
102
  return testInfo;
113
103
  }
114
104
  export function getTestSession(session, testFromQueryParams, previewEnabled) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pack/hydrogen",
3
3
  "description": "Pack Hydrogen",
4
- "version": "1.0.4",
4
+ "version": "1.0.6-ab-test.0",
5
5
  "exports": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
7
7
  "engines": {
@@ -22,14 +22,17 @@
22
22
  "dist"
23
23
  ],
24
24
  "dependencies": {
25
- "@pack/client": "^1.0.1",
25
+ "@pack/client": "^1.0.1-ab-test.0",
26
26
  "@pack/packlytics": "^1.0.1",
27
- "@shopify/hydrogen": "^2023.10.2"
27
+ "@shopify/hydrogen": "^2023.10.2",
28
+ "cookie": "^0.6.0",
29
+ "js-cookie": "^3.0.5"
28
30
  },
29
31
  "devDependencies": {
30
32
  "@remix-run/server-runtime": "^2.0.0",
31
33
  "@shopify/oxygen-workers-types": "^4.0.0",
32
34
  "@shopify/remix-oxygen": "^2.0.1",
35
+ "@types/js-cookie": "^3.0.6",
33
36
  "@types/node": "^20.11.17"
34
37
  },
35
38
  "peerDependencies": {