@shopify/remix-oxygen 2.0.11 → 2.1.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.
- package/dist/development/index.cjs +47 -5
- package/dist/development/index.cjs.map +1 -1
- package/dist/development/index.js +47 -5
- package/dist/development/index.js.map +1 -1
- package/dist/production/index.cjs +2 -49
- package/dist/production/index.cjs.map +1 -1
- package/dist/production/index.d.cts +30 -3
- package/dist/production/index.d.ts +30 -3
- package/dist/production/index.js +2 -7
- package/dist/production/index.js.map +1 -1
- package/package.json +4 -4
|
@@ -87,13 +87,33 @@ var originalErrorToString = Error.prototype.toString;
|
|
|
87
87
|
Error.prototype.toString = function() {
|
|
88
88
|
return this.stack || originalErrorToString.call(this);
|
|
89
89
|
};
|
|
90
|
+
var HYDROGEN_SFAPI_PROXY_KEY = "_sfapi_proxy";
|
|
91
|
+
var hasWarnedAboutStorefront = false;
|
|
92
|
+
function warnOnce(message) {
|
|
93
|
+
if (!hasWarnedAboutStorefront) {
|
|
94
|
+
hasWarnedAboutStorefront = true;
|
|
95
|
+
console.warn(message);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
function buildServerTimingHeader(values) {
|
|
99
|
+
return Object.entries(values).map(([key, value]) => value ? `${key};desc=${value}` : void 0).filter(Boolean).join(", ");
|
|
100
|
+
}
|
|
101
|
+
function appendServerTimingHeader(response, values) {
|
|
102
|
+
const header = typeof values === "string" ? values : buildServerTimingHeader(values);
|
|
103
|
+
if (header) {
|
|
104
|
+
response.headers.append("Server-Timing", header);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
90
107
|
function createRequestHandler({
|
|
91
108
|
build,
|
|
92
109
|
mode,
|
|
93
110
|
poweredByHeader = true,
|
|
94
|
-
getLoadContext
|
|
111
|
+
getLoadContext,
|
|
112
|
+
collectTrackingInformation = true,
|
|
113
|
+
proxyStandardRoutes = true
|
|
95
114
|
}) {
|
|
96
115
|
const handleRequest = serverRuntime.createRequestHandler(build, mode);
|
|
116
|
+
const appendPoweredByHeader = poweredByHeader ? (response) => response.headers.append("powered-by", "Shopify, Hydrogen") : void 0;
|
|
97
117
|
return async (request) => {
|
|
98
118
|
const method = request.method;
|
|
99
119
|
if ((method === "GET" || method === "HEAD") && request.body) {
|
|
@@ -111,14 +131,34 @@ function createRequestHandler({
|
|
|
111
131
|
});
|
|
112
132
|
}
|
|
113
133
|
const context = getLoadContext ? await getLoadContext(request) : void 0;
|
|
134
|
+
const storefront = context?.storefront;
|
|
135
|
+
if (proxyStandardRoutes) {
|
|
136
|
+
if (!storefront) {
|
|
137
|
+
warnOnce(
|
|
138
|
+
"[h2:createRequestHandler] Storefront instance is required to proxy standard routes."
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
if (storefront?.isStorefrontApiUrl(request)) {
|
|
142
|
+
const response2 = await storefront.forward(request);
|
|
143
|
+
appendPoweredByHeader?.(response2);
|
|
144
|
+
return response2;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
114
147
|
if (context) {
|
|
115
148
|
globalThis.__H2O_LOG_EVENT ??= createEventLogger(context);
|
|
116
149
|
}
|
|
117
150
|
const startTime = Date.now();
|
|
118
151
|
const response = await handleRequest(request, context);
|
|
119
|
-
if (
|
|
120
|
-
|
|
152
|
+
if (storefront && proxyStandardRoutes) {
|
|
153
|
+
if (collectTrackingInformation) {
|
|
154
|
+
storefront.setCollectedSubrequestHeaders(response);
|
|
155
|
+
}
|
|
156
|
+
const fetchDest = request.headers.get("sec-fetch-dest");
|
|
157
|
+
if (fetchDest && fetchDest === "document" || request.headers.get("accept")?.includes("text/html")) {
|
|
158
|
+
appendServerTimingHeader(response, { [HYDROGEN_SFAPI_PROXY_KEY]: "1" });
|
|
159
|
+
}
|
|
121
160
|
}
|
|
161
|
+
appendPoweredByHeader?.(response);
|
|
122
162
|
{
|
|
123
163
|
globalThis.__H2O_LOG_EVENT?.({
|
|
124
164
|
eventType: "request",
|
|
@@ -141,8 +181,10 @@ function getStorefrontHeaders(request) {
|
|
|
141
181
|
return {
|
|
142
182
|
requestGroupId: headers.get("request-id"),
|
|
143
183
|
buyerIp: headers.get("oxygen-buyer-ip"),
|
|
184
|
+
buyerIpSig: headers.get("X-Shopify-Client-IP-Sig"),
|
|
144
185
|
cookie: headers.get("cookie"),
|
|
145
|
-
purpose
|
|
186
|
+
// sec-purpose is added by browsers automatically when using link/prefetch or Speculation Rules
|
|
187
|
+
purpose: headers.get("sec-purpose") || headers.get("purpose")
|
|
146
188
|
};
|
|
147
189
|
}
|
|
148
190
|
|
|
@@ -188,5 +230,5 @@ exports.createMemorySessionStorage = createMemorySessionStorage;
|
|
|
188
230
|
exports.createRequestHandler = createRequestHandler;
|
|
189
231
|
exports.createSessionStorage = createSessionStorage;
|
|
190
232
|
exports.getStorefrontHeaders = getStorefrontHeaders;
|
|
191
|
-
//# sourceMappingURL=
|
|
233
|
+
//# sourceMappingURL=index.cjs.map
|
|
192
234
|
//# sourceMappingURL=index.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/implementations.ts","../../src/crypto.ts","../../src/server.ts","../../src/event-logger.ts","../../src/index.ts"],"names":["data"],"mappings":"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","sourcesContent":["import {\n createCookieFactory,\n createCookieSessionStorageFactory,\n createMemorySessionStorageFactory,\n createSessionStorageFactory,\n} from '@remix-run/server-runtime';\n\nimport {sign, unsign} from './crypto';\n\nexport const createCookie = createCookieFactory({sign, unsign});\nexport const createCookieSessionStorage =\n createCookieSessionStorageFactory(createCookie);\nexport const createSessionStorage = createSessionStorageFactory(createCookie);\nexport const createMemorySessionStorage =\n createMemorySessionStorageFactory(createSessionStorage);\n","import type {SignFunction, UnsignFunction} from '@remix-run/server-runtime';\n\nconst encoder = new TextEncoder();\n\nexport const sign: SignFunction = async (value, secret) => {\n const key = await createKey(secret, ['sign']);\n const data = encoder.encode(value);\n const signature = await crypto.subtle.sign('HMAC', key, data);\n const hash = btoa(String.fromCharCode(...new Uint8Array(signature))).replace(\n /=+$/,\n '',\n );\n\n return value + '.' + hash;\n};\n\nexport const unsign: UnsignFunction = async (signed, secret) => {\n const index = signed.lastIndexOf('.');\n const value = signed.slice(0, index);\n const hash = signed.slice(index + 1);\n\n const key = await createKey(secret, ['verify']);\n const data = encoder.encode(value);\n const signature = byteStringToUint8Array(atob(hash));\n const valid = await crypto.subtle.verify('HMAC', key, signature, data);\n\n return valid ? value : false;\n};\n\nasync function createKey(\n secret: string,\n usages: CryptoKey['usages'],\n): Promise<CryptoKey> {\n const key = await crypto.subtle.importKey(\n 'raw',\n encoder.encode(secret),\n {name: 'HMAC', hash: 'SHA-256'},\n false,\n usages,\n );\n\n return key;\n}\n\nfunction byteStringToUint8Array(byteString: string): Uint8Array {\n const array = new Uint8Array(byteString.length);\n\n for (let i = 0; i < byteString.length; i++) {\n array[i] = byteString.charCodeAt(i);\n }\n\n return array;\n}\n","/// <reference types=\"@shopify/hydrogen\" />\nimport {\n createRequestHandler as createRemixRequestHandler,\n type AppLoadContext,\n type ServerBuild,\n} from '@remix-run/server-runtime';\nimport {createEventLogger} from './event-logger';\n\nconst originalErrorToString = Error.prototype.toString;\nError.prototype.toString = function () {\n return this.stack || originalErrorToString.call(this);\n};\n\nexport function createRequestHandler<Context = unknown>({\n build,\n mode,\n poweredByHeader = true,\n getLoadContext,\n}: {\n build: ServerBuild;\n mode?: string;\n poweredByHeader?: boolean;\n getLoadContext?: (request: Request) => Promise<Context> | Context;\n}) {\n const handleRequest = createRemixRequestHandler(build, mode);\n\n return async (request: Request) => {\n const method = request.method;\n\n if ((method === 'GET' || method === 'HEAD') && request.body) {\n return new Response(`${method} requests cannot have a body`, {\n status: 400,\n });\n }\n\n const url = new URL(request.url);\n\n if (url.pathname.includes('//')) {\n return new Response(null, {\n status: 301,\n headers: {\n location: url.pathname.replace(/\\/+/g, '/'),\n },\n });\n }\n\n const context = getLoadContext\n ? ((await getLoadContext(request)) as AppLoadContext)\n : undefined;\n\n if (process.env.NODE_ENV === 'development' && context) {\n // Store logger in globalThis so it can be accessed from the worker.\n // The global property must be different from the binding name,\n // otherwise Miniflare throws an error when accessing it.\n globalThis.__H2O_LOG_EVENT ??= createEventLogger(context);\n }\n\n const startTime = Date.now();\n\n const response = await handleRequest(request, context);\n\n if (poweredByHeader) {\n response.headers.append('powered-by', 'Shopify, Hydrogen');\n }\n\n if (process.env.NODE_ENV === 'development') {\n globalThis.__H2O_LOG_EVENT?.({\n eventType: 'request',\n url: request.url,\n requestId: request.headers.get('request-id'),\n purpose: request.headers.get('purpose'),\n startTime,\n responseInit: {\n status: response.status,\n statusText: response.statusText,\n headers: Array.from(response.headers.entries()),\n } satisfies ResponseInit,\n });\n }\n\n return response;\n };\n}\n\ntype StorefrontHeaders = {\n requestGroupId: string | null;\n buyerIp: string | null;\n cookie: string | null;\n purpose: string | null;\n};\n\nexport function getStorefrontHeaders(request: Request): StorefrontHeaders {\n const headers = request.headers;\n return {\n requestGroupId: headers.get('request-id'),\n buyerIp: headers.get('oxygen-buyer-ip'),\n cookie: headers.get('cookie'),\n purpose: headers.get('purpose'),\n };\n}\n","type H2OEvent = Parameters<NonNullable<typeof __H2O_LOG_EVENT>>[0];\n\nlet hasWarned = false;\n\n/**\n * @deprecated Only used with the classic Remix compiler\n */\nexport function createEventLogger(appLoadContext: Record<string, unknown>) {\n const context = (appLoadContext || {}) as {\n env?: Record<string, any>;\n waitUntil?: (promise: Promise<any>) => void;\n };\n\n const eventLoggerService = context?.env?.H2O_LOG_EVENT as\n | undefined\n | {fetch: (req: Request) => Promise<Response>};\n\n if (typeof eventLoggerService?.fetch !== 'function') return;\n\n return ({\n url,\n endTime = Date.now(),\n waitUntil = context?.waitUntil,\n ...rest\n }: H2OEvent) => {\n const promise = Promise.resolve().then(() =>\n eventLoggerService\n .fetch(\n new Request(url, {\n method: 'POST',\n body: JSON.stringify({\n endTime,\n ...rest,\n }),\n }),\n )\n .catch((error: Error) => {\n if (!hasWarned) {\n // This might repeat a lot of times due to\n // the same issue, so we only warn once.\n console.debug('Failed to log H2O event\\n', error.stack);\n hasWarned = true;\n }\n }),\n );\n\n promise && waitUntil?.(promise);\n };\n}\n","export {\n createCookie,\n createCookieSessionStorage,\n createMemorySessionStorage,\n createSessionStorage,\n} from './implementations';\nexport {createRequestHandler, getStorefrontHeaders} from './server';\nexport type {\n ActionFunction,\n ActionFunctionArgs,\n AppLoadContext,\n Cookie,\n CookieOptions,\n CookieParseOptions,\n CookieSerializeOptions,\n CookieSignatureOptions,\n DataFunctionArgs,\n EntryContext,\n ErrorResponse,\n HandleDataRequestFunction,\n HandleDocumentRequestFunction,\n HandleErrorFunction,\n HeadersArgs,\n HeadersFunction,\n HtmlLinkDescriptor,\n JsonFunction,\n LinkDescriptor,\n LinksFunction,\n LoaderFunction,\n LoaderFunctionArgs,\n MemoryUploadHandlerFilterArgs,\n MemoryUploadHandlerOptions,\n ServerRuntimeMetaArgs as MetaArgs,\n ServerRuntimeMetaDescriptor as MetaDescriptor,\n ServerRuntimeMetaFunction as MetaFunction,\n PageLinkDescriptor,\n RequestHandler,\n SerializeFrom,\n ServerBuild,\n ServerEntryModule,\n Session,\n SessionData,\n SessionIdStorageStrategy,\n SessionStorage,\n SignFunction,\n TypedDeferredData,\n TypedResponse,\n UnsignFunction,\n UploadHandler,\n UploadHandlerPart,\n} from '@remix-run/server-runtime';\nexport {\n createSession,\n data,\n defer,\n isCookie,\n isSession,\n json,\n MaxPartSizeExceededError,\n redirect,\n redirectDocument,\n} from '@remix-run/server-runtime';\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/crypto.ts","../../src/implementations.ts","../../src/event-logger.ts","../../src/server.ts"],"names":["data","createCookieFactory","createCookieSessionStorageFactory","createSessionStorageFactory","createMemorySessionStorageFactory","createRemixRequestHandler","response"],"mappings":"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","file":"index.cjs","sourcesContent":["import type {SignFunction, UnsignFunction} from '@remix-run/server-runtime';\n\nconst encoder = new TextEncoder();\n\nexport const sign: SignFunction = async (value, secret) => {\n const key = await createKey(secret, ['sign']);\n const data = encoder.encode(value);\n const signature = await crypto.subtle.sign('HMAC', key, data);\n const hash = btoa(String.fromCharCode(...new Uint8Array(signature))).replace(\n /=+$/,\n '',\n );\n\n return value + '.' + hash;\n};\n\nexport const unsign: UnsignFunction = async (signed, secret) => {\n const index = signed.lastIndexOf('.');\n const value = signed.slice(0, index);\n const hash = signed.slice(index + 1);\n\n const key = await createKey(secret, ['verify']);\n const data = encoder.encode(value);\n const signature = byteStringToUint8Array(atob(hash));\n const valid = await crypto.subtle.verify('HMAC', key, signature, data);\n\n return valid ? value : false;\n};\n\nasync function createKey(\n secret: string,\n usages: CryptoKey['usages'],\n): Promise<CryptoKey> {\n const key = await crypto.subtle.importKey(\n 'raw',\n encoder.encode(secret),\n {name: 'HMAC', hash: 'SHA-256'},\n false,\n usages,\n );\n\n return key;\n}\n\nfunction byteStringToUint8Array(byteString: string): Uint8Array {\n const array = new Uint8Array(byteString.length);\n\n for (let i = 0; i < byteString.length; i++) {\n array[i] = byteString.charCodeAt(i);\n }\n\n return array;\n}\n","import {\n createCookieFactory,\n createCookieSessionStorageFactory,\n createMemorySessionStorageFactory,\n createSessionStorageFactory,\n} from '@remix-run/server-runtime';\n\nimport {sign, unsign} from './crypto';\n\nexport const createCookie = createCookieFactory({sign, unsign});\nexport const createCookieSessionStorage =\n createCookieSessionStorageFactory(createCookie);\nexport const createSessionStorage = createSessionStorageFactory(createCookie);\nexport const createMemorySessionStorage =\n createMemorySessionStorageFactory(createSessionStorage);\n","type H2OEvent = Parameters<NonNullable<typeof __H2O_LOG_EVENT>>[0];\n\nlet hasWarned = false;\n\n/**\n * @deprecated Only used with the classic Remix compiler\n */\nexport function createEventLogger(appLoadContext: Record<string, unknown>) {\n const context = (appLoadContext || {}) as {\n env?: Record<string, any>;\n waitUntil?: (promise: Promise<any>) => void;\n };\n\n const eventLoggerService = context?.env?.H2O_LOG_EVENT as\n | undefined\n | {fetch: (req: Request) => Promise<Response>};\n\n if (typeof eventLoggerService?.fetch !== 'function') return;\n\n return ({\n url,\n endTime = Date.now(),\n waitUntil = context?.waitUntil,\n ...rest\n }: H2OEvent) => {\n const promise = Promise.resolve().then(() =>\n eventLoggerService\n .fetch(\n new Request(url, {\n method: 'POST',\n body: JSON.stringify({\n endTime,\n ...rest,\n }),\n }),\n )\n .catch((error: Error) => {\n if (!hasWarned) {\n // This might repeat a lot of times due to\n // the same issue, so we only warn once.\n console.debug('Failed to log H2O event\\n', error.stack);\n hasWarned = true;\n }\n }),\n );\n\n promise && waitUntil?.(promise);\n };\n}\n","/// <reference types=\"@shopify/hydrogen\" />\nimport {\n createRequestHandler as createRemixRequestHandler,\n type AppLoadContext,\n type ServerBuild,\n} from '@remix-run/server-runtime';\nimport {createEventLogger} from './event-logger';\n\nconst originalErrorToString = Error.prototype.toString;\nError.prototype.toString = function () {\n return this.stack || originalErrorToString.call(this);\n};\n\n/** Server-Timing header key to signal that the SFAPI proxy is enabled */\nconst HYDROGEN_SFAPI_PROXY_KEY = '_sfapi_proxy';\n\nlet hasWarnedAboutStorefront = false;\n\nfunction warnOnce(message: string) {\n if (!hasWarnedAboutStorefront) {\n hasWarnedAboutStorefront = true;\n console.warn(message);\n }\n}\n\nfunction buildServerTimingHeader(values: Record<string, string | undefined>) {\n return Object.entries(values)\n .map(([key, value]) => (value ? `${key};desc=${value}` : undefined))\n .filter(Boolean)\n .join(', ');\n}\n\nfunction appendServerTimingHeader(\n response: {headers: Headers},\n values: string | Record<string, string | undefined>,\n) {\n const header =\n typeof values === 'string' ? values : buildServerTimingHeader(values);\n\n if (header) {\n response.headers.append('Server-Timing', header);\n }\n}\n\ntype CreateRequestHandlerOptions<Context = unknown> = {\n /** Remix's server build */\n build: ServerBuild;\n /** Remix's mode */\n mode?: string;\n /**\n * Function to provide the load context for each request.\n * It must contain Hydrogen's storefront client instance\n * for other Hydrogen utilities to work properly.\n */\n getLoadContext?: (request: Request) => Promise<Context> | Context;\n /**\n * Whether to include the `powered-by` header in responses\n * @default true\n */\n poweredByHeader?: boolean;\n /**\n * Collect tracking information from subrequests such as cookies\n * and forward them to the browser. Disable this if you are not\n * using Hydrogen's built-in analytics.\n * @default true\n */\n collectTrackingInformation?: boolean;\n /**\n * Whether to proxy standard routes such as `/api/.../graphql.json` (Storefront API).\n * You can disable this if you are handling these routes yourself. Ensure that\n * the proxy works if you rely on Hydrogen's built-in behaviors such as analytics.\n * @default true\n */\n proxyStandardRoutes?: boolean;\n};\n\nexport function createRequestHandler<Context = unknown>({\n build,\n mode,\n poweredByHeader = true,\n getLoadContext,\n collectTrackingInformation = true,\n proxyStandardRoutes = true,\n}: CreateRequestHandlerOptions<Context>) {\n const handleRequest = createRemixRequestHandler(build, mode);\n\n const appendPoweredByHeader = poweredByHeader\n ? (response: Response) =>\n response.headers.append('powered-by', 'Shopify, Hydrogen')\n : undefined;\n\n return async (request: Request) => {\n const method = request.method;\n\n if ((method === 'GET' || method === 'HEAD') && request.body) {\n return new Response(`${method} requests cannot have a body`, {\n status: 400,\n });\n }\n\n const url = new URL(request.url);\n\n if (url.pathname.includes('//')) {\n return new Response(null, {\n status: 301,\n headers: {\n location: url.pathname.replace(/\\/+/g, '/'),\n },\n });\n }\n\n const context = getLoadContext\n ? ((await getLoadContext(request)) as AppLoadContext)\n : undefined;\n\n // Access storefront from context if available\n const storefront = (\n context as {storefront?: StorefrontForProxy} | undefined\n )?.storefront;\n\n if (proxyStandardRoutes) {\n if (!storefront) {\n // TODO: this should throw error in future major version\n warnOnce(\n '[h2:createRequestHandler] Storefront instance is required to proxy standard routes.',\n );\n }\n\n // Proxy Storefront API requests\n if (storefront?.isStorefrontApiUrl(request)) {\n const response = await storefront.forward(request);\n appendPoweredByHeader?.(response);\n return response;\n }\n }\n\n if (process.env.NODE_ENV === 'development' && context) {\n // Store logger in globalThis so it can be accessed from the worker.\n // The global property must be different from the binding name,\n // otherwise Miniflare throws an error when accessing it.\n globalThis.__H2O_LOG_EVENT ??= createEventLogger(context);\n }\n\n const startTime = Date.now();\n\n const response = await handleRequest(request, context);\n\n if (storefront && proxyStandardRoutes) {\n if (collectTrackingInformation) {\n storefront.setCollectedSubrequestHeaders(response);\n }\n\n // TODO: assume SFAPI proxy is available in future major version\n // Signal that SFAPI proxy is enabled for document requests.\n // Note: sec-fetch-dest is automatically added by modern browsers,\n // but we also check the Accept header for other clients.\n const fetchDest = request.headers.get('sec-fetch-dest');\n if (\n (fetchDest && fetchDest === 'document') ||\n request.headers.get('accept')?.includes('text/html')\n ) {\n appendServerTimingHeader(response, {[HYDROGEN_SFAPI_PROXY_KEY]: '1'});\n }\n }\n\n appendPoweredByHeader?.(response);\n\n if (process.env.NODE_ENV === 'development') {\n globalThis.__H2O_LOG_EVENT?.({\n eventType: 'request',\n url: request.url,\n requestId: request.headers.get('request-id'),\n purpose: request.headers.get('purpose'),\n startTime,\n responseInit: {\n status: response.status,\n statusText: response.statusText,\n headers: Array.from(response.headers.entries()),\n } satisfies ResponseInit,\n });\n }\n\n return response;\n };\n}\n\ntype StorefrontHeaders = {\n requestGroupId: string | null;\n buyerIp: string | null;\n buyerIpSig: string | null;\n cookie: string | null;\n purpose: string | null;\n};\n\nexport function getStorefrontHeaders(request: Request): StorefrontHeaders {\n const headers = request.headers;\n return {\n requestGroupId: headers.get('request-id'),\n buyerIp: headers.get('oxygen-buyer-ip'),\n buyerIpSig: headers.get('X-Shopify-Client-IP-Sig'),\n cookie: headers.get('cookie'),\n // sec-purpose is added by browsers automatically when using link/prefetch or Speculation Rules\n purpose: headers.get('sec-purpose') || headers.get('purpose'),\n };\n}\n\n/**\n * Minimal storefront interface needed for proxy functionality.\n * The full Storefront type is defined in @shopify/hydrogen.\n */\ntype StorefrontForProxy = {\n isStorefrontApiUrl: (request: {url?: string}) => boolean;\n forward: (request: Request) => Promise<Response>;\n setCollectedSubrequestHeaders: (response: {headers: Headers}) => void;\n};\n"]}
|
|
@@ -86,13 +86,33 @@ var originalErrorToString = Error.prototype.toString;
|
|
|
86
86
|
Error.prototype.toString = function() {
|
|
87
87
|
return this.stack || originalErrorToString.call(this);
|
|
88
88
|
};
|
|
89
|
+
var HYDROGEN_SFAPI_PROXY_KEY = "_sfapi_proxy";
|
|
90
|
+
var hasWarnedAboutStorefront = false;
|
|
91
|
+
function warnOnce(message) {
|
|
92
|
+
if (!hasWarnedAboutStorefront) {
|
|
93
|
+
hasWarnedAboutStorefront = true;
|
|
94
|
+
console.warn(message);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function buildServerTimingHeader(values) {
|
|
98
|
+
return Object.entries(values).map(([key, value]) => value ? `${key};desc=${value}` : void 0).filter(Boolean).join(", ");
|
|
99
|
+
}
|
|
100
|
+
function appendServerTimingHeader(response, values) {
|
|
101
|
+
const header = typeof values === "string" ? values : buildServerTimingHeader(values);
|
|
102
|
+
if (header) {
|
|
103
|
+
response.headers.append("Server-Timing", header);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
89
106
|
function createRequestHandler({
|
|
90
107
|
build,
|
|
91
108
|
mode,
|
|
92
109
|
poweredByHeader = true,
|
|
93
|
-
getLoadContext
|
|
110
|
+
getLoadContext,
|
|
111
|
+
collectTrackingInformation = true,
|
|
112
|
+
proxyStandardRoutes = true
|
|
94
113
|
}) {
|
|
95
114
|
const handleRequest = createRequestHandler$1(build, mode);
|
|
115
|
+
const appendPoweredByHeader = poweredByHeader ? (response) => response.headers.append("powered-by", "Shopify, Hydrogen") : void 0;
|
|
96
116
|
return async (request) => {
|
|
97
117
|
const method = request.method;
|
|
98
118
|
if ((method === "GET" || method === "HEAD") && request.body) {
|
|
@@ -110,14 +130,34 @@ function createRequestHandler({
|
|
|
110
130
|
});
|
|
111
131
|
}
|
|
112
132
|
const context = getLoadContext ? await getLoadContext(request) : void 0;
|
|
133
|
+
const storefront = context?.storefront;
|
|
134
|
+
if (proxyStandardRoutes) {
|
|
135
|
+
if (!storefront) {
|
|
136
|
+
warnOnce(
|
|
137
|
+
"[h2:createRequestHandler] Storefront instance is required to proxy standard routes."
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
if (storefront?.isStorefrontApiUrl(request)) {
|
|
141
|
+
const response2 = await storefront.forward(request);
|
|
142
|
+
appendPoweredByHeader?.(response2);
|
|
143
|
+
return response2;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
113
146
|
if (context) {
|
|
114
147
|
globalThis.__H2O_LOG_EVENT ??= createEventLogger(context);
|
|
115
148
|
}
|
|
116
149
|
const startTime = Date.now();
|
|
117
150
|
const response = await handleRequest(request, context);
|
|
118
|
-
if (
|
|
119
|
-
|
|
151
|
+
if (storefront && proxyStandardRoutes) {
|
|
152
|
+
if (collectTrackingInformation) {
|
|
153
|
+
storefront.setCollectedSubrequestHeaders(response);
|
|
154
|
+
}
|
|
155
|
+
const fetchDest = request.headers.get("sec-fetch-dest");
|
|
156
|
+
if (fetchDest && fetchDest === "document" || request.headers.get("accept")?.includes("text/html")) {
|
|
157
|
+
appendServerTimingHeader(response, { [HYDROGEN_SFAPI_PROXY_KEY]: "1" });
|
|
158
|
+
}
|
|
120
159
|
}
|
|
160
|
+
appendPoweredByHeader?.(response);
|
|
121
161
|
{
|
|
122
162
|
globalThis.__H2O_LOG_EVENT?.({
|
|
123
163
|
eventType: "request",
|
|
@@ -140,11 +180,13 @@ function getStorefrontHeaders(request) {
|
|
|
140
180
|
return {
|
|
141
181
|
requestGroupId: headers.get("request-id"),
|
|
142
182
|
buyerIp: headers.get("oxygen-buyer-ip"),
|
|
183
|
+
buyerIpSig: headers.get("X-Shopify-Client-IP-Sig"),
|
|
143
184
|
cookie: headers.get("cookie"),
|
|
144
|
-
purpose
|
|
185
|
+
// sec-purpose is added by browsers automatically when using link/prefetch or Speculation Rules
|
|
186
|
+
purpose: headers.get("sec-purpose") || headers.get("purpose")
|
|
145
187
|
};
|
|
146
188
|
}
|
|
147
189
|
|
|
148
190
|
export { createCookie, createCookieSessionStorage, createMemorySessionStorage, createRequestHandler, createSessionStorage, getStorefrontHeaders };
|
|
149
|
-
//# sourceMappingURL=
|
|
191
|
+
//# sourceMappingURL=index.js.map
|
|
150
192
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/implementations.ts","../../src/crypto.ts","../../src/server.ts","../../src/event-logger.ts","../../src/index.ts"],"names":["data"],"mappings":"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","sourcesContent":["import {\n createCookieFactory,\n createCookieSessionStorageFactory,\n createMemorySessionStorageFactory,\n createSessionStorageFactory,\n} from '@remix-run/server-runtime';\n\nimport {sign, unsign} from './crypto';\n\nexport const createCookie = createCookieFactory({sign, unsign});\nexport const createCookieSessionStorage =\n createCookieSessionStorageFactory(createCookie);\nexport const createSessionStorage = createSessionStorageFactory(createCookie);\nexport const createMemorySessionStorage =\n createMemorySessionStorageFactory(createSessionStorage);\n","import type {SignFunction, UnsignFunction} from '@remix-run/server-runtime';\n\nconst encoder = new TextEncoder();\n\nexport const sign: SignFunction = async (value, secret) => {\n const key = await createKey(secret, ['sign']);\n const data = encoder.encode(value);\n const signature = await crypto.subtle.sign('HMAC', key, data);\n const hash = btoa(String.fromCharCode(...new Uint8Array(signature))).replace(\n /=+$/,\n '',\n );\n\n return value + '.' + hash;\n};\n\nexport const unsign: UnsignFunction = async (signed, secret) => {\n const index = signed.lastIndexOf('.');\n const value = signed.slice(0, index);\n const hash = signed.slice(index + 1);\n\n const key = await createKey(secret, ['verify']);\n const data = encoder.encode(value);\n const signature = byteStringToUint8Array(atob(hash));\n const valid = await crypto.subtle.verify('HMAC', key, signature, data);\n\n return valid ? value : false;\n};\n\nasync function createKey(\n secret: string,\n usages: CryptoKey['usages'],\n): Promise<CryptoKey> {\n const key = await crypto.subtle.importKey(\n 'raw',\n encoder.encode(secret),\n {name: 'HMAC', hash: 'SHA-256'},\n false,\n usages,\n );\n\n return key;\n}\n\nfunction byteStringToUint8Array(byteString: string): Uint8Array {\n const array = new Uint8Array(byteString.length);\n\n for (let i = 0; i < byteString.length; i++) {\n array[i] = byteString.charCodeAt(i);\n }\n\n return array;\n}\n","/// <reference types=\"@shopify/hydrogen\" />\nimport {\n createRequestHandler as createRemixRequestHandler,\n type AppLoadContext,\n type ServerBuild,\n} from '@remix-run/server-runtime';\nimport {createEventLogger} from './event-logger';\n\nconst originalErrorToString = Error.prototype.toString;\nError.prototype.toString = function () {\n return this.stack || originalErrorToString.call(this);\n};\n\nexport function createRequestHandler<Context = unknown>({\n build,\n mode,\n poweredByHeader = true,\n getLoadContext,\n}: {\n build: ServerBuild;\n mode?: string;\n poweredByHeader?: boolean;\n getLoadContext?: (request: Request) => Promise<Context> | Context;\n}) {\n const handleRequest = createRemixRequestHandler(build, mode);\n\n return async (request: Request) => {\n const method = request.method;\n\n if ((method === 'GET' || method === 'HEAD') && request.body) {\n return new Response(`${method} requests cannot have a body`, {\n status: 400,\n });\n }\n\n const url = new URL(request.url);\n\n if (url.pathname.includes('//')) {\n return new Response(null, {\n status: 301,\n headers: {\n location: url.pathname.replace(/\\/+/g, '/'),\n },\n });\n }\n\n const context = getLoadContext\n ? ((await getLoadContext(request)) as AppLoadContext)\n : undefined;\n\n if (process.env.NODE_ENV === 'development' && context) {\n // Store logger in globalThis so it can be accessed from the worker.\n // The global property must be different from the binding name,\n // otherwise Miniflare throws an error when accessing it.\n globalThis.__H2O_LOG_EVENT ??= createEventLogger(context);\n }\n\n const startTime = Date.now();\n\n const response = await handleRequest(request, context);\n\n if (poweredByHeader) {\n response.headers.append('powered-by', 'Shopify, Hydrogen');\n }\n\n if (process.env.NODE_ENV === 'development') {\n globalThis.__H2O_LOG_EVENT?.({\n eventType: 'request',\n url: request.url,\n requestId: request.headers.get('request-id'),\n purpose: request.headers.get('purpose'),\n startTime,\n responseInit: {\n status: response.status,\n statusText: response.statusText,\n headers: Array.from(response.headers.entries()),\n } satisfies ResponseInit,\n });\n }\n\n return response;\n };\n}\n\ntype StorefrontHeaders = {\n requestGroupId: string | null;\n buyerIp: string | null;\n cookie: string | null;\n purpose: string | null;\n};\n\nexport function getStorefrontHeaders(request: Request): StorefrontHeaders {\n const headers = request.headers;\n return {\n requestGroupId: headers.get('request-id'),\n buyerIp: headers.get('oxygen-buyer-ip'),\n cookie: headers.get('cookie'),\n purpose: headers.get('purpose'),\n };\n}\n","type H2OEvent = Parameters<NonNullable<typeof __H2O_LOG_EVENT>>[0];\n\nlet hasWarned = false;\n\n/**\n * @deprecated Only used with the classic Remix compiler\n */\nexport function createEventLogger(appLoadContext: Record<string, unknown>) {\n const context = (appLoadContext || {}) as {\n env?: Record<string, any>;\n waitUntil?: (promise: Promise<any>) => void;\n };\n\n const eventLoggerService = context?.env?.H2O_LOG_EVENT as\n | undefined\n | {fetch: (req: Request) => Promise<Response>};\n\n if (typeof eventLoggerService?.fetch !== 'function') return;\n\n return ({\n url,\n endTime = Date.now(),\n waitUntil = context?.waitUntil,\n ...rest\n }: H2OEvent) => {\n const promise = Promise.resolve().then(() =>\n eventLoggerService\n .fetch(\n new Request(url, {\n method: 'POST',\n body: JSON.stringify({\n endTime,\n ...rest,\n }),\n }),\n )\n .catch((error: Error) => {\n if (!hasWarned) {\n // This might repeat a lot of times due to\n // the same issue, so we only warn once.\n console.debug('Failed to log H2O event\\n', error.stack);\n hasWarned = true;\n }\n }),\n );\n\n promise && waitUntil?.(promise);\n };\n}\n","export {\n createCookie,\n createCookieSessionStorage,\n createMemorySessionStorage,\n createSessionStorage,\n} from './implementations';\nexport {createRequestHandler, getStorefrontHeaders} from './server';\nexport type {\n ActionFunction,\n ActionFunctionArgs,\n AppLoadContext,\n Cookie,\n CookieOptions,\n CookieParseOptions,\n CookieSerializeOptions,\n CookieSignatureOptions,\n DataFunctionArgs,\n EntryContext,\n ErrorResponse,\n HandleDataRequestFunction,\n HandleDocumentRequestFunction,\n HandleErrorFunction,\n HeadersArgs,\n HeadersFunction,\n HtmlLinkDescriptor,\n JsonFunction,\n LinkDescriptor,\n LinksFunction,\n LoaderFunction,\n LoaderFunctionArgs,\n MemoryUploadHandlerFilterArgs,\n MemoryUploadHandlerOptions,\n ServerRuntimeMetaArgs as MetaArgs,\n ServerRuntimeMetaDescriptor as MetaDescriptor,\n ServerRuntimeMetaFunction as MetaFunction,\n PageLinkDescriptor,\n RequestHandler,\n SerializeFrom,\n ServerBuild,\n ServerEntryModule,\n Session,\n SessionData,\n SessionIdStorageStrategy,\n SessionStorage,\n SignFunction,\n TypedDeferredData,\n TypedResponse,\n UnsignFunction,\n UploadHandler,\n UploadHandlerPart,\n} from '@remix-run/server-runtime';\nexport {\n createSession,\n data,\n defer,\n isCookie,\n isSession,\n json,\n MaxPartSizeExceededError,\n redirect,\n redirectDocument,\n} from '@remix-run/server-runtime';\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/crypto.ts","../../src/implementations.ts","../../src/event-logger.ts","../../src/server.ts"],"names":["data","createRemixRequestHandler","response"],"mappings":"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","file":"index.js","sourcesContent":["import type {SignFunction, UnsignFunction} from '@remix-run/server-runtime';\n\nconst encoder = new TextEncoder();\n\nexport const sign: SignFunction = async (value, secret) => {\n const key = await createKey(secret, ['sign']);\n const data = encoder.encode(value);\n const signature = await crypto.subtle.sign('HMAC', key, data);\n const hash = btoa(String.fromCharCode(...new Uint8Array(signature))).replace(\n /=+$/,\n '',\n );\n\n return value + '.' + hash;\n};\n\nexport const unsign: UnsignFunction = async (signed, secret) => {\n const index = signed.lastIndexOf('.');\n const value = signed.slice(0, index);\n const hash = signed.slice(index + 1);\n\n const key = await createKey(secret, ['verify']);\n const data = encoder.encode(value);\n const signature = byteStringToUint8Array(atob(hash));\n const valid = await crypto.subtle.verify('HMAC', key, signature, data);\n\n return valid ? value : false;\n};\n\nasync function createKey(\n secret: string,\n usages: CryptoKey['usages'],\n): Promise<CryptoKey> {\n const key = await crypto.subtle.importKey(\n 'raw',\n encoder.encode(secret),\n {name: 'HMAC', hash: 'SHA-256'},\n false,\n usages,\n );\n\n return key;\n}\n\nfunction byteStringToUint8Array(byteString: string): Uint8Array {\n const array = new Uint8Array(byteString.length);\n\n for (let i = 0; i < byteString.length; i++) {\n array[i] = byteString.charCodeAt(i);\n }\n\n return array;\n}\n","import {\n createCookieFactory,\n createCookieSessionStorageFactory,\n createMemorySessionStorageFactory,\n createSessionStorageFactory,\n} from '@remix-run/server-runtime';\n\nimport {sign, unsign} from './crypto';\n\nexport const createCookie = createCookieFactory({sign, unsign});\nexport const createCookieSessionStorage =\n createCookieSessionStorageFactory(createCookie);\nexport const createSessionStorage = createSessionStorageFactory(createCookie);\nexport const createMemorySessionStorage =\n createMemorySessionStorageFactory(createSessionStorage);\n","type H2OEvent = Parameters<NonNullable<typeof __H2O_LOG_EVENT>>[0];\n\nlet hasWarned = false;\n\n/**\n * @deprecated Only used with the classic Remix compiler\n */\nexport function createEventLogger(appLoadContext: Record<string, unknown>) {\n const context = (appLoadContext || {}) as {\n env?: Record<string, any>;\n waitUntil?: (promise: Promise<any>) => void;\n };\n\n const eventLoggerService = context?.env?.H2O_LOG_EVENT as\n | undefined\n | {fetch: (req: Request) => Promise<Response>};\n\n if (typeof eventLoggerService?.fetch !== 'function') return;\n\n return ({\n url,\n endTime = Date.now(),\n waitUntil = context?.waitUntil,\n ...rest\n }: H2OEvent) => {\n const promise = Promise.resolve().then(() =>\n eventLoggerService\n .fetch(\n new Request(url, {\n method: 'POST',\n body: JSON.stringify({\n endTime,\n ...rest,\n }),\n }),\n )\n .catch((error: Error) => {\n if (!hasWarned) {\n // This might repeat a lot of times due to\n // the same issue, so we only warn once.\n console.debug('Failed to log H2O event\\n', error.stack);\n hasWarned = true;\n }\n }),\n );\n\n promise && waitUntil?.(promise);\n };\n}\n","/// <reference types=\"@shopify/hydrogen\" />\nimport {\n createRequestHandler as createRemixRequestHandler,\n type AppLoadContext,\n type ServerBuild,\n} from '@remix-run/server-runtime';\nimport {createEventLogger} from './event-logger';\n\nconst originalErrorToString = Error.prototype.toString;\nError.prototype.toString = function () {\n return this.stack || originalErrorToString.call(this);\n};\n\n/** Server-Timing header key to signal that the SFAPI proxy is enabled */\nconst HYDROGEN_SFAPI_PROXY_KEY = '_sfapi_proxy';\n\nlet hasWarnedAboutStorefront = false;\n\nfunction warnOnce(message: string) {\n if (!hasWarnedAboutStorefront) {\n hasWarnedAboutStorefront = true;\n console.warn(message);\n }\n}\n\nfunction buildServerTimingHeader(values: Record<string, string | undefined>) {\n return Object.entries(values)\n .map(([key, value]) => (value ? `${key};desc=${value}` : undefined))\n .filter(Boolean)\n .join(', ');\n}\n\nfunction appendServerTimingHeader(\n response: {headers: Headers},\n values: string | Record<string, string | undefined>,\n) {\n const header =\n typeof values === 'string' ? values : buildServerTimingHeader(values);\n\n if (header) {\n response.headers.append('Server-Timing', header);\n }\n}\n\ntype CreateRequestHandlerOptions<Context = unknown> = {\n /** Remix's server build */\n build: ServerBuild;\n /** Remix's mode */\n mode?: string;\n /**\n * Function to provide the load context for each request.\n * It must contain Hydrogen's storefront client instance\n * for other Hydrogen utilities to work properly.\n */\n getLoadContext?: (request: Request) => Promise<Context> | Context;\n /**\n * Whether to include the `powered-by` header in responses\n * @default true\n */\n poweredByHeader?: boolean;\n /**\n * Collect tracking information from subrequests such as cookies\n * and forward them to the browser. Disable this if you are not\n * using Hydrogen's built-in analytics.\n * @default true\n */\n collectTrackingInformation?: boolean;\n /**\n * Whether to proxy standard routes such as `/api/.../graphql.json` (Storefront API).\n * You can disable this if you are handling these routes yourself. Ensure that\n * the proxy works if you rely on Hydrogen's built-in behaviors such as analytics.\n * @default true\n */\n proxyStandardRoutes?: boolean;\n};\n\nexport function createRequestHandler<Context = unknown>({\n build,\n mode,\n poweredByHeader = true,\n getLoadContext,\n collectTrackingInformation = true,\n proxyStandardRoutes = true,\n}: CreateRequestHandlerOptions<Context>) {\n const handleRequest = createRemixRequestHandler(build, mode);\n\n const appendPoweredByHeader = poweredByHeader\n ? (response: Response) =>\n response.headers.append('powered-by', 'Shopify, Hydrogen')\n : undefined;\n\n return async (request: Request) => {\n const method = request.method;\n\n if ((method === 'GET' || method === 'HEAD') && request.body) {\n return new Response(`${method} requests cannot have a body`, {\n status: 400,\n });\n }\n\n const url = new URL(request.url);\n\n if (url.pathname.includes('//')) {\n return new Response(null, {\n status: 301,\n headers: {\n location: url.pathname.replace(/\\/+/g, '/'),\n },\n });\n }\n\n const context = getLoadContext\n ? ((await getLoadContext(request)) as AppLoadContext)\n : undefined;\n\n // Access storefront from context if available\n const storefront = (\n context as {storefront?: StorefrontForProxy} | undefined\n )?.storefront;\n\n if (proxyStandardRoutes) {\n if (!storefront) {\n // TODO: this should throw error in future major version\n warnOnce(\n '[h2:createRequestHandler] Storefront instance is required to proxy standard routes.',\n );\n }\n\n // Proxy Storefront API requests\n if (storefront?.isStorefrontApiUrl(request)) {\n const response = await storefront.forward(request);\n appendPoweredByHeader?.(response);\n return response;\n }\n }\n\n if (process.env.NODE_ENV === 'development' && context) {\n // Store logger in globalThis so it can be accessed from the worker.\n // The global property must be different from the binding name,\n // otherwise Miniflare throws an error when accessing it.\n globalThis.__H2O_LOG_EVENT ??= createEventLogger(context);\n }\n\n const startTime = Date.now();\n\n const response = await handleRequest(request, context);\n\n if (storefront && proxyStandardRoutes) {\n if (collectTrackingInformation) {\n storefront.setCollectedSubrequestHeaders(response);\n }\n\n // TODO: assume SFAPI proxy is available in future major version\n // Signal that SFAPI proxy is enabled for document requests.\n // Note: sec-fetch-dest is automatically added by modern browsers,\n // but we also check the Accept header for other clients.\n const fetchDest = request.headers.get('sec-fetch-dest');\n if (\n (fetchDest && fetchDest === 'document') ||\n request.headers.get('accept')?.includes('text/html')\n ) {\n appendServerTimingHeader(response, {[HYDROGEN_SFAPI_PROXY_KEY]: '1'});\n }\n }\n\n appendPoweredByHeader?.(response);\n\n if (process.env.NODE_ENV === 'development') {\n globalThis.__H2O_LOG_EVENT?.({\n eventType: 'request',\n url: request.url,\n requestId: request.headers.get('request-id'),\n purpose: request.headers.get('purpose'),\n startTime,\n responseInit: {\n status: response.status,\n statusText: response.statusText,\n headers: Array.from(response.headers.entries()),\n } satisfies ResponseInit,\n });\n }\n\n return response;\n };\n}\n\ntype StorefrontHeaders = {\n requestGroupId: string | null;\n buyerIp: string | null;\n buyerIpSig: string | null;\n cookie: string | null;\n purpose: string | null;\n};\n\nexport function getStorefrontHeaders(request: Request): StorefrontHeaders {\n const headers = request.headers;\n return {\n requestGroupId: headers.get('request-id'),\n buyerIp: headers.get('oxygen-buyer-ip'),\n buyerIpSig: headers.get('X-Shopify-Client-IP-Sig'),\n cookie: headers.get('cookie'),\n // sec-purpose is added by browsers automatically when using link/prefetch or Speculation Rules\n purpose: headers.get('sec-purpose') || headers.get('purpose'),\n };\n}\n\n/**\n * Minimal storefront interface needed for proxy functionality.\n * The full Storefront type is defined in @shopify/hydrogen.\n */\ntype StorefrontForProxy = {\n isStorefrontApiUrl: (request: {url?: string}) => boolean;\n forward: (request: Request) => Promise<Response>;\n setCollectedSubrequestHeaders: (response: {headers: Headers}) => void;\n};\n"]}
|
|
@@ -1,50 +1,3 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var serverRuntime = require('@remix-run/server-runtime');
|
|
4
|
-
|
|
5
|
-
var c=new TextEncoder,l=async(e,t)=>{let r=await y(t,["sign"]),n=c.encode(e),s=await crypto.subtle.sign("HMAC",r,n),o=btoa(String.fromCharCode(...new Uint8Array(s))).replace(/=+$/,"");return e+"."+o},g=async(e,t)=>{let r=e.lastIndexOf("."),n=e.slice(0,r),s=e.slice(r+1),o=await y(t,["verify"]),a=c.encode(n),i=m(atob(s));return await crypto.subtle.verify("HMAC",o,i,a)?n:!1};async function y(e,t){return await crypto.subtle.importKey("raw",c.encode(e),{name:"HMAC",hash:"SHA-256"},!1,t)}function m(e){let t=new Uint8Array(e.length);for(let r=0;r<e.length;r++)t[r]=e.charCodeAt(r);return t}var u=serverRuntime.createCookieFactory({sign:l,unsign:g}),C=serverRuntime.createCookieSessionStorageFactory(u),S=serverRuntime.createSessionStorageFactory(u),k=serverRuntime.createMemorySessionStorageFactory(S);var A=Error.prototype.toString;Error.prototype.toString=function(){return this.stack||A.call(this)};function E({build:e,mode:t,poweredByHeader:r=!0,getLoadContext:n}){let s=serverRuntime.createRequestHandler(e,t);return async o=>{let a=o.method;if((a==="GET"||a==="HEAD")&&o.body)return new Response(`${a} requests cannot have a body`,{status:400});let i=new URL(o.url);if(i.pathname.includes("//"))return new Response(null,{status:301,headers:{location:i.pathname.replace(/\/+/g,"/")}});let p=n?await n(o):void 0,d=await s(o,p);return r&&d.headers.append("powered-by","Shopify, Hydrogen"),d}}function v(e){let t=e.headers;return {requestGroupId:t.get("request-id"),buyerIp:t.get("oxygen-buyer-ip"),cookie:t.get("cookie"),purpose:t.get("purpose")}}
|
|
6
|
-
|
|
7
|
-
Object.defineProperty(exports, "MaxPartSizeExceededError", {
|
|
8
|
-
enumerable: true,
|
|
9
|
-
get: function () { return serverRuntime.MaxPartSizeExceededError; }
|
|
10
|
-
});
|
|
11
|
-
Object.defineProperty(exports, "createSession", {
|
|
12
|
-
enumerable: true,
|
|
13
|
-
get: function () { return serverRuntime.createSession; }
|
|
14
|
-
});
|
|
15
|
-
Object.defineProperty(exports, "data", {
|
|
16
|
-
enumerable: true,
|
|
17
|
-
get: function () { return serverRuntime.data; }
|
|
18
|
-
});
|
|
19
|
-
Object.defineProperty(exports, "defer", {
|
|
20
|
-
enumerable: true,
|
|
21
|
-
get: function () { return serverRuntime.defer; }
|
|
22
|
-
});
|
|
23
|
-
Object.defineProperty(exports, "isCookie", {
|
|
24
|
-
enumerable: true,
|
|
25
|
-
get: function () { return serverRuntime.isCookie; }
|
|
26
|
-
});
|
|
27
|
-
Object.defineProperty(exports, "isSession", {
|
|
28
|
-
enumerable: true,
|
|
29
|
-
get: function () { return serverRuntime.isSession; }
|
|
30
|
-
});
|
|
31
|
-
Object.defineProperty(exports, "json", {
|
|
32
|
-
enumerable: true,
|
|
33
|
-
get: function () { return serverRuntime.json; }
|
|
34
|
-
});
|
|
35
|
-
Object.defineProperty(exports, "redirect", {
|
|
36
|
-
enumerable: true,
|
|
37
|
-
get: function () { return serverRuntime.redirect; }
|
|
38
|
-
});
|
|
39
|
-
Object.defineProperty(exports, "redirectDocument", {
|
|
40
|
-
enumerable: true,
|
|
41
|
-
get: function () { return serverRuntime.redirectDocument; }
|
|
42
|
-
});
|
|
43
|
-
exports.createCookie = u;
|
|
44
|
-
exports.createCookieSessionStorage = C;
|
|
45
|
-
exports.createMemorySessionStorage = k;
|
|
46
|
-
exports.createRequestHandler = E;
|
|
47
|
-
exports.createSessionStorage = S;
|
|
48
|
-
exports.getStorefrontHeaders = v;
|
|
49
|
-
//# sourceMappingURL=out.js.map
|
|
1
|
+
'use strict';var serverRuntime=require('@remix-run/server-runtime');var g=new TextEncoder,m=async(t,e)=>{let r=await x(e,["sign"]),n=g.encode(t),i=await crypto.subtle.sign("HMAC",r,n),s=btoa(String.fromCharCode(...new Uint8Array(i))).replace(/=+$/,"");return t+"."+s},h=async(t,e)=>{let r=t.lastIndexOf("."),n=t.slice(0,r),i=t.slice(r+1),s=await x(e,["verify"]),p=g.encode(n),u=R(atob(i));return await crypto.subtle.verify("HMAC",s,u,p)?n:false};async function x(t,e){return await crypto.subtle.importKey("raw",g.encode(t),{name:"HMAC",hash:"SHA-256"},false,e)}function R(t){let e=new Uint8Array(t.length);for(let r=0;r<t.length;r++)e[r]=t.charCodeAt(r);return e}var f=serverRuntime.createCookieFactory({sign:m,unsign:h}),b=serverRuntime.createCookieSessionStorageFactory(f),H=serverRuntime.createSessionStorageFactory(f),E=serverRuntime.createMemorySessionStorageFactory(H);var v=Error.prototype.toString;Error.prototype.toString=function(){return this.stack||v.call(this)};var T="_sfapi_proxy",C=false;function D(t){C||(C=true,console.warn(t));}function O(t){return Object.entries(t).map(([e,r])=>r?`${e};desc=${r}`:void 0).filter(Boolean).join(", ")}function M(t,e){let r=typeof e=="string"?e:O(e);r&&t.headers.append("Server-Timing",r);}function _({build:t,mode:e,poweredByHeader:r=true,getLoadContext:n,collectTrackingInformation:i=true,proxyStandardRoutes:s=true}){let p=serverRuntime.createRequestHandler(t,e),u=r?o=>o.headers.append("powered-by","Shopify, Hydrogen"):void 0;return async o=>{let l=o.method;if((l==="GET"||l==="HEAD")&&o.body)return new Response(`${l} requests cannot have a body`,{status:400});let y=new URL(o.url);if(y.pathname.includes("//"))return new Response(null,{status:301,headers:{location:y.pathname.replace(/\/+/g,"/")}});let S=n?await n(o):void 0,a=S?.storefront;if(s&&(a||D("[h2:createRequestHandler] Storefront instance is required to proxy standard routes."),a?.isStorefrontApiUrl(o))){let c=await a.forward(o);return u?.(c),c}let d=await p(o,S);if(a&&s){i&&a.setCollectedSubrequestHeaders(d);let c=o.headers.get("sec-fetch-dest");(c&&c==="document"||o.headers.get("accept")?.includes("text/html"))&&M(d,{[T]:"1"});}return u?.(d),d}}function I(t){let e=t.headers;return {requestGroupId:e.get("request-id"),buyerIp:e.get("oxygen-buyer-ip"),buyerIpSig:e.get("X-Shopify-Client-IP-Sig"),cookie:e.get("cookie"),purpose:e.get("sec-purpose")||e.get("purpose")}}
|
|
2
|
+
Object.defineProperty(exports,"MaxPartSizeExceededError",{enumerable:true,get:function(){return serverRuntime.MaxPartSizeExceededError}});Object.defineProperty(exports,"createSession",{enumerable:true,get:function(){return serverRuntime.createSession}});Object.defineProperty(exports,"data",{enumerable:true,get:function(){return serverRuntime.data}});Object.defineProperty(exports,"defer",{enumerable:true,get:function(){return serverRuntime.defer}});Object.defineProperty(exports,"isCookie",{enumerable:true,get:function(){return serverRuntime.isCookie}});Object.defineProperty(exports,"isSession",{enumerable:true,get:function(){return serverRuntime.isSession}});Object.defineProperty(exports,"json",{enumerable:true,get:function(){return serverRuntime.json}});Object.defineProperty(exports,"redirect",{enumerable:true,get:function(){return serverRuntime.redirect}});Object.defineProperty(exports,"redirectDocument",{enumerable:true,get:function(){return serverRuntime.redirectDocument}});exports.createCookie=f;exports.createCookieSessionStorage=b;exports.createMemorySessionStorage=E;exports.createRequestHandler=_;exports.createSessionStorage=H;exports.getStorefrontHeaders=I;//# sourceMappingURL=index.cjs.map
|
|
50
3
|
//# sourceMappingURL=index.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/implementations.ts","../../src/crypto.ts","../../src/server.ts","../../src/index.ts"],"names":["createCookieFactory","createCookieSessionStorageFactory","createMemorySessionStorageFactory","createSessionStorageFactory","encoder","sign","value","secret","key","createKey","data","signature","hash","unsign","signed","index","byteStringToUint8Array","usages","byteString","array","i","createCookie","createCookieSessionStorage","createSessionStorage","createMemorySessionStorage","createRemixRequestHandler","originalErrorToString","createRequestHandler","build","mode","poweredByHeader","getLoadContext","handleRequest","request","method","url","context","startTime","response","getStorefrontHeaders","headers","createSession","defer","isCookie","isSession","json","MaxPartSizeExceededError","redirect","redirectDocument"],"mappings":"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","sourcesContent":["import {\n createCookieFactory,\n createCookieSessionStorageFactory,\n createMemorySessionStorageFactory,\n createSessionStorageFactory,\n} from '@remix-run/server-runtime';\n\nimport {sign, unsign} from './crypto';\n\nexport const createCookie = createCookieFactory({sign, unsign});\nexport const createCookieSessionStorage =\n createCookieSessionStorageFactory(createCookie);\nexport const createSessionStorage = createSessionStorageFactory(createCookie);\nexport const createMemorySessionStorage =\n createMemorySessionStorageFactory(createSessionStorage);\n","import type {SignFunction, UnsignFunction} from '@remix-run/server-runtime';\n\nconst encoder = new TextEncoder();\n\nexport const sign: SignFunction = async (value, secret) => {\n const key = await createKey(secret, ['sign']);\n const data = encoder.encode(value);\n const signature = await crypto.subtle.sign('HMAC', key, data);\n const hash = btoa(String.fromCharCode(...new Uint8Array(signature))).replace(\n /=+$/,\n '',\n );\n\n return value + '.' + hash;\n};\n\nexport const unsign: UnsignFunction = async (signed, secret) => {\n const index = signed.lastIndexOf('.');\n const value = signed.slice(0, index);\n const hash = signed.slice(index + 1);\n\n const key = await createKey(secret, ['verify']);\n const data = encoder.encode(value);\n const signature = byteStringToUint8Array(atob(hash));\n const valid = await crypto.subtle.verify('HMAC', key, signature, data);\n\n return valid ? value : false;\n};\n\nasync function createKey(\n secret: string,\n usages: CryptoKey['usages'],\n): Promise<CryptoKey> {\n const key = await crypto.subtle.importKey(\n 'raw',\n encoder.encode(secret),\n {name: 'HMAC', hash: 'SHA-256'},\n false,\n usages,\n );\n\n return key;\n}\n\nfunction byteStringToUint8Array(byteString: string): Uint8Array {\n const array = new Uint8Array(byteString.length);\n\n for (let i = 0; i < byteString.length; i++) {\n array[i] = byteString.charCodeAt(i);\n }\n\n return array;\n}\n","/// <reference types=\"@shopify/hydrogen\" />\nimport {\n createRequestHandler as createRemixRequestHandler,\n type AppLoadContext,\n type ServerBuild,\n} from '@remix-run/server-runtime';\nimport {createEventLogger} from './event-logger';\n\nconst originalErrorToString = Error.prototype.toString;\nError.prototype.toString = function () {\n return this.stack || originalErrorToString.call(this);\n};\n\nexport function createRequestHandler<Context = unknown>({\n build,\n mode,\n poweredByHeader = true,\n getLoadContext,\n}: {\n build: ServerBuild;\n mode?: string;\n poweredByHeader?: boolean;\n getLoadContext?: (request: Request) => Promise<Context> | Context;\n}) {\n const handleRequest = createRemixRequestHandler(build, mode);\n\n return async (request: Request) => {\n const method = request.method;\n\n if ((method === 'GET' || method === 'HEAD') && request.body) {\n return new Response(`${method} requests cannot have a body`, {\n status: 400,\n });\n }\n\n const url = new URL(request.url);\n\n if (url.pathname.includes('//')) {\n return new Response(null, {\n status: 301,\n headers: {\n location: url.pathname.replace(/\\/+/g, '/'),\n },\n });\n }\n\n const context = getLoadContext\n ? ((await getLoadContext(request)) as AppLoadContext)\n : undefined;\n\n if (process.env.NODE_ENV === 'development' && context) {\n // Store logger in globalThis so it can be accessed from the worker.\n // The global property must be different from the binding name,\n // otherwise Miniflare throws an error when accessing it.\n globalThis.__H2O_LOG_EVENT ??= createEventLogger(context);\n }\n\n const startTime = Date.now();\n\n const response = await handleRequest(request, context);\n\n if (poweredByHeader) {\n response.headers.append('powered-by', 'Shopify, Hydrogen');\n }\n\n if (process.env.NODE_ENV === 'development') {\n globalThis.__H2O_LOG_EVENT?.({\n eventType: 'request',\n url: request.url,\n requestId: request.headers.get('request-id'),\n purpose: request.headers.get('purpose'),\n startTime,\n responseInit: {\n status: response.status,\n statusText: response.statusText,\n headers: Array.from(response.headers.entries()),\n } satisfies ResponseInit,\n });\n }\n\n return response;\n };\n}\n\ntype StorefrontHeaders = {\n requestGroupId: string | null;\n buyerIp: string | null;\n cookie: string | null;\n purpose: string | null;\n};\n\nexport function getStorefrontHeaders(request: Request): StorefrontHeaders {\n const headers = request.headers;\n return {\n requestGroupId: headers.get('request-id'),\n buyerIp: headers.get('oxygen-buyer-ip'),\n cookie: headers.get('cookie'),\n purpose: headers.get('purpose'),\n };\n}\n","export {\n createCookie,\n createCookieSessionStorage,\n createMemorySessionStorage,\n createSessionStorage,\n} from './implementations';\nexport {createRequestHandler, getStorefrontHeaders} from './server';\nexport type {\n ActionFunction,\n ActionFunctionArgs,\n AppLoadContext,\n Cookie,\n CookieOptions,\n CookieParseOptions,\n CookieSerializeOptions,\n CookieSignatureOptions,\n DataFunctionArgs,\n EntryContext,\n ErrorResponse,\n HandleDataRequestFunction,\n HandleDocumentRequestFunction,\n HandleErrorFunction,\n HeadersArgs,\n HeadersFunction,\n HtmlLinkDescriptor,\n JsonFunction,\n LinkDescriptor,\n LinksFunction,\n LoaderFunction,\n LoaderFunctionArgs,\n MemoryUploadHandlerFilterArgs,\n MemoryUploadHandlerOptions,\n ServerRuntimeMetaArgs as MetaArgs,\n ServerRuntimeMetaDescriptor as MetaDescriptor,\n ServerRuntimeMetaFunction as MetaFunction,\n PageLinkDescriptor,\n RequestHandler,\n SerializeFrom,\n ServerBuild,\n ServerEntryModule,\n Session,\n SessionData,\n SessionIdStorageStrategy,\n SessionStorage,\n SignFunction,\n TypedDeferredData,\n TypedResponse,\n UnsignFunction,\n UploadHandler,\n UploadHandlerPart,\n} from '@remix-run/server-runtime';\nexport {\n createSession,\n data,\n defer,\n isCookie,\n isSession,\n json,\n MaxPartSizeExceededError,\n redirect,\n redirectDocument,\n} from '@remix-run/server-runtime';\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/crypto.ts","../../src/implementations.ts","../../src/server.ts"],"names":["encoder","sign","value","secret","key","createKey","data","signature","hash","unsign","signed","index","byteStringToUint8Array","usages","byteString","array","i","createCookie","createCookieFactory","createCookieSessionStorage","createCookieSessionStorageFactory","createSessionStorage","createSessionStorageFactory","createMemorySessionStorage","createMemorySessionStorageFactory","originalErrorToString","HYDROGEN_SFAPI_PROXY_KEY","hasWarnedAboutStorefront","warnOnce","message","buildServerTimingHeader","values","appendServerTimingHeader","response","header","createRequestHandler","build","mode","poweredByHeader","getLoadContext","collectTrackingInformation","proxyStandardRoutes","handleRequest","createRemixRequestHandler","appendPoweredByHeader","request","method","url","context","storefront","fetchDest","getStorefrontHeaders","headers"],"mappings":"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","file":"index.cjs","sourcesContent":["import type {SignFunction, UnsignFunction} from '@remix-run/server-runtime';\n\nconst encoder = new TextEncoder();\n\nexport const sign: SignFunction = async (value, secret) => {\n const key = await createKey(secret, ['sign']);\n const data = encoder.encode(value);\n const signature = await crypto.subtle.sign('HMAC', key, data);\n const hash = btoa(String.fromCharCode(...new Uint8Array(signature))).replace(\n /=+$/,\n '',\n );\n\n return value + '.' + hash;\n};\n\nexport const unsign: UnsignFunction = async (signed, secret) => {\n const index = signed.lastIndexOf('.');\n const value = signed.slice(0, index);\n const hash = signed.slice(index + 1);\n\n const key = await createKey(secret, ['verify']);\n const data = encoder.encode(value);\n const signature = byteStringToUint8Array(atob(hash));\n const valid = await crypto.subtle.verify('HMAC', key, signature, data);\n\n return valid ? value : false;\n};\n\nasync function createKey(\n secret: string,\n usages: CryptoKey['usages'],\n): Promise<CryptoKey> {\n const key = await crypto.subtle.importKey(\n 'raw',\n encoder.encode(secret),\n {name: 'HMAC', hash: 'SHA-256'},\n false,\n usages,\n );\n\n return key;\n}\n\nfunction byteStringToUint8Array(byteString: string): Uint8Array {\n const array = new Uint8Array(byteString.length);\n\n for (let i = 0; i < byteString.length; i++) {\n array[i] = byteString.charCodeAt(i);\n }\n\n return array;\n}\n","import {\n createCookieFactory,\n createCookieSessionStorageFactory,\n createMemorySessionStorageFactory,\n createSessionStorageFactory,\n} from '@remix-run/server-runtime';\n\nimport {sign, unsign} from './crypto';\n\nexport const createCookie = createCookieFactory({sign, unsign});\nexport const createCookieSessionStorage =\n createCookieSessionStorageFactory(createCookie);\nexport const createSessionStorage = createSessionStorageFactory(createCookie);\nexport const createMemorySessionStorage =\n createMemorySessionStorageFactory(createSessionStorage);\n","/// <reference types=\"@shopify/hydrogen\" />\nimport {\n createRequestHandler as createRemixRequestHandler,\n type AppLoadContext,\n type ServerBuild,\n} from '@remix-run/server-runtime';\nimport {createEventLogger} from './event-logger';\n\nconst originalErrorToString = Error.prototype.toString;\nError.prototype.toString = function () {\n return this.stack || originalErrorToString.call(this);\n};\n\n/** Server-Timing header key to signal that the SFAPI proxy is enabled */\nconst HYDROGEN_SFAPI_PROXY_KEY = '_sfapi_proxy';\n\nlet hasWarnedAboutStorefront = false;\n\nfunction warnOnce(message: string) {\n if (!hasWarnedAboutStorefront) {\n hasWarnedAboutStorefront = true;\n console.warn(message);\n }\n}\n\nfunction buildServerTimingHeader(values: Record<string, string | undefined>) {\n return Object.entries(values)\n .map(([key, value]) => (value ? `${key};desc=${value}` : undefined))\n .filter(Boolean)\n .join(', ');\n}\n\nfunction appendServerTimingHeader(\n response: {headers: Headers},\n values: string | Record<string, string | undefined>,\n) {\n const header =\n typeof values === 'string' ? values : buildServerTimingHeader(values);\n\n if (header) {\n response.headers.append('Server-Timing', header);\n }\n}\n\ntype CreateRequestHandlerOptions<Context = unknown> = {\n /** Remix's server build */\n build: ServerBuild;\n /** Remix's mode */\n mode?: string;\n /**\n * Function to provide the load context for each request.\n * It must contain Hydrogen's storefront client instance\n * for other Hydrogen utilities to work properly.\n */\n getLoadContext?: (request: Request) => Promise<Context> | Context;\n /**\n * Whether to include the `powered-by` header in responses\n * @default true\n */\n poweredByHeader?: boolean;\n /**\n * Collect tracking information from subrequests such as cookies\n * and forward them to the browser. Disable this if you are not\n * using Hydrogen's built-in analytics.\n * @default true\n */\n collectTrackingInformation?: boolean;\n /**\n * Whether to proxy standard routes such as `/api/.../graphql.json` (Storefront API).\n * You can disable this if you are handling these routes yourself. Ensure that\n * the proxy works if you rely on Hydrogen's built-in behaviors such as analytics.\n * @default true\n */\n proxyStandardRoutes?: boolean;\n};\n\nexport function createRequestHandler<Context = unknown>({\n build,\n mode,\n poweredByHeader = true,\n getLoadContext,\n collectTrackingInformation = true,\n proxyStandardRoutes = true,\n}: CreateRequestHandlerOptions<Context>) {\n const handleRequest = createRemixRequestHandler(build, mode);\n\n const appendPoweredByHeader = poweredByHeader\n ? (response: Response) =>\n response.headers.append('powered-by', 'Shopify, Hydrogen')\n : undefined;\n\n return async (request: Request) => {\n const method = request.method;\n\n if ((method === 'GET' || method === 'HEAD') && request.body) {\n return new Response(`${method} requests cannot have a body`, {\n status: 400,\n });\n }\n\n const url = new URL(request.url);\n\n if (url.pathname.includes('//')) {\n return new Response(null, {\n status: 301,\n headers: {\n location: url.pathname.replace(/\\/+/g, '/'),\n },\n });\n }\n\n const context = getLoadContext\n ? ((await getLoadContext(request)) as AppLoadContext)\n : undefined;\n\n // Access storefront from context if available\n const storefront = (\n context as {storefront?: StorefrontForProxy} | undefined\n )?.storefront;\n\n if (proxyStandardRoutes) {\n if (!storefront) {\n // TODO: this should throw error in future major version\n warnOnce(\n '[h2:createRequestHandler] Storefront instance is required to proxy standard routes.',\n );\n }\n\n // Proxy Storefront API requests\n if (storefront?.isStorefrontApiUrl(request)) {\n const response = await storefront.forward(request);\n appendPoweredByHeader?.(response);\n return response;\n }\n }\n\n if (process.env.NODE_ENV === 'development' && context) {\n // Store logger in globalThis so it can be accessed from the worker.\n // The global property must be different from the binding name,\n // otherwise Miniflare throws an error when accessing it.\n globalThis.__H2O_LOG_EVENT ??= createEventLogger(context);\n }\n\n const startTime = Date.now();\n\n const response = await handleRequest(request, context);\n\n if (storefront && proxyStandardRoutes) {\n if (collectTrackingInformation) {\n storefront.setCollectedSubrequestHeaders(response);\n }\n\n // TODO: assume SFAPI proxy is available in future major version\n // Signal that SFAPI proxy is enabled for document requests.\n // Note: sec-fetch-dest is automatically added by modern browsers,\n // but we also check the Accept header for other clients.\n const fetchDest = request.headers.get('sec-fetch-dest');\n if (\n (fetchDest && fetchDest === 'document') ||\n request.headers.get('accept')?.includes('text/html')\n ) {\n appendServerTimingHeader(response, {[HYDROGEN_SFAPI_PROXY_KEY]: '1'});\n }\n }\n\n appendPoweredByHeader?.(response);\n\n if (process.env.NODE_ENV === 'development') {\n globalThis.__H2O_LOG_EVENT?.({\n eventType: 'request',\n url: request.url,\n requestId: request.headers.get('request-id'),\n purpose: request.headers.get('purpose'),\n startTime,\n responseInit: {\n status: response.status,\n statusText: response.statusText,\n headers: Array.from(response.headers.entries()),\n } satisfies ResponseInit,\n });\n }\n\n return response;\n };\n}\n\ntype StorefrontHeaders = {\n requestGroupId: string | null;\n buyerIp: string | null;\n buyerIpSig: string | null;\n cookie: string | null;\n purpose: string | null;\n};\n\nexport function getStorefrontHeaders(request: Request): StorefrontHeaders {\n const headers = request.headers;\n return {\n requestGroupId: headers.get('request-id'),\n buyerIp: headers.get('oxygen-buyer-ip'),\n buyerIpSig: headers.get('X-Shopify-Client-IP-Sig'),\n cookie: headers.get('cookie'),\n // sec-purpose is added by browsers automatically when using link/prefetch or Speculation Rules\n purpose: headers.get('sec-purpose') || headers.get('purpose'),\n };\n}\n\n/**\n * Minimal storefront interface needed for proxy functionality.\n * The full Storefront type is defined in @shopify/hydrogen.\n */\ntype StorefrontForProxy = {\n isStorefrontApiUrl: (request: {url?: string}) => boolean;\n forward: (request: Request) => Promise<Response>;\n setCollectedSubrequestHeaders: (response: {headers: Headers}) => void;\n};\n"]}
|
|
@@ -7,15 +7,42 @@ declare const createCookieSessionStorage: _remix_run_server_runtime.CreateCookie
|
|
|
7
7
|
declare const createSessionStorage: _remix_run_server_runtime.CreateSessionStorageFunction;
|
|
8
8
|
declare const createMemorySessionStorage: _remix_run_server_runtime.CreateMemorySessionStorageFunction;
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
type CreateRequestHandlerOptions<Context = unknown> = {
|
|
11
|
+
/** Remix's server build */
|
|
11
12
|
build: ServerBuild;
|
|
13
|
+
/** Remix's mode */
|
|
12
14
|
mode?: string;
|
|
13
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Function to provide the load context for each request.
|
|
17
|
+
* It must contain Hydrogen's storefront client instance
|
|
18
|
+
* for other Hydrogen utilities to work properly.
|
|
19
|
+
*/
|
|
14
20
|
getLoadContext?: (request: Request) => Promise<Context> | Context;
|
|
15
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Whether to include the `powered-by` header in responses
|
|
23
|
+
* @default true
|
|
24
|
+
*/
|
|
25
|
+
poweredByHeader?: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Collect tracking information from subrequests such as cookies
|
|
28
|
+
* and forward them to the browser. Disable this if you are not
|
|
29
|
+
* using Hydrogen's built-in analytics.
|
|
30
|
+
* @default true
|
|
31
|
+
*/
|
|
32
|
+
collectTrackingInformation?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Whether to proxy standard routes such as `/api/.../graphql.json` (Storefront API).
|
|
35
|
+
* You can disable this if you are handling these routes yourself. Ensure that
|
|
36
|
+
* the proxy works if you rely on Hydrogen's built-in behaviors such as analytics.
|
|
37
|
+
* @default true
|
|
38
|
+
*/
|
|
39
|
+
proxyStandardRoutes?: boolean;
|
|
40
|
+
};
|
|
41
|
+
declare function createRequestHandler<Context = unknown>({ build, mode, poweredByHeader, getLoadContext, collectTrackingInformation, proxyStandardRoutes, }: CreateRequestHandlerOptions<Context>): (request: Request) => Promise<Response>;
|
|
16
42
|
type StorefrontHeaders = {
|
|
17
43
|
requestGroupId: string | null;
|
|
18
44
|
buyerIp: string | null;
|
|
45
|
+
buyerIpSig: string | null;
|
|
19
46
|
cookie: string | null;
|
|
20
47
|
purpose: string | null;
|
|
21
48
|
};
|
|
@@ -7,15 +7,42 @@ declare const createCookieSessionStorage: _remix_run_server_runtime.CreateCookie
|
|
|
7
7
|
declare const createSessionStorage: _remix_run_server_runtime.CreateSessionStorageFunction;
|
|
8
8
|
declare const createMemorySessionStorage: _remix_run_server_runtime.CreateMemorySessionStorageFunction;
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
type CreateRequestHandlerOptions<Context = unknown> = {
|
|
11
|
+
/** Remix's server build */
|
|
11
12
|
build: ServerBuild;
|
|
13
|
+
/** Remix's mode */
|
|
12
14
|
mode?: string;
|
|
13
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Function to provide the load context for each request.
|
|
17
|
+
* It must contain Hydrogen's storefront client instance
|
|
18
|
+
* for other Hydrogen utilities to work properly.
|
|
19
|
+
*/
|
|
14
20
|
getLoadContext?: (request: Request) => Promise<Context> | Context;
|
|
15
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Whether to include the `powered-by` header in responses
|
|
23
|
+
* @default true
|
|
24
|
+
*/
|
|
25
|
+
poweredByHeader?: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Collect tracking information from subrequests such as cookies
|
|
28
|
+
* and forward them to the browser. Disable this if you are not
|
|
29
|
+
* using Hydrogen's built-in analytics.
|
|
30
|
+
* @default true
|
|
31
|
+
*/
|
|
32
|
+
collectTrackingInformation?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Whether to proxy standard routes such as `/api/.../graphql.json` (Storefront API).
|
|
35
|
+
* You can disable this if you are handling these routes yourself. Ensure that
|
|
36
|
+
* the proxy works if you rely on Hydrogen's built-in behaviors such as analytics.
|
|
37
|
+
* @default true
|
|
38
|
+
*/
|
|
39
|
+
proxyStandardRoutes?: boolean;
|
|
40
|
+
};
|
|
41
|
+
declare function createRequestHandler<Context = unknown>({ build, mode, poweredByHeader, getLoadContext, collectTrackingInformation, proxyStandardRoutes, }: CreateRequestHandlerOptions<Context>): (request: Request) => Promise<Response>;
|
|
16
42
|
type StorefrontHeaders = {
|
|
17
43
|
requestGroupId: string | null;
|
|
18
44
|
buyerIp: string | null;
|
|
45
|
+
buyerIpSig: string | null;
|
|
19
46
|
cookie: string | null;
|
|
20
47
|
purpose: string | null;
|
|
21
48
|
};
|
package/dist/production/index.js
CHANGED
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export
|
|
3
|
-
|
|
4
|
-
var c=new TextEncoder,l=async(e,t)=>{let r=await y(t,["sign"]),n=c.encode(e),s=await crypto.subtle.sign("HMAC",r,n),o=btoa(String.fromCharCode(...new Uint8Array(s))).replace(/=+$/,"");return e+"."+o},g=async(e,t)=>{let r=e.lastIndexOf("."),n=e.slice(0,r),s=e.slice(r+1),o=await y(t,["verify"]),a=c.encode(n),i=m(atob(s));return await crypto.subtle.verify("HMAC",o,i,a)?n:!1};async function y(e,t){return await crypto.subtle.importKey("raw",c.encode(e),{name:"HMAC",hash:"SHA-256"},!1,t)}function m(e){let t=new Uint8Array(e.length);for(let r=0;r<e.length;r++)t[r]=e.charCodeAt(r);return t}var u=createCookieFactory({sign:l,unsign:g}),C=createCookieSessionStorageFactory(u),S=createSessionStorageFactory(u),k=createMemorySessionStorageFactory(S);var A=Error.prototype.toString;Error.prototype.toString=function(){return this.stack||A.call(this)};function E({build:e,mode:t,poweredByHeader:r=!0,getLoadContext:n}){let s=createRequestHandler(e,t);return async o=>{let a=o.method;if((a==="GET"||a==="HEAD")&&o.body)return new Response(`${a} requests cannot have a body`,{status:400});let i=new URL(o.url);if(i.pathname.includes("//"))return new Response(null,{status:301,headers:{location:i.pathname.replace(/\/+/g,"/")}});let p=n?await n(o):void 0,d=await s(o,p);return r&&d.headers.append("powered-by","Shopify, Hydrogen"),d}}function v(e){let t=e.headers;return {requestGroupId:t.get("request-id"),buyerIp:t.get("oxygen-buyer-ip"),cookie:t.get("cookie"),purpose:t.get("purpose")}}
|
|
5
|
-
|
|
6
|
-
export { u as createCookie, C as createCookieSessionStorage, k as createMemorySessionStorage, E as createRequestHandler, S as createSessionStorage, v as getStorefrontHeaders };
|
|
7
|
-
//# sourceMappingURL=out.js.map
|
|
1
|
+
import {createCookieFactory,createCookieSessionStorageFactory,createSessionStorageFactory,createMemorySessionStorageFactory,createRequestHandler}from'@remix-run/server-runtime';export{MaxPartSizeExceededError,createSession,data,defer,isCookie,isSession,json,redirect,redirectDocument}from'@remix-run/server-runtime';var g=new TextEncoder,m=async(t,e)=>{let r=await x(e,["sign"]),n=g.encode(t),i=await crypto.subtle.sign("HMAC",r,n),s=btoa(String.fromCharCode(...new Uint8Array(i))).replace(/=+$/,"");return t+"."+s},h=async(t,e)=>{let r=t.lastIndexOf("."),n=t.slice(0,r),i=t.slice(r+1),s=await x(e,["verify"]),p=g.encode(n),u=R(atob(i));return await crypto.subtle.verify("HMAC",s,u,p)?n:false};async function x(t,e){return await crypto.subtle.importKey("raw",g.encode(t),{name:"HMAC",hash:"SHA-256"},false,e)}function R(t){let e=new Uint8Array(t.length);for(let r=0;r<t.length;r++)e[r]=t.charCodeAt(r);return e}var f=createCookieFactory({sign:m,unsign:h}),b=createCookieSessionStorageFactory(f),H=createSessionStorageFactory(f),E=createMemorySessionStorageFactory(H);var v=Error.prototype.toString;Error.prototype.toString=function(){return this.stack||v.call(this)};var T="_sfapi_proxy",C=false;function D(t){C||(C=true,console.warn(t));}function O(t){return Object.entries(t).map(([e,r])=>r?`${e};desc=${r}`:void 0).filter(Boolean).join(", ")}function M(t,e){let r=typeof e=="string"?e:O(e);r&&t.headers.append("Server-Timing",r);}function _({build:t,mode:e,poweredByHeader:r=true,getLoadContext:n,collectTrackingInformation:i=true,proxyStandardRoutes:s=true}){let p=createRequestHandler(t,e),u=r?o=>o.headers.append("powered-by","Shopify, Hydrogen"):void 0;return async o=>{let l=o.method;if((l==="GET"||l==="HEAD")&&o.body)return new Response(`${l} requests cannot have a body`,{status:400});let y=new URL(o.url);if(y.pathname.includes("//"))return new Response(null,{status:301,headers:{location:y.pathname.replace(/\/+/g,"/")}});let S=n?await n(o):void 0,a=S?.storefront;if(s&&(a||D("[h2:createRequestHandler] Storefront instance is required to proxy standard routes."),a?.isStorefrontApiUrl(o))){let c=await a.forward(o);return u?.(c),c}let d=await p(o,S);if(a&&s){i&&a.setCollectedSubrequestHeaders(d);let c=o.headers.get("sec-fetch-dest");(c&&c==="document"||o.headers.get("accept")?.includes("text/html"))&&M(d,{[T]:"1"});}return u?.(d),d}}function I(t){let e=t.headers;return {requestGroupId:e.get("request-id"),buyerIp:e.get("oxygen-buyer-ip"),buyerIpSig:e.get("X-Shopify-Client-IP-Sig"),cookie:e.get("cookie"),purpose:e.get("sec-purpose")||e.get("purpose")}}
|
|
2
|
+
export{f as createCookie,b as createCookieSessionStorage,E as createMemorySessionStorage,_ as createRequestHandler,H as createSessionStorage,I as getStorefrontHeaders};//# sourceMappingURL=index.js.map
|
|
8
3
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/implementations.ts","../../src/crypto.ts","../../src/server.ts","../../src/index.ts"],"names":["createCookieFactory","createCookieSessionStorageFactory","createMemorySessionStorageFactory","createSessionStorageFactory","encoder","sign","value","secret","key","createKey","data","signature","hash","unsign","signed","index","byteStringToUint8Array","usages","byteString","array","i","createCookie","createCookieSessionStorage","createSessionStorage","createMemorySessionStorage","createRemixRequestHandler","originalErrorToString","createRequestHandler","build","mode","poweredByHeader","getLoadContext","handleRequest","request","method","url","context","startTime","response","getStorefrontHeaders","headers","createSession","defer","isCookie","isSession","json","MaxPartSizeExceededError","redirect","redirectDocument"],"mappings":"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","sourcesContent":["import {\n createCookieFactory,\n createCookieSessionStorageFactory,\n createMemorySessionStorageFactory,\n createSessionStorageFactory,\n} from '@remix-run/server-runtime';\n\nimport {sign, unsign} from './crypto';\n\nexport const createCookie = createCookieFactory({sign, unsign});\nexport const createCookieSessionStorage =\n createCookieSessionStorageFactory(createCookie);\nexport const createSessionStorage = createSessionStorageFactory(createCookie);\nexport const createMemorySessionStorage =\n createMemorySessionStorageFactory(createSessionStorage);\n","import type {SignFunction, UnsignFunction} from '@remix-run/server-runtime';\n\nconst encoder = new TextEncoder();\n\nexport const sign: SignFunction = async (value, secret) => {\n const key = await createKey(secret, ['sign']);\n const data = encoder.encode(value);\n const signature = await crypto.subtle.sign('HMAC', key, data);\n const hash = btoa(String.fromCharCode(...new Uint8Array(signature))).replace(\n /=+$/,\n '',\n );\n\n return value + '.' + hash;\n};\n\nexport const unsign: UnsignFunction = async (signed, secret) => {\n const index = signed.lastIndexOf('.');\n const value = signed.slice(0, index);\n const hash = signed.slice(index + 1);\n\n const key = await createKey(secret, ['verify']);\n const data = encoder.encode(value);\n const signature = byteStringToUint8Array(atob(hash));\n const valid = await crypto.subtle.verify('HMAC', key, signature, data);\n\n return valid ? value : false;\n};\n\nasync function createKey(\n secret: string,\n usages: CryptoKey['usages'],\n): Promise<CryptoKey> {\n const key = await crypto.subtle.importKey(\n 'raw',\n encoder.encode(secret),\n {name: 'HMAC', hash: 'SHA-256'},\n false,\n usages,\n );\n\n return key;\n}\n\nfunction byteStringToUint8Array(byteString: string): Uint8Array {\n const array = new Uint8Array(byteString.length);\n\n for (let i = 0; i < byteString.length; i++) {\n array[i] = byteString.charCodeAt(i);\n }\n\n return array;\n}\n","/// <reference types=\"@shopify/hydrogen\" />\nimport {\n createRequestHandler as createRemixRequestHandler,\n type AppLoadContext,\n type ServerBuild,\n} from '@remix-run/server-runtime';\nimport {createEventLogger} from './event-logger';\n\nconst originalErrorToString = Error.prototype.toString;\nError.prototype.toString = function () {\n return this.stack || originalErrorToString.call(this);\n};\n\nexport function createRequestHandler<Context = unknown>({\n build,\n mode,\n poweredByHeader = true,\n getLoadContext,\n}: {\n build: ServerBuild;\n mode?: string;\n poweredByHeader?: boolean;\n getLoadContext?: (request: Request) => Promise<Context> | Context;\n}) {\n const handleRequest = createRemixRequestHandler(build, mode);\n\n return async (request: Request) => {\n const method = request.method;\n\n if ((method === 'GET' || method === 'HEAD') && request.body) {\n return new Response(`${method} requests cannot have a body`, {\n status: 400,\n });\n }\n\n const url = new URL(request.url);\n\n if (url.pathname.includes('//')) {\n return new Response(null, {\n status: 301,\n headers: {\n location: url.pathname.replace(/\\/+/g, '/'),\n },\n });\n }\n\n const context = getLoadContext\n ? ((await getLoadContext(request)) as AppLoadContext)\n : undefined;\n\n if (process.env.NODE_ENV === 'development' && context) {\n // Store logger in globalThis so it can be accessed from the worker.\n // The global property must be different from the binding name,\n // otherwise Miniflare throws an error when accessing it.\n globalThis.__H2O_LOG_EVENT ??= createEventLogger(context);\n }\n\n const startTime = Date.now();\n\n const response = await handleRequest(request, context);\n\n if (poweredByHeader) {\n response.headers.append('powered-by', 'Shopify, Hydrogen');\n }\n\n if (process.env.NODE_ENV === 'development') {\n globalThis.__H2O_LOG_EVENT?.({\n eventType: 'request',\n url: request.url,\n requestId: request.headers.get('request-id'),\n purpose: request.headers.get('purpose'),\n startTime,\n responseInit: {\n status: response.status,\n statusText: response.statusText,\n headers: Array.from(response.headers.entries()),\n } satisfies ResponseInit,\n });\n }\n\n return response;\n };\n}\n\ntype StorefrontHeaders = {\n requestGroupId: string | null;\n buyerIp: string | null;\n cookie: string | null;\n purpose: string | null;\n};\n\nexport function getStorefrontHeaders(request: Request): StorefrontHeaders {\n const headers = request.headers;\n return {\n requestGroupId: headers.get('request-id'),\n buyerIp: headers.get('oxygen-buyer-ip'),\n cookie: headers.get('cookie'),\n purpose: headers.get('purpose'),\n };\n}\n","export {\n createCookie,\n createCookieSessionStorage,\n createMemorySessionStorage,\n createSessionStorage,\n} from './implementations';\nexport {createRequestHandler, getStorefrontHeaders} from './server';\nexport type {\n ActionFunction,\n ActionFunctionArgs,\n AppLoadContext,\n Cookie,\n CookieOptions,\n CookieParseOptions,\n CookieSerializeOptions,\n CookieSignatureOptions,\n DataFunctionArgs,\n EntryContext,\n ErrorResponse,\n HandleDataRequestFunction,\n HandleDocumentRequestFunction,\n HandleErrorFunction,\n HeadersArgs,\n HeadersFunction,\n HtmlLinkDescriptor,\n JsonFunction,\n LinkDescriptor,\n LinksFunction,\n LoaderFunction,\n LoaderFunctionArgs,\n MemoryUploadHandlerFilterArgs,\n MemoryUploadHandlerOptions,\n ServerRuntimeMetaArgs as MetaArgs,\n ServerRuntimeMetaDescriptor as MetaDescriptor,\n ServerRuntimeMetaFunction as MetaFunction,\n PageLinkDescriptor,\n RequestHandler,\n SerializeFrom,\n ServerBuild,\n ServerEntryModule,\n Session,\n SessionData,\n SessionIdStorageStrategy,\n SessionStorage,\n SignFunction,\n TypedDeferredData,\n TypedResponse,\n UnsignFunction,\n UploadHandler,\n UploadHandlerPart,\n} from '@remix-run/server-runtime';\nexport {\n createSession,\n data,\n defer,\n isCookie,\n isSession,\n json,\n MaxPartSizeExceededError,\n redirect,\n redirectDocument,\n} from '@remix-run/server-runtime';\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/crypto.ts","../../src/implementations.ts","../../src/server.ts"],"names":["encoder","sign","value","secret","key","createKey","data","signature","hash","unsign","signed","index","byteStringToUint8Array","usages","byteString","array","i","createCookie","createCookieFactory","createCookieSessionStorage","createCookieSessionStorageFactory","createSessionStorage","createSessionStorageFactory","createMemorySessionStorage","createMemorySessionStorageFactory","originalErrorToString","HYDROGEN_SFAPI_PROXY_KEY","hasWarnedAboutStorefront","warnOnce","message","buildServerTimingHeader","values","appendServerTimingHeader","response","header","createRequestHandler","build","mode","poweredByHeader","getLoadContext","collectTrackingInformation","proxyStandardRoutes","handleRequest","createRemixRequestHandler","appendPoweredByHeader","request","method","url","context","storefront","fetchDest","getStorefrontHeaders","headers"],"mappings":"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","file":"index.js","sourcesContent":["import type {SignFunction, UnsignFunction} from '@remix-run/server-runtime';\n\nconst encoder = new TextEncoder();\n\nexport const sign: SignFunction = async (value, secret) => {\n const key = await createKey(secret, ['sign']);\n const data = encoder.encode(value);\n const signature = await crypto.subtle.sign('HMAC', key, data);\n const hash = btoa(String.fromCharCode(...new Uint8Array(signature))).replace(\n /=+$/,\n '',\n );\n\n return value + '.' + hash;\n};\n\nexport const unsign: UnsignFunction = async (signed, secret) => {\n const index = signed.lastIndexOf('.');\n const value = signed.slice(0, index);\n const hash = signed.slice(index + 1);\n\n const key = await createKey(secret, ['verify']);\n const data = encoder.encode(value);\n const signature = byteStringToUint8Array(atob(hash));\n const valid = await crypto.subtle.verify('HMAC', key, signature, data);\n\n return valid ? value : false;\n};\n\nasync function createKey(\n secret: string,\n usages: CryptoKey['usages'],\n): Promise<CryptoKey> {\n const key = await crypto.subtle.importKey(\n 'raw',\n encoder.encode(secret),\n {name: 'HMAC', hash: 'SHA-256'},\n false,\n usages,\n );\n\n return key;\n}\n\nfunction byteStringToUint8Array(byteString: string): Uint8Array {\n const array = new Uint8Array(byteString.length);\n\n for (let i = 0; i < byteString.length; i++) {\n array[i] = byteString.charCodeAt(i);\n }\n\n return array;\n}\n","import {\n createCookieFactory,\n createCookieSessionStorageFactory,\n createMemorySessionStorageFactory,\n createSessionStorageFactory,\n} from '@remix-run/server-runtime';\n\nimport {sign, unsign} from './crypto';\n\nexport const createCookie = createCookieFactory({sign, unsign});\nexport const createCookieSessionStorage =\n createCookieSessionStorageFactory(createCookie);\nexport const createSessionStorage = createSessionStorageFactory(createCookie);\nexport const createMemorySessionStorage =\n createMemorySessionStorageFactory(createSessionStorage);\n","/// <reference types=\"@shopify/hydrogen\" />\nimport {\n createRequestHandler as createRemixRequestHandler,\n type AppLoadContext,\n type ServerBuild,\n} from '@remix-run/server-runtime';\nimport {createEventLogger} from './event-logger';\n\nconst originalErrorToString = Error.prototype.toString;\nError.prototype.toString = function () {\n return this.stack || originalErrorToString.call(this);\n};\n\n/** Server-Timing header key to signal that the SFAPI proxy is enabled */\nconst HYDROGEN_SFAPI_PROXY_KEY = '_sfapi_proxy';\n\nlet hasWarnedAboutStorefront = false;\n\nfunction warnOnce(message: string) {\n if (!hasWarnedAboutStorefront) {\n hasWarnedAboutStorefront = true;\n console.warn(message);\n }\n}\n\nfunction buildServerTimingHeader(values: Record<string, string | undefined>) {\n return Object.entries(values)\n .map(([key, value]) => (value ? `${key};desc=${value}` : undefined))\n .filter(Boolean)\n .join(', ');\n}\n\nfunction appendServerTimingHeader(\n response: {headers: Headers},\n values: string | Record<string, string | undefined>,\n) {\n const header =\n typeof values === 'string' ? values : buildServerTimingHeader(values);\n\n if (header) {\n response.headers.append('Server-Timing', header);\n }\n}\n\ntype CreateRequestHandlerOptions<Context = unknown> = {\n /** Remix's server build */\n build: ServerBuild;\n /** Remix's mode */\n mode?: string;\n /**\n * Function to provide the load context for each request.\n * It must contain Hydrogen's storefront client instance\n * for other Hydrogen utilities to work properly.\n */\n getLoadContext?: (request: Request) => Promise<Context> | Context;\n /**\n * Whether to include the `powered-by` header in responses\n * @default true\n */\n poweredByHeader?: boolean;\n /**\n * Collect tracking information from subrequests such as cookies\n * and forward them to the browser. Disable this if you are not\n * using Hydrogen's built-in analytics.\n * @default true\n */\n collectTrackingInformation?: boolean;\n /**\n * Whether to proxy standard routes such as `/api/.../graphql.json` (Storefront API).\n * You can disable this if you are handling these routes yourself. Ensure that\n * the proxy works if you rely on Hydrogen's built-in behaviors such as analytics.\n * @default true\n */\n proxyStandardRoutes?: boolean;\n};\n\nexport function createRequestHandler<Context = unknown>({\n build,\n mode,\n poweredByHeader = true,\n getLoadContext,\n collectTrackingInformation = true,\n proxyStandardRoutes = true,\n}: CreateRequestHandlerOptions<Context>) {\n const handleRequest = createRemixRequestHandler(build, mode);\n\n const appendPoweredByHeader = poweredByHeader\n ? (response: Response) =>\n response.headers.append('powered-by', 'Shopify, Hydrogen')\n : undefined;\n\n return async (request: Request) => {\n const method = request.method;\n\n if ((method === 'GET' || method === 'HEAD') && request.body) {\n return new Response(`${method} requests cannot have a body`, {\n status: 400,\n });\n }\n\n const url = new URL(request.url);\n\n if (url.pathname.includes('//')) {\n return new Response(null, {\n status: 301,\n headers: {\n location: url.pathname.replace(/\\/+/g, '/'),\n },\n });\n }\n\n const context = getLoadContext\n ? ((await getLoadContext(request)) as AppLoadContext)\n : undefined;\n\n // Access storefront from context if available\n const storefront = (\n context as {storefront?: StorefrontForProxy} | undefined\n )?.storefront;\n\n if (proxyStandardRoutes) {\n if (!storefront) {\n // TODO: this should throw error in future major version\n warnOnce(\n '[h2:createRequestHandler] Storefront instance is required to proxy standard routes.',\n );\n }\n\n // Proxy Storefront API requests\n if (storefront?.isStorefrontApiUrl(request)) {\n const response = await storefront.forward(request);\n appendPoweredByHeader?.(response);\n return response;\n }\n }\n\n if (process.env.NODE_ENV === 'development' && context) {\n // Store logger in globalThis so it can be accessed from the worker.\n // The global property must be different from the binding name,\n // otherwise Miniflare throws an error when accessing it.\n globalThis.__H2O_LOG_EVENT ??= createEventLogger(context);\n }\n\n const startTime = Date.now();\n\n const response = await handleRequest(request, context);\n\n if (storefront && proxyStandardRoutes) {\n if (collectTrackingInformation) {\n storefront.setCollectedSubrequestHeaders(response);\n }\n\n // TODO: assume SFAPI proxy is available in future major version\n // Signal that SFAPI proxy is enabled for document requests.\n // Note: sec-fetch-dest is automatically added by modern browsers,\n // but we also check the Accept header for other clients.\n const fetchDest = request.headers.get('sec-fetch-dest');\n if (\n (fetchDest && fetchDest === 'document') ||\n request.headers.get('accept')?.includes('text/html')\n ) {\n appendServerTimingHeader(response, {[HYDROGEN_SFAPI_PROXY_KEY]: '1'});\n }\n }\n\n appendPoweredByHeader?.(response);\n\n if (process.env.NODE_ENV === 'development') {\n globalThis.__H2O_LOG_EVENT?.({\n eventType: 'request',\n url: request.url,\n requestId: request.headers.get('request-id'),\n purpose: request.headers.get('purpose'),\n startTime,\n responseInit: {\n status: response.status,\n statusText: response.statusText,\n headers: Array.from(response.headers.entries()),\n } satisfies ResponseInit,\n });\n }\n\n return response;\n };\n}\n\ntype StorefrontHeaders = {\n requestGroupId: string | null;\n buyerIp: string | null;\n buyerIpSig: string | null;\n cookie: string | null;\n purpose: string | null;\n};\n\nexport function getStorefrontHeaders(request: Request): StorefrontHeaders {\n const headers = request.headers;\n return {\n requestGroupId: headers.get('request-id'),\n buyerIp: headers.get('oxygen-buyer-ip'),\n buyerIpSig: headers.get('X-Shopify-Client-IP-Sig'),\n cookie: headers.get('cookie'),\n // sec-purpose is added by browsers automatically when using link/prefetch or Speculation Rules\n purpose: headers.get('sec-purpose') || headers.get('purpose'),\n };\n}\n\n/**\n * Minimal storefront interface needed for proxy functionality.\n * The full Storefront type is defined in @shopify/hydrogen.\n */\ntype StorefrontForProxy = {\n isStorefrontApiUrl: (request: {url?: string}) => boolean;\n forward: (request: Request) => Promise<Response>;\n setCollectedSubrequestHeaders: (response: {headers: Headers}) => void;\n};\n"]}
|
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"@shopify:registry": "https://registry.npmjs.org"
|
|
6
6
|
},
|
|
7
7
|
"type": "module",
|
|
8
|
-
"version": "2.0
|
|
8
|
+
"version": "2.1.0",
|
|
9
9
|
"license": "MIT",
|
|
10
10
|
"main": "dist/index.cjs",
|
|
11
11
|
"module": "dist/production/index.js",
|
|
@@ -45,11 +45,11 @@
|
|
|
45
45
|
"dist"
|
|
46
46
|
],
|
|
47
47
|
"devDependencies": {
|
|
48
|
-
"@remix-run/server-runtime": "^2.
|
|
49
|
-
"@shopify/oxygen-workers-types": "^4.1.
|
|
48
|
+
"@remix-run/server-runtime": "^2.16.1",
|
|
49
|
+
"@shopify/oxygen-workers-types": "^4.1.6"
|
|
50
50
|
},
|
|
51
51
|
"peerDependencies": {
|
|
52
|
-
"@remix-run/server-runtime": "^2.1
|
|
52
|
+
"@remix-run/server-runtime": "^2.16.1",
|
|
53
53
|
"@shopify/oxygen-workers-types": "^3.17.3 || ^4.1.2"
|
|
54
54
|
}
|
|
55
55
|
}
|