@pack/hydrogen 3.1.1 → 3.2.1

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 CHANGED
@@ -1 +1,54 @@
1
- # hydrogen
1
+ # `@pack/hydrogen`
2
+
3
+ Hydrogen runtime integration for Pack content, preview, A/B testing, and server-side error tracking.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @pack/hydrogen
9
+ ```
10
+
11
+ ## Main exports
12
+
13
+ - `createPackClient`
14
+ - `handleRequest`
15
+ - `PackSession`, `PackTestSession`
16
+ - `previewModeAction`, `previewModeLoader`
17
+ - A/B test helpers (`PackTestProvider`, `useAbTest`, etc.)
18
+
19
+ ## Minimal setup
20
+
21
+ ```ts
22
+ import {
23
+ createPackClient,
24
+ handleRequest,
25
+ } from '@pack/hydrogen';
26
+ import { createServerErrorHandler } from '@pack/errors';
27
+
28
+ // Optional if you want route-level error capture in addition to handleRequest wrapping.
29
+ // Duplicate reports for the same Error instance are deduplicated automatically.
30
+ export const handleError = createServerErrorHandler();
31
+
32
+ export default async function fetch(request: Request, env: Env, ctx: ExecutionContext) {
33
+ const pack = createPackClient({
34
+ cache: caches.default,
35
+ waitUntil: ctx.waitUntil.bind(ctx),
36
+ storeId: env.PACK_STORE_ID,
37
+ token: env.PACK_API_TOKEN,
38
+ errorTracking: {
39
+ dsn: env.PUBLIC_PACK_ERROR_TRACKING_DSN,
40
+ },
41
+ session,
42
+ testSession,
43
+ });
44
+
45
+ return handleRequest(pack, request, (req) => storefrontHandler(req));
46
+ }
47
+ ```
48
+
49
+ ## Development
50
+
51
+ ```bash
52
+ yarn
53
+ yarn build
54
+ ```
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { PackClient } from '@pack/client';
2
+ import { ErrorTrackingOptions } from '@pack/errors';
2
3
  import { CacheCustom } from '@shopify/hydrogen';
3
4
  import { SessionStorage, Session, ActionFunctionArgs, LoaderFunctionArgs } from 'react-router';
4
5
  import * as react from 'react';
@@ -85,6 +86,8 @@ interface CreatePackClientOptions extends EnvironmentOptions {
85
86
  i18n?: I18nOptions;
86
87
  /** Default theme data to use when no token is provided */
87
88
  defaultThemeData?: DefaultThemeData;
89
+ /** Configuration for server-side error tracking */
90
+ errorTracking?: ErrorTrackingOptions;
88
91
  /**
89
92
  * Initial request to extract query parameters from.
90
93
  * If not provided, it will be captured from the first handleRequest call.
@@ -142,12 +145,14 @@ interface Pack {
142
145
  packAbTest: Test$1 | null | undefined;
143
146
  packIsPreviewMode: boolean;
144
147
  packCustomizerMeta: PackCustomizerMeta | null;
148
+ packErrorTracking?: ErrorTrackingOptions;
145
149
  };
146
150
  handleRequest(request: Request): Promise<(response: Response) => void>;
147
151
  isPreviewModeEnabled: () => boolean;
148
152
  isValidEditToken: PackClient["isValidEditToken"];
149
153
  query: <T = any>(query: string, options: QueryOptions) => Promise<QueryResponse<T>>;
150
154
  session: PackSession;
155
+ errorTracking?: ErrorTrackingOptions;
151
156
  testSession: PackTestSession;
152
157
  }
153
158
  interface DefaultThemeData {
package/dist/index.js CHANGED
@@ -4356,6 +4356,9 @@ var require_dist = __commonJS({
4356
4356
  }
4357
4357
  });
4358
4358
 
4359
+ // src/handle-request.ts
4360
+ import { sendServerErrorEvent } from "@pack/errors";
4361
+
4359
4362
  // ../packlytics/dist/utils/get-packlytics-id.js
4360
4363
  function sha256(ascii) {
4361
4364
  function rightRotate(value, amount) {
@@ -4668,11 +4671,28 @@ async function packlytics(pack, request, next) {
4668
4671
  }
4669
4672
 
4670
4673
  // src/handle-request.ts
4674
+ function getServerStatusError(request, status) {
4675
+ let pathname = request.url;
4676
+ try {
4677
+ pathname = new URL(request.url).pathname;
4678
+ } catch {
4679
+ }
4680
+ return new Error(`HTTP ${status} response for ${request.method} ${pathname}`);
4681
+ }
4671
4682
  async function handleRequest(pack, request, handleRequest2) {
4672
4683
  const packHandleResponse = await pack.handleRequest(request);
4673
- const response = await packlytics(pack, request, () => {
4674
- return handleRequest2(request);
4675
- });
4684
+ let response;
4685
+ try {
4686
+ response = await packlytics(pack, request, () => {
4687
+ return handleRequest2(request);
4688
+ });
4689
+ } catch (error) {
4690
+ sendServerErrorEvent(error, request, pack);
4691
+ throw error;
4692
+ }
4693
+ if (response.status >= 500 && !request.signal.aborted) {
4694
+ sendServerErrorEvent(getServerStatusError(request, response.status), request, pack);
4695
+ }
4676
4696
  packHandleResponse(response);
4677
4697
  response.headers.append("powered-by", "Shopify, Hydrogen + Pack Digital");
4678
4698
  response.headers.append("Set-Cookie", await pack.session.commit());
@@ -5026,11 +5046,26 @@ var cookie2 = __toESM(require_cookie(), 1);
5026
5046
  // src/tests/local-test-resolver.ts
5027
5047
  var import_json_rules_engine = __toESM(require_dist(), 1);
5028
5048
  var import_debug2 = __toESM(require_browser(), 1);
5029
- var debug2 = (0, import_debug2.default)("pack:ab-testing:local-resolver");
5030
- function getImpressionSectionIds(testVariant) {
5049
+
5050
+ // src/tests/impression.ts
5051
+ function getVariantImpressionSectionIds(testVariant) {
5031
5052
  const sectionIds = testVariant?.sectionTestVariants?.map((variant) => variant?.section?.id).filter((id) => Boolean(id)) || [];
5032
5053
  return [...new Set(sectionIds)];
5033
5054
  }
5055
+ function getImpressionSectionIdsForVariant(test, testVariant) {
5056
+ const selectedVariantSectionIds = getVariantImpressionSectionIds(testVariant);
5057
+ if (selectedVariantSectionIds.length > 0) {
5058
+ return selectedVariantSectionIds;
5059
+ }
5060
+ if (testVariant?.handle !== "control") {
5061
+ return [];
5062
+ }
5063
+ const fallbackSectionIds = test?.testVariants?.filter((variant) => variant.handle !== "control").flatMap((variant) => getVariantImpressionSectionIds(variant)) || [];
5064
+ return [...new Set(fallbackSectionIds)];
5065
+ }
5066
+
5067
+ // src/tests/local-test-resolver.ts
5068
+ var debug2 = (0, import_debug2.default)("pack:ab-testing:local-resolver");
5034
5069
  var LocalTestResolver = class {
5035
5070
  testRules = [];
5036
5071
  constructor() {
@@ -5134,7 +5169,10 @@ var LocalTestResolver = class {
5134
5169
  })
5135
5170
  );
5136
5171
  if (accumulatedPercentage >= randomPercentage) {
5137
- const impressionSectionIds = getImpressionSectionIds(variant);
5172
+ const impressionSectionIds = getImpressionSectionIdsForVariant(
5173
+ selectedTest,
5174
+ variant
5175
+ );
5138
5176
  const result = {
5139
5177
  id: selectedTest.id,
5140
5178
  handle: selectedTest.handle,
@@ -5346,10 +5384,6 @@ var QUERY_TESTS_BY_RULES = `#graphql
5346
5384
  }
