@shopify/hydrogen 0.7.1 → 0.8.3

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 (100) hide show
  1. package/dist/esnext/components/CartEstimatedCost/CartEstimatedCost.client.d.ts +1 -1
  2. package/dist/esnext/components/CartEstimatedCost/CartEstimatedCost.client.js +1 -1
  3. package/dist/esnext/components/CartLineQuantityAdjustButton/CartLineQuantityAdjustButton.js +4 -0
  4. package/dist/esnext/components/LocalizationProvider/index.d.ts +1 -0
  5. package/dist/esnext/components/LocalizationProvider/index.js +1 -0
  6. package/dist/esnext/components/ProductPrice/ProductPrice.client.d.ts +1 -3
  7. package/dist/esnext/components/ProductPrice/ProductPrice.client.js +1 -3
  8. package/dist/esnext/components/ShopPayButton/ShopPayButton.client.js +1 -1
  9. package/dist/esnext/components/UnitPrice/UnitPrice.client.d.ts +1 -2
  10. package/dist/esnext/components/UnitPrice/UnitPrice.client.js +1 -2
  11. package/dist/esnext/components/index.d.ts +1 -0
  12. package/dist/esnext/components/index.js +1 -0
  13. package/dist/esnext/entry-client.js +4 -6
  14. package/dist/esnext/entry-server.js +70 -47
  15. package/dist/esnext/foundation/RenderCacheProvider/RenderCacheContext.d.ts +2 -0
  16. package/dist/esnext/foundation/RenderCacheProvider/RenderCacheContext.js +4 -0
  17. package/dist/esnext/foundation/RenderCacheProvider/RenderCacheProvider.d.ts +2 -0
  18. package/dist/esnext/foundation/RenderCacheProvider/RenderCacheProvider.js +5 -0
  19. package/dist/esnext/foundation/RenderCacheProvider/hook.d.ts +11 -0
  20. package/dist/esnext/foundation/RenderCacheProvider/hook.js +34 -0
  21. package/dist/esnext/foundation/RenderCacheProvider/index.d.ts +1 -0
  22. package/dist/esnext/foundation/RenderCacheProvider/index.js +1 -0
  23. package/dist/esnext/foundation/RenderCacheProvider/types.d.ts +18 -0
  24. package/dist/esnext/foundation/RenderCacheProvider/types.js +1 -0
  25. package/dist/esnext/foundation/Router/DefaultRoutes.d.ts +3 -1
  26. package/dist/esnext/foundation/Router/DefaultRoutes.js +2 -2
  27. package/dist/esnext/foundation/ServerStateProvider/ServerStateProvider.client.js +4 -2
  28. package/dist/esnext/foundation/ShopifyProvider/ShopifyServerProvider.server.d.ts +1 -3
  29. package/dist/esnext/foundation/ShopifyProvider/ShopifyServerProvider.server.js +2 -4
  30. package/dist/esnext/foundation/ShopifyProvider/types.d.ts +0 -5
  31. package/dist/esnext/foundation/useQuery/hooks.d.ts +8 -6
  32. package/dist/esnext/foundation/useQuery/hooks.js +13 -14
  33. package/dist/esnext/foundation/useQuery/index.d.ts +0 -1
  34. package/dist/esnext/foundation/useQuery/index.js +0 -1
  35. package/dist/esnext/foundation/useShop/use-shop.d.ts +1 -1
  36. package/dist/esnext/foundation/useShop/use-shop.js +1 -1
  37. package/dist/esnext/framework/Hydration/ServerComponentRequest.server.d.ts +1 -0
  38. package/dist/esnext/framework/Hydration/ServerComponentRequest.server.js +2 -0
  39. package/dist/esnext/framework/cache.d.ts +2 -2
  40. package/dist/esnext/framework/cache.js +1 -1
  41. package/dist/esnext/framework/plugins/vite-plugin-hydrogen-config.d.ts +1 -1
  42. package/dist/esnext/framework/plugins/vite-plugin-hydrogen-config.js +13 -1
  43. package/dist/esnext/framework/plugins/vite-plugin-hydrogen-middleware.d.ts +7 -1
  44. package/dist/esnext/framework/plugins/vite-plugin-hydrogen-middleware.js +15 -1
  45. package/dist/esnext/framework/plugins/vite-plugin-react-server-components-shim.js +1 -13
  46. package/dist/esnext/handle-event.d.ts +1 -1
  47. package/dist/esnext/handle-event.js +68 -10
  48. package/dist/esnext/hooks/index.d.ts +1 -1
  49. package/dist/esnext/hooks/index.js +1 -1
  50. package/dist/esnext/hooks/useShopQuery/hooks.d.ts +4 -4
  51. package/dist/esnext/hooks/useShopQuery/hooks.js +47 -20
  52. package/dist/esnext/index.d.ts +2 -1
  53. package/dist/esnext/index.js +2 -1
  54. package/dist/esnext/types.d.ts +2 -1
  55. package/dist/esnext/utilities/fetch.js +3 -6
  56. package/dist/esnext/utilities/index.d.ts +1 -0
  57. package/dist/esnext/utilities/index.js +1 -0
  58. package/dist/esnext/utilities/log/index.d.ts +1 -0
  59. package/dist/esnext/utilities/log/index.js +1 -0
  60. package/dist/esnext/utilities/log/log.d.ts +17 -0
  61. package/dist/esnext/utilities/log/log.js +75 -0
  62. package/dist/esnext/utilities/suspense.d.ts +2 -2
  63. package/dist/esnext/utilities/suspense.js +0 -6
  64. package/dist/esnext/utilities/timing.d.ts +7 -0
  65. package/dist/esnext/utilities/timing.js +14 -0
  66. package/dist/esnext/version.d.ts +1 -1
  67. package/dist/esnext/version.js +1 -1
  68. package/dist/node/framework/Hydration/ServerComponentRequest.server.d.ts +1 -0
  69. package/dist/node/framework/Hydration/ServerComponentRequest.server.js +2 -0
  70. package/dist/node/framework/cache.d.ts +2 -2
  71. package/dist/node/framework/cache.js +2 -1
  72. package/dist/node/framework/plugins/vite-plugin-hydrogen-config.d.ts +1 -1
  73. package/dist/node/framework/plugins/vite-plugin-hydrogen-config.js +13 -1
  74. package/dist/node/framework/plugins/vite-plugin-hydrogen-middleware.d.ts +7 -1
  75. package/dist/node/framework/plugins/vite-plugin-hydrogen-middleware.js +15 -1
  76. package/dist/node/framework/plugins/vite-plugin-react-server-components-shim.js +1 -13
  77. package/dist/node/handle-event.d.ts +1 -1
  78. package/dist/node/handle-event.js +68 -10
  79. package/dist/node/types.d.ts +2 -1
  80. package/dist/node/utilities/fetch.js +3 -6
  81. package/dist/node/utilities/index.d.ts +1 -0
  82. package/dist/node/utilities/index.js +3 -1
  83. package/dist/node/utilities/suspense.d.ts +2 -2
  84. package/dist/node/utilities/suspense.js +0 -6
  85. package/dist/node/utilities/timing.d.ts +7 -0
  86. package/dist/node/utilities/timing.js +18 -0
  87. package/dist/node/version.d.ts +1 -1
  88. package/dist/node/version.js +1 -1
  89. package/dist/worker/framework/Hydration/ServerComponentRequest.server.d.ts +1 -0
  90. package/dist/worker/framework/Hydration/ServerComponentRequest.server.js +2 -0
  91. package/dist/worker/framework/cache.d.ts +2 -2
  92. package/dist/worker/framework/cache.js +1 -1
  93. package/dist/worker/handle-event.d.ts +1 -1
  94. package/dist/worker/handle-event.js +68 -10
  95. package/dist/worker/types.d.ts +2 -1
  96. package/dist/worker/utilities/timing.d.ts +7 -0
  97. package/dist/worker/utilities/timing.js +14 -0
  98. package/package.json +10 -9
  99. package/dist/esnext/foundation/useQuery/QueryProvider.d.ts +0 -6
  100. package/dist/esnext/foundation/useQuery/QueryProvider.js +0 -13
@@ -0,0 +1,75 @@
1
+ import { yellow, red, green, italic, lightBlue } from 'kolorist';
2
+ import { getTime } from '../timing';
3
+ const defaultLogger = {
4
+ trace(context, ...args) {
5
+ console.log(...args);
6
+ },
7
+ debug(context, ...args) {
8
+ console.log(...args);
9
+ },
10
+ warn(context, ...args) {
11
+ console.warn(yellow('WARN: '), ...args);
12
+ },
13
+ error(context, ...args) {
14
+ console.error(red('ERROR: '), ...args);
15
+ },
16
+ fatal(context, ...args) {
17
+ console.error(red('FATAL: '), ...args);
18
+ },
19
+ };
20
+ let logger = defaultLogger;
21
+ export function getLoggerFromContext(context) {
22
+ return {
23
+ trace: (...args) => logger.trace(context, ...args),
24
+ debug: (...args) => logger.debug(context, ...args),
25
+ warn: (...args) => logger.warn(context, ...args),
26
+ error: (...args) => logger.error(context, ...args),
27
+ fatal: (...args) => logger.fatal(context, ...args),
28
+ };
29
+ }
30
+ export function setLogger(newLogger) {
31
+ logger = newLogger;
32
+ }
33
+ export function resetLogger() {
34
+ logger = defaultLogger;
35
+ }
36
+ export const log = {
37
+ trace(...args) {
38
+ return logger.trace({}, ...args);
39
+ },
40
+ debug(...args) {
41
+ return logger.debug({}, ...args);
42
+ },
43
+ warn(...args) {
44
+ return logger.warn({}, ...args);
45
+ },
46
+ error(...args) {
47
+ return logger.error({}, ...args);
48
+ },
49
+ fatal(...args) {
50
+ return logger.fatal({}, ...args);
51
+ },
52
+ };
53
+ export function logServerResponse(type, log, request, responseStatus) {
54
+ const coloredResponseStatus = responseStatus >= 500
55
+ ? red(responseStatus)
56
+ : responseStatus >= 400
57
+ ? yellow(responseStatus)
58
+ : responseStatus >= 300
59
+ ? lightBlue(responseStatus)
60
+ : green(responseStatus);
61
+ const fullType = type === 'str'
62
+ ? 'streaming SSR'
63
+ : type === 'rsc'
64
+ ? 'server components'
65
+ : 'buffered SSR';
66
+ const styledType = italic(pad(fullType, ' '));
67
+ const paddedTiming = pad((getTime() - request.time).toFixed(2) + ' ms', ' ');
68
+ const url = type === 'rsc'
69
+ ? decodeURIComponent(request.url.substring(request.url.indexOf('=') + 1))
70
+ : request.url;
71
+ log.debug(`${request.method} ${styledType} ${coloredResponseStatus} ${paddedTiming} ${url}`);
72
+ }
73
+ function pad(str, _pad) {
74
+ return (str + _pad).substring(0, _pad.length);
75
+ }
@@ -2,6 +2,6 @@
2
2
  * Wrap the fetch promise in a way that React Suspense understands.
3
3
  * Essentially, keep throwing something until you have legit data.
4
4
  */
5
- export declare function wrapPromise(promise: Promise<any>): {
6
- read: () => any;
5
+ export declare function wrapPromise<T>(promise: Promise<T>): {
6
+ read: () => T;
7
7
  };
@@ -11,14 +11,8 @@ export function wrapPromise(promise) {
11
11
  }, (err) => {
12
12
  status = 'error';
13
13
  response = err;
14
- throw err;
15
14
  });
16
15
  const read = () => {
17
- /**
18
- * TODO: This logic doesn't hold up when an error is thrown. For some reason.
19
- * We instead throw the exception above in the suspender. We should revisit
20
- * this and add a better server fetch implementation.
21
- */
22
16
  switch (status) {
23
17
  case 'pending':
24
18
  throw suspender;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Not all environments have access to Performance.now(). This is to prevent
3
+ * timing side channel attacks.
4
+ *
5
+ * See: https://community.cloudflare.com/t/cloudflare-workers-how-do-i-measure-execution-time-of-my-method/69672
6
+ */
7
+ export declare function getTime(): number;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Not all environments have access to Performance.now(). This is to prevent
3
+ * timing side channel attacks.
4
+ *
5
+ * See: https://community.cloudflare.com/t/cloudflare-workers-how-do-i-measure-execution-time-of-my-method/69672
6
+ */
7
+ export function getTime() {
8
+ if (typeof performance !== 'undefined' && performance.now) {
9
+ return performance.now();
10
+ }
11
+ else {
12
+ return Date.now();
13
+ }
14
+ }
@@ -1 +1 @@
1
- export declare const LIB_VERSION = "0.7.1";
1
+ export declare const LIB_VERSION = "0.8.3";
@@ -1 +1 @@
1
- export const LIB_VERSION = '0.7.1';
1
+ export const LIB_VERSION = '0.8.3';
@@ -7,6 +7,7 @@
7
7
  */
8
8
  export declare class ServerComponentRequest extends Request {
9
9
  cookies: Map<string, string>;
10
+ time: number;
10
11
  constructor(input: any);
11
12
  constructor(input: RequestInfo, init?: RequestInit);
12
13
  private parseCookies;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ServerComponentRequest = void 0;
4
+ const timing_1 = require("../../utilities/timing");
4
5
  /**
5
6
  * This augments the `Request` object from the Fetch API:
6
7
  * @see https://developer.mozilla.org/en-US/docs/Web/API/Request
@@ -19,6 +20,7 @@ class ServerComponentRequest extends Request {
19
20
  method: input.method,
20
21
  });
21
22
  }
23
+ this.time = (0, timing_1.getTime)();
22
24
  this.cookies = this.parseCookies();
23
25
  }
24
26
  parseCookies() {
@@ -1,5 +1,4 @@
1
- import type { QueryKey } from 'react-query';
2
- import type { CacheOptions } from '../types';
1
+ import type { CacheOptions, QueryKey } from '../types';
3
2
  export declare function generateCacheControlHeader(options: CacheOptions): string;
4
3
  /**
5
4
  * Use a preview header during development.
@@ -9,6 +8,7 @@ export declare function generateCacheControlHeader(options: CacheOptions): strin
9
8
  export declare function getCacheControlHeader({ dev }: {
10
9
  dev?: boolean;
11
10
  }): "cache-control-preview" | "cache-control";
11
+ export declare function hashKey(key: QueryKey): string;
12
12
  /**
13
13
  * Get an item from the cache. If a match is found, returns a tuple
14
14
  * containing the `JSON.parse` version of the response as well
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isStale = exports.deleteItemFromCache = exports.setItemInCache = exports.getItemFromCache = exports.getCacheControlHeader = exports.generateCacheControlHeader = void 0;
3
+ exports.isStale = exports.deleteItemFromCache = exports.setItemInCache = exports.getItemFromCache = exports.hashKey = exports.getCacheControlHeader = exports.generateCacheControlHeader = void 0;
4
4
  const runtime_1 = require("./runtime");
5
5
  const DEFAULT_SUBREQUEST_CACHE_OPTIONS = {
6
6
  maxAge: 1,
@@ -33,6 +33,7 @@ function hashKey(key) {
33
33
  */
34
34
  return rawKey.map((k) => JSON.stringify(k)).join('');
35
35
  }
36
+ exports.hashKey = hashKey;
36
37
  /**
37
38
  * Cache API is weird. We just need a full URL, so we make one up.
38
39
  */
@@ -1,3 +1,3 @@
1
- import type { Plugin } from 'vite';
1
+ import { Plugin } from 'vite';
2
2
  declare const _default: () => Plugin;
3
3
  export default _default;
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.default = () => {
4
4
  return {
5
5
  name: 'vite-plugin-hydrogen-config',
6
- config: (_, env) => ({
6
+ config: async (config, env) => ({
7
7
  resolve: {
8
8
  alias: {
9
9
  /**
@@ -18,6 +18,17 @@ exports.default = () => {
18
18
  },
19
19
  build: {
20
20
  sourcemap: true,
21
+ /**
22
+ * By default, SSR dedupe logic gets bundled which runs `require('module')`.
23
+ * We don't want this in our workers runtime, because `require` is not supported.
24
+ */
25
+ rollupOptions: process.env.WORKER
26
+ ? {
27
+ output: {
28
+ format: 'es',
29
+ },
30
+ }
31
+ : {},
21
32
  },
22
33
  ssr: {
23
34
  external: ['isomorphic-dompurify'],
@@ -57,6 +68,7 @@ exports.default = () => {
57
68
  define: {
58
69
  __DEV__: env.mode !== 'production',
59
70
  },
71
+ envPrefix: ['VITE_', 'PUBLIC_'],
60
72
  }),
61
73
  };
62
74
  };
@@ -1,4 +1,10 @@
1
- import type { Plugin } from 'vite';
1
+ import { Plugin } from 'vite';
2
2
  import type { HydrogenVitePluginOptions, ShopifyConfig } from '../../types';
3
3
  declare const _default: (shopifyConfig: ShopifyConfig, pluginOptions: HydrogenVitePluginOptions) => Plugin;
4
4
  export default _default;
5
+ declare global {
6
+ var Oxygen: {
7
+ env: Record<string, string | undefined>;
8
+ [key: string]: any;
9
+ };
10
+ }
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ const vite_1 = require("vite");
6
7
  const path_1 = __importDefault(require("path"));
7
8
  const fs_1 = require("fs");
8
9
  const middleware_1 = require("../middleware");
@@ -16,12 +17,13 @@ exports.default = (shopifyConfig, pluginOptions) => {
16
17
  * loading them in an SSR context, rendering them using the `entry-server` endpoint in the
17
18
  * user's project, and injecting the static HTML into the template.
18
19
  */
19
- configureServer(server) {
20
+ async configureServer(server) {
20
21
  const resolve = (p) => path_1.default.resolve(server.config.root, p);
21
22
  async function getIndexTemplate(url) {
22
23
  const indexHtml = await fs_1.promises.readFile(resolve('index.html'), 'utf-8');
23
24
  return await server.transformIndexHtml(url, indexHtml);
24
25
  }
26
+ await polyfillOxygenEnv(server.config);
25
27
  // The default vite middleware rewrites the URL `/graphqil` to `/index.html`
26
28
  // By running this middleware first, we avoid that.
27
29
  server.middlewares.use((0, middleware_1.graphiqlMiddleware)({
@@ -41,3 +43,15 @@ exports.default = (shopifyConfig, pluginOptions) => {
41
43
  },
42
44
  };
43
45
  };
46
+ async function polyfillOxygenEnv(config) {
47
+ const env = await (0, vite_1.loadEnv)(config.mode, config.root, '');
48
+ const publicPrefixes = Array.isArray(config.envPrefix)
49
+ ? config.envPrefix
50
+ : [config.envPrefix || ''];
51
+ for (const key of Object.keys(env)) {
52
+ if (publicPrefixes.some((prefix) => key.startsWith(prefix))) {
53
+ delete env[key];
54
+ }
55
+ }
56
+ globalThis.Oxygen = { env };
57
+ }
@@ -77,19 +77,7 @@ exports.default = () => {
77
77
  }
78
78
  },
79
79
  };
80
- // Mitigation for upcoming minor Vite update
81
- // https://github.com/vitejs/vite/pull/5253
82
- // TO-DO: When the vite package is updated with the above Vite PR,
83
- // clean up this function and treat `options` param as objects
84
- // from this point forward
85
- // Timeline: Targetting for Vite 2.7
86
80
  function isSSR(options) {
87
- if (typeof options === 'boolean') {
88
- return options;
89
- }
90
- if (typeof options === 'object') {
91
- return !!options.ssr;
92
- }
93
- return false;
81
+ return !!(options === null || options === void 0 ? void 0 : options.ssr);
94
82
  }
95
83
  };
@@ -1,5 +1,5 @@
1
1
  /// <reference types="node" />
2
- import type { ServerResponse } from 'http';
2
+ import { ServerResponse } from 'http';
3
3
  import type { ServerComponentRequest } from './framework/Hydration/ServerComponentRequest.server';
4
4
  import { RuntimeContext } from './framework/runtime';
5
5
  interface HydrogenFetchEvent {
@@ -29,7 +29,8 @@ async function handleEvent(event, { request, entrypoint, indexTemplate, assetHan
29
29
  throw new Error(`entry-server.jsx could not be loaded. This likely occurred because of a Vite compilation error.\n` +
30
30
  `Please check your server logs for more information.`);
31
31
  }
32
- const isStreamable = streamableResponse && isStreamableRequest(url);
32
+ const userAgent = request.headers.get('user-agent');
33
+ const isStreamable = streamableResponse && !isBotUA(url, userAgent);
33
34
  /**
34
35
  * Stream back real-user responses, but for bots/etc,
35
36
  * use `render` instead. This is because we need to inject <head>
@@ -37,7 +38,12 @@ async function handleEvent(event, { request, entrypoint, indexTemplate, assetHan
37
38
  */
38
39
  if (isStreamable) {
39
40
  if (isReactHydrationRequest) {
40
- hydrate(url, { context: {}, request, response: streamableResponse, dev });
41
+ hydrate(url, {
42
+ context: {},
43
+ request,
44
+ response: streamableResponse,
45
+ dev,
46
+ });
41
47
  }
42
48
  else {
43
49
  stream(url, {
@@ -50,7 +56,12 @@ async function handleEvent(event, { request, entrypoint, indexTemplate, assetHan
50
56
  }
51
57
  return;
52
58
  }
53
- const { body, bodyAttributes, htmlAttributes, componentResponse, ...head } = await render(url, { request, context: {}, isReactHydrationRequest, dev });
59
+ const { body, bodyAttributes, htmlAttributes, componentResponse, ...head } = await render(url, {
60
+ request,
61
+ context: {},
62
+ isReactHydrationRequest,
63
+ dev,
64
+ });
54
65
  const headers = componentResponse.headers;
55
66
  /**
56
67
  * TODO: Also add `Vary` headers for `accept-language` and any other keys
@@ -89,13 +100,6 @@ async function handleEvent(event, { request, entrypoint, indexTemplate, assetHan
89
100
  return response;
90
101
  }
91
102
  exports.default = handleEvent;
92
- function isStreamableRequest(url) {
93
- /**
94
- * TODO: Add UA detection.
95
- */
96
- const isBot = url.searchParams.has('_bot');
97
- return !isBot;
98
- }
99
103
  /**
100
104
  * Generate the contents of the `head` tag, and update the existing `<title>` tag
101
105
  * if one exists, and if a title is passed.
@@ -120,3 +124,57 @@ function generateHeadTag(head) {
120
124
  return `<head>${headHtml}</head>`;
121
125
  };
122
126
  }
127
+ /**
128
+ * Determines if the request is from a bot, using the URL and User Agent
129
+ */
130
+ function isBotUA(url, userAgent) {
131
+ return (url.searchParams.has('_bot') || (!!userAgent && botUARegex.test(userAgent)));
132
+ }
133
+ /**
134
+ * An alphabetized list of User Agents of known bots, combined from lists found at:
135
+ * https://github.com/vercel/next.js/blob/d87dc2b5a0b3fdbc0f6806a47be72bad59564bd0/packages/next/server/utils.ts#L18-L22
136
+ * https://github.com/GoogleChrome/rendertron/blob/6f681688737846b28754fbfdf5db173846a826df/middleware/src/middleware.ts#L24-L41
137
+ */
138
+ const botUserAgents = [
139
+ 'AdsBot-Google',
140
+ 'applebot',
141
+ 'Baiduspider',
142
+ 'baiduspider',
143
+ 'bingbot',
144
+ 'Bingbot',
145
+ 'BingPreview',
146
+ 'bitlybot',
147
+ 'Discordbot',
148
+ 'DuckDuckBot',
149
+ 'Embedly',
150
+ 'facebookcatalog',
151
+ 'facebookexternalhit',
152
+ 'Google-PageRenderer',
153
+ 'Googlebot',
154
+ 'googleweblight',
155
+ 'ia_archive',
156
+ 'LinkedInBot',
157
+ 'Mediapartners-Google',
158
+ 'outbrain',
159
+ 'pinterest',
160
+ 'quora link preview',
161
+ 'redditbot',
162
+ 'rogerbot',
163
+ 'showyoubot',
164
+ 'SkypeUriPreview',
165
+ 'Slackbot',
166
+ 'Slurp',
167
+ 'sogou',
168
+ 'Storebot-Google',
169
+ 'TelegramBot',
170
+ 'tumblr',
171
+ 'Twitterbot',
172
+ 'vkShare',
173
+ 'W3C_Validator',
174
+ 'WhatsApp',
175
+ 'yandex',
176
+ ];
177
+ /**
178
+ * Creates a regex based on the botUserAgents array
179
+ */
180
+ const botUARegex = new RegExp(botUserAgents.join('|'), 'i');
@@ -1,5 +1,5 @@
1
1
  /// <reference types="node" />
2
- import type { ServerResponse } from 'http';
2
+ import { ServerResponse } from 'http';
3
3
  import type { ServerComponentResponse } from './framework/Hydration/ServerComponentResponse.server';
4
4
  import type { ServerComponentRequest } from './framework/Hydration/ServerComponentRequest.server';
5
5
  import type { Metafield, Image, MediaContentType } from './graphql/types/types';
@@ -78,6 +78,7 @@ export interface Measurement {
78
78
  unit: string;
79
79
  value: number;
80
80
  }
81
+ export declare type QueryKey = string | readonly unknown[];
81
82
  export interface CacheOptions {
82
83
  private?: boolean;
83
84
  maxAge?: number;
@@ -41,6 +41,9 @@ function fetchBuilder(request) {
41
41
  headers,
42
42
  method: clonedRequest.method,
43
43
  });
44
+ if (!response.ok) {
45
+ throw response;
46
+ }
44
47
  const data = await response.json();
45
48
  return data;
46
49
  };
@@ -55,12 +58,6 @@ function graphqlRequestBody(query, variables) {
55
58
  }
56
59
  exports.graphqlRequestBody = graphqlRequestBody;
57
60
  function decodeShopifyId(id) {
58
- if (!id.startsWith('gid://')) {
59
- id =
60
- typeof btoa !== 'undefined'
61
- ? btoa(id)
62
- : Buffer.from(id, 'base64').toString('ascii');
63
- }
64
61
  if (!id.startsWith('gid://')) {
65
62
  throw new Error('invalid Shopify ID');
66
63
  }
@@ -8,3 +8,4 @@ export { isServer } from './isServer';
8
8
  export { getMeasurementAsParts, getMeasurementAsString } from './measurement';
9
9
  export { parseMetafieldValue } from './parseMetafieldValue';
10
10
  export { fetchBuilder, graphqlRequestBody, decodeShopifyId } from './fetch';
11
+ export { getTime } from './timing';
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.decodeShopifyId = exports.graphqlRequestBody = exports.fetchBuilder = exports.parseMetafieldValue = exports.getMeasurementAsString = exports.getMeasurementAsParts = exports.isServer = exports.isClient = exports.flattenConnection = exports.wrapPromise = exports.loadScript = exports.useEmbeddedVideoUrl = exports.addParametersToEmbeddedVideoUrl = exports.shopifyImageLoader = exports.getShopifyImageDimensions = exports.useImageUrl = exports.addImageSizeParametersToUrl = void 0;
3
+ exports.getTime = exports.decodeShopifyId = exports.graphqlRequestBody = exports.fetchBuilder = exports.parseMetafieldValue = exports.getMeasurementAsString = exports.getMeasurementAsParts = exports.isServer = exports.isClient = exports.flattenConnection = exports.wrapPromise = exports.loadScript = exports.useEmbeddedVideoUrl = exports.addParametersToEmbeddedVideoUrl = exports.shopifyImageLoader = exports.getShopifyImageDimensions = exports.useImageUrl = exports.addImageSizeParametersToUrl = void 0;
4
4
  var image_size_1 = require("./image_size");
5
5
  Object.defineProperty(exports, "addImageSizeParametersToUrl", { enumerable: true, get: function () { return image_size_1.addImageSizeParametersToUrl; } });
6
6
  Object.defineProperty(exports, "useImageUrl", { enumerable: true, get: function () { return image_size_1.useImageUrl; } });
@@ -28,3 +28,5 @@ var fetch_1 = require("./fetch");
28
28
  Object.defineProperty(exports, "fetchBuilder", { enumerable: true, get: function () { return fetch_1.fetchBuilder; } });
29
29
  Object.defineProperty(exports, "graphqlRequestBody", { enumerable: true, get: function () { return fetch_1.graphqlRequestBody; } });
30
30
  Object.defineProperty(exports, "decodeShopifyId", { enumerable: true, get: function () { return fetch_1.decodeShopifyId; } });
31
+ var timing_1 = require("./timing");
32
+ Object.defineProperty(exports, "getTime", { enumerable: true, get: function () { return timing_1.getTime; } });
@@ -2,6 +2,6 @@
2
2
  * Wrap the fetch promise in a way that React Suspense understands.
3
3
  * Essentially, keep throwing something until you have legit data.
4
4
  */
5
- export declare function wrapPromise(promise: Promise<any>): {
6
- read: () => any;
5
+ export declare function wrapPromise<T>(promise: Promise<T>): {
6
+ read: () => T;
7
7
  };
@@ -14,14 +14,8 @@ function wrapPromise(promise) {
14
14
  }, (err) => {
15
15
  status = 'error';
16
16
  response = err;
17
- throw err;
18
17
  });
19
18
  const read = () => {
20
- /**
21
- * TODO: This logic doesn't hold up when an error is thrown. For some reason.
22
- * We instead throw the exception above in the suspender. We should revisit
23
- * this and add a better server fetch implementation.
24
- */
25
19
  switch (status) {
26
20
  case 'pending':
27
21
  throw suspender;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Not all environments have access to Performance.now(). This is to prevent
3
+ * timing side channel attacks.
4
+ *
5
+ * See: https://community.cloudflare.com/t/cloudflare-workers-how-do-i-measure-execution-time-of-my-method/69672
6
+ */
7
+ export declare function getTime(): number;
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getTime = void 0;
4
+ /**
5
+ * Not all environments have access to Performance.now(). This is to prevent
6
+ * timing side channel attacks.
7
+ *
8
+ * See: https://community.cloudflare.com/t/cloudflare-workers-how-do-i-measure-execution-time-of-my-method/69672
9
+ */
10
+ function getTime() {
11
+ if (typeof performance !== 'undefined' && performance.now) {
12
+ return performance.now();
13
+ }
14
+ else {
15
+ return Date.now();
16
+ }
17
+ }
18
+ exports.getTime = getTime;
@@ -1 +1 @@
1
- export declare const LIB_VERSION = "0.7.1";
1
+ export declare const LIB_VERSION = "0.8.3";
@@ -1,4 +1,4 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.LIB_VERSION = void 0;
4
- exports.LIB_VERSION = '0.7.1';
4
+ exports.LIB_VERSION = '0.8.3';
@@ -7,6 +7,7 @@
7
7
  */
8
8
  export declare class ServerComponentRequest extends Request {
9
9
  cookies: Map<string, string>;
10
+ time: number;
10
11
  constructor(input: any);
11
12
  constructor(input: RequestInfo, init?: RequestInit);
12
13
  private parseCookies;
@@ -1,3 +1,4 @@
1
+ import { getTime } from '../../utilities/timing';
1
2
  /**
2
3
  * This augments the `Request` object from the Fetch API:
3
4
  * @see https://developer.mozilla.org/en-US/docs/Web/API/Request
@@ -16,6 +17,7 @@ export class ServerComponentRequest extends Request {
16
17
  method: input.method,
17
18
  });
18
19
  }
20
+ this.time = getTime();
19
21
  this.cookies = this.parseCookies();
20
22
  }
21
23
  parseCookies() {
@@ -1,5 +1,4 @@
1
- import type { QueryKey } from 'react-query';
2
- import type { CacheOptions } from '../types';
1
+ import type { CacheOptions, QueryKey } from '../types';
3
2
  export declare function generateCacheControlHeader(options: CacheOptions): string;
4
3
  /**
5
4
  * Use a preview header during development.
@@ -9,6 +8,7 @@ export declare function generateCacheControlHeader(options: CacheOptions): strin
9
8
  export declare function getCacheControlHeader({ dev }: {
10
9
  dev?: boolean;
11
10
  }): "cache-control-preview" | "cache-control";
11
+ export declare function hashKey(key: QueryKey): string;
12
12
  /**
13
13
  * Get an item from the cache. If a match is found, returns a tuple
14
14
  * containing the `JSON.parse` version of the response as well
@@ -21,7 +21,7 @@ export function generateCacheControlHeader(options) {
21
21
  export function getCacheControlHeader({ dev }) {
22
22
  return dev ? 'cache-control-preview' : 'cache-control';
23
23
  }
24
- function hashKey(key) {
24
+ export function hashKey(key) {
25
25
  const rawKey = key instanceof Array ? key : [key];
26
26
  /**
27
27
  * TODO: Smarter hash
@@ -1,5 +1,5 @@
1
1
  /// <reference types="@types/node" />
2
- import type { ServerResponse } from 'http';
2
+ import { ServerResponse } from 'http';
3
3
  import type { ServerComponentRequest } from './framework/Hydration/ServerComponentRequest.server';
4
4
  import { RuntimeContext } from './framework/runtime';
5
5
  interface HydrogenFetchEvent {