5347
5385
  }
5348
5386
  `;
5349
- function getImpressionSectionIds2(testVariant) {
5350
- const sectionIds = testVariant?.sectionTestVariants?.map((variant) => variant?.section?.id).filter((id) => Boolean(id)) || [];
5351
- return [...new Set(sectionIds)];
5352
- }
5353
5387
  var localTestResolver = new LocalTestResolver();
5354
5388
  function generateTestRulesCacheKey(storeId, contentEnvironment) {
5355
5389
  return `pack-tests:${storeId}:${contentEnvironment || "default"}`;
@@ -5563,7 +5597,10 @@ async function packClientFetchTestByRules(packClient, testTargetAudienceAttribut
5563
5597
  (v) => v.id === currentTestData.testVariant.id
5564
5598
  );
5565
5599
  if (currentVariant) {
5566
- const impressionSectionIds = getImpressionSectionIds2(currentVariant);
5600
+ const impressionSectionIds = getImpressionSectionIdsForVariant(
5601
+ freshTestData,
5602
+ currentVariant
5603
+ );
5567
5604
  const refreshedTest = {
5568
5605
  id: freshTestData.id,
5569
5606
  handle: freshTestData.handle,
@@ -5575,7 +5612,10 @@ async function packClientFetchTestByRules(packClient, testTargetAudienceAttribut
5575
5612
  impression: impressionSectionIds.length > 0 ? { sectionIds: impressionSectionIds } : void 0,
5576
5613
  isFirstExposure: void 0
5577
5614
  };
5578
- const { isFirstExposure: isFirstExposure2, ...testDataWithoutFlag2 } = refreshedTest;
5615
+ const {
5616
+ isFirstExposure: _isFirstExposure2,
5617
+ ...testDataWithoutFlag2
5618
+ } = refreshedTest;
5579
5619
  testSession.setTestData(testDataWithoutFlag2);
5580
5620
  testSession.setExpireAt(getExpireAtDate().toISOString());
5581
5621
  return refreshedTest;
@@ -5620,7 +5660,10 @@ async function packClientFetchTestByRules(packClient, testTargetAudienceAttribut
5620
5660
  (v) => v.id === exposedTest.testVariant?.id
5621
5661
  );
5622
5662
  if (exposedVariant) {
5623
- const impressionSectionIds = getImpressionSectionIds2(exposedVariant);
5663
+ const impressionSectionIds = getImpressionSectionIdsForVariant(
5664
+ freshTestData,
5665
+ exposedVariant
5666
+ );
5624
5667
  const result2 = {
5625
5668
  id: freshTestData.id,
5626
5669
  handle: freshTestData.handle,
@@ -5632,7 +5675,7 @@ async function packClientFetchTestByRules(packClient, testTargetAudienceAttribut
5632
5675
  impression: impressionSectionIds.length > 0 ? { sectionIds: impressionSectionIds } : void 0,
5633
5676
  isFirstExposure: void 0
5634
5677
  };
5635
- const { isFirstExposure: isFirstExposure2, ...testDataWithoutFlag2 } = result2;
5678
+ const { isFirstExposure: _isFirstExposure2, ...testDataWithoutFlag2 } = result2;
5636
5679
  testSession.setTestData(testDataWithoutFlag2);
5637
5680
  testSession.setExpireAt(getExpireAtDate().toISOString());
5638
5681
  return result2;
@@ -5687,7 +5730,7 @@ async function packClientFetchTestByRules(packClient, testTargetAudienceAttribut
5687
5730
  variantHandle: result.testVariant.handle
5688
5731
  })
5689
5732
  );
5690
- const { isFirstExposure, ...testDataWithoutFlag } = result;
5733
+ const { isFirstExposure: _isFirstExposure, ...testDataWithoutFlag } = result;
5691
5734
  testSession.setTestData(testDataWithoutFlag);
5692
5735
  testSession.setExpireAt(getExpireAtDate().toISOString());
5693
5736
  debug3("[Pack Test] Returning test info with first exposure flag");
@@ -6160,6 +6203,7 @@ function createPackClient(options) {
6160
6203
  token,
6161
6204
  apiUrl,
6162
6205
  defaultThemeData,
6206
+ errorTracking,
6163
6207
  i18n,
6164
6208
  request
6165
6209
  } = options;
@@ -6234,7 +6278,8 @@ function createPackClient(options) {
6234
6278
  previewEnabled
6235
6279
  ),
6236
6280
  packIsPreviewMode: previewEnabled,
6237
- packCustomizerMeta: session.get("customizerMeta")
6281
+ packCustomizerMeta: session.get("customizerMeta"),
6282
+ packErrorTracking: errorTracking
6238
6283
  };
6239
6284
  },
6240
6285
  isPreviewModeEnabled: () => previewEnabled,
@@ -6249,6 +6294,7 @@ function createPackClient(options) {
6249
6294
  return { data, error: null };
6250
6295
  },
6251
6296
  session,
6297
+ errorTracking,
6252
6298
  testSession
6253
6299
  };
6254
6300
  }
@@ -6298,7 +6344,8 @@ function createPackClient(options) {
6298
6344
  previewEnabled
6299
6345
  ),
6300
6346
  packIsPreviewMode: previewEnabled,
6301
- packCustomizerMeta: session.get("customizerMeta")
6347
+ packCustomizerMeta: session.get("customizerMeta"),
6348
+ packErrorTracking: errorTracking
6302
6349
  };
6303
6350
  },
6304
6351
  handleRequest: handleRequest2,
@@ -6363,7 +6410,7 @@ function createPackClient(options) {
6363
6410
  }
6364
6411
  }
6365
6412
  if (testInfoForRequest?.isFirstExposure) {
6366
- const { isFirstExposure, ...testInfo } = testInfoForRequest;
6413
+ const { isFirstExposure: _isFirstExposure, ...testInfo } = testInfoForRequest;
6367
6414
  testInfoForLoader = testInfo;
6368
6415
  }
6369
6416
  headers = setTestHeaders(headers, {
@@ -6425,6 +6472,7 @@ function createPackClient(options) {
6425
6472
  }
6426
6473
  },
6427
6474
  session,
6475
+ errorTracking,
6428
6476
  testSession
6429
6477
  };
6430
6478
  }