@decocms/apps-wake 8.1.0-next.0 → 8.1.0-next.2
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/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/storefront.d.ts +14 -0
- package/dist/storefront.d.ts.map +1 -0
- package/dist/storefront.js +14 -0
- package/dist/storefront.js.map +1 -0
- package/dist/storefrontTypes.d.ts +7 -0
- package/dist/storefrontTypes.d.ts.map +1 -0
- package/dist/storefrontTypes.js +2 -0
- package/dist/storefrontTypes.js.map +1 -0
- package/dist/utils/client.d.ts +12 -0
- package/dist/utils/client.d.ts.map +1 -0
- package/dist/utils/client.js +2 -0
- package/dist/utils/client.js.map +1 -0
- package/dist/utils/gql.d.ts +16 -0
- package/dist/utils/gql.d.ts.map +1 -0
- package/dist/utils/gql.js +16 -0
- package/dist/utils/gql.js.map +1 -0
- package/dist/utils/graphql/queries.d.ts +117 -0
- package/dist/utils/graphql/queries.d.ts.map +1 -0
- package/dist/utils/graphql/queries.js +1323 -0
- package/dist/utils/graphql/queries.js.map +1 -0
- package/dist/utils/graphql/storefront.graphql.gen.d.ts +10344 -0
- package/dist/utils/graphql/storefront.graphql.gen.d.ts.map +1 -0
- package/dist/utils/graphql/storefront.graphql.gen.js +9 -0
- package/dist/utils/graphql/storefront.graphql.gen.js.map +1 -0
- package/dist/utils/graphqlOperationName.d.ts +36 -0
- package/dist/utils/graphqlOperationName.d.ts.map +1 -0
- package/dist/utils/graphqlOperationName.js +59 -0
- package/dist/utils/graphqlOperationName.js.map +1 -0
- package/dist/wakeClient.d.ts +54 -0
- package/dist/wakeClient.d.ts.map +1 -0
- package/dist/wakeClient.js +85 -0
- package/dist/wakeClient.js.map +1 -0
- package/package.json +26 -37
- package/src/index.ts +3 -63
- package/src/storefront.ts +1 -1
- package/src/storefrontTypes.ts +1 -1
- package/src/utils/gql.ts +1 -2
- package/src/utils/graphql/queries.ts +1 -1
- package/src/wakeClient.ts +3 -3
- package/src/__tests__/wakeClient.test.ts +0 -136
- package/src/actions/cart/addCoupon.ts +0 -43
- package/src/actions/cart/addItem.ts +0 -10
- package/src/actions/cart/addItems.ts +0 -50
- package/src/actions/cart/addKit.ts +0 -53
- package/src/actions/cart/partnerAssociate.ts +0 -48
- package/src/actions/cart/partnerDisassociate.ts +0 -43
- package/src/actions/cart/removeCoupon.ts +0 -39
- package/src/actions/cart/removeKit.ts +0 -56
- package/src/actions/cart/updateItemQuantity.ts +0 -69
- package/src/actions/newsletter/register.ts +0 -32
- package/src/actions/notifyme.ts +0 -28
- package/src/actions/review/create.ts +0 -30
- package/src/actions/shippingSimulation.ts +0 -60
- package/src/actions/submmitForm.ts +0 -25
- package/src/actions/wishlist/addProduct.ts +0 -43
- package/src/actions/wishlist/removeProduct.ts +0 -45
- package/src/client.ts +0 -123
- package/src/commerceLoaders.ts +0 -137
- package/src/handlers/sitemap.ts +0 -61
- package/src/loaders/cart.ts +0 -46
- package/src/loaders/partners.ts +0 -58
- package/src/loaders/productDetailsPage.ts +0 -174
- package/src/loaders/productList.ts +0 -262
- package/src/loaders/productListingPage.ts +0 -333
- package/src/loaders/proxy.ts +0 -79
- package/src/loaders/recommendations.ts +0 -84
- package/src/loaders/shop.ts +0 -37
- package/src/loaders/suggestion.ts +0 -67
- package/src/loaders/user.ts +0 -48
- package/src/loaders/wishlist.ts +0 -40
- package/src/manifest.gen.ts +0 -69
- package/src/mod.ts +0 -88
- package/src/registry.ts +0 -9
- package/src/utils/authError.ts +0 -107
- package/src/utils/authenticate.ts +0 -42
- package/src/utils/capitalize.ts +0 -9
- package/src/utils/cart.ts +0 -33
- package/src/utils/cookies.ts +0 -82
- package/src/utils/getVariations.ts +0 -47
- package/src/utils/graphql.ts +0 -72
- package/src/utils/httpError.ts +0 -15
- package/src/utils/instrumentedFetch.ts +0 -46
- package/src/utils/operationRouter.ts +0 -51
- package/src/utils/parseHeaders.ts +0 -19
- package/src/utils/partner.ts +0 -34
- package/src/utils/requestCtx.ts +0 -41
- package/src/utils/transform.ts +0 -460
- package/src/utils/types.ts +0 -6
- package/src/utils/user.ts +0 -9
- package/tsconfig.json +0 -7
package/src/utils/cookies.ts
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Node.js-compatible cookie helpers.
|
|
3
|
-
* Replaces Deno's std/http/cookie.ts.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
export function getCookies(headers: Headers): Record<string, string> {
|
|
7
|
-
const cookieHeader = headers.get("cookie") || "";
|
|
8
|
-
const cookies: Record<string, string> = {};
|
|
9
|
-
for (const pair of cookieHeader.split(";")) {
|
|
10
|
-
const [key, ...rest] = pair.trim().split("=");
|
|
11
|
-
if (key) {
|
|
12
|
-
cookies[key.trim()] = decodeURIComponent(rest.join("=").trim());
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
return cookies;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function setCookie(
|
|
19
|
-
headers: Headers,
|
|
20
|
-
options: {
|
|
21
|
-
name: string;
|
|
22
|
-
value: string;
|
|
23
|
-
path?: string;
|
|
24
|
-
expires?: Date;
|
|
25
|
-
maxAge?: number;
|
|
26
|
-
httpOnly?: boolean;
|
|
27
|
-
secure?: boolean;
|
|
28
|
-
sameSite?: "Strict" | "Lax" | "None";
|
|
29
|
-
},
|
|
30
|
-
) {
|
|
31
|
-
const parts = [`${options.name}=${encodeURIComponent(options.value)}`];
|
|
32
|
-
if (options.path) parts.push(`Path=${options.path}`);
|
|
33
|
-
if (options.expires) parts.push(`Expires=${options.expires.toUTCString()}`);
|
|
34
|
-
if (options.maxAge !== undefined) parts.push(`Max-Age=${options.maxAge}`);
|
|
35
|
-
if (options.httpOnly) parts.push("HttpOnly");
|
|
36
|
-
if (options.secure) parts.push("Secure");
|
|
37
|
-
if (options.sameSite) parts.push(`SameSite=${options.sameSite}`);
|
|
38
|
-
|
|
39
|
-
headers.append("Set-Cookie", parts.join("; "));
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export interface Cookie {
|
|
43
|
-
name: string;
|
|
44
|
-
value: string;
|
|
45
|
-
path?: string;
|
|
46
|
-
expires?: Date;
|
|
47
|
-
maxAge?: number;
|
|
48
|
-
httpOnly?: boolean;
|
|
49
|
-
secure?: boolean;
|
|
50
|
-
sameSite?: "Strict" | "Lax" | "None";
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export function getSetCookies(headers: Headers): Cookie[] {
|
|
54
|
-
const cookies: Cookie[] = [];
|
|
55
|
-
const setCookieHeaders = headers.getSetCookie?.() ?? [];
|
|
56
|
-
for (const header of setCookieHeaders) {
|
|
57
|
-
const parts = header.split(";").map((p) => p.trim());
|
|
58
|
-
const [nameValue, ...attrs] = parts;
|
|
59
|
-
const [name, ...rest] = nameValue.split("=");
|
|
60
|
-
const value = rest.join("=");
|
|
61
|
-
const cookie: Cookie = { name: name.trim(), value };
|
|
62
|
-
for (const attr of attrs) {
|
|
63
|
-
const [k, v] = attr.split("=");
|
|
64
|
-
const key = k.trim().toLowerCase();
|
|
65
|
-
if (key === "path") cookie.path = v?.trim();
|
|
66
|
-
if (key === "httponly") cookie.httpOnly = true;
|
|
67
|
-
if (key === "secure") cookie.secure = true;
|
|
68
|
-
if (key === "samesite") cookie.sameSite = v?.trim() as any;
|
|
69
|
-
if (key === "max-age") cookie.maxAge = Number(v?.trim());
|
|
70
|
-
}
|
|
71
|
-
cookies.push(cookie);
|
|
72
|
-
}
|
|
73
|
-
return cookies;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
export function debounce<T extends (...args: any[]) => void>(fn: T, delay: number): T {
|
|
77
|
-
let timer: ReturnType<typeof setTimeout>;
|
|
78
|
-
return ((...args: any[]) => {
|
|
79
|
-
clearTimeout(timer);
|
|
80
|
-
timer = setTimeout(() => fn(...args), delay);
|
|
81
|
-
}) as T;
|
|
82
|
-
}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { getWakeClient } from "../client";
|
|
2
|
-
import { GetProducts } from "./graphql/queries";
|
|
3
|
-
import type {
|
|
4
|
-
GetProductsQuery,
|
|
5
|
-
GetProductsQueryVariables,
|
|
6
|
-
ProductFragment,
|
|
7
|
-
} from "./graphql/storefront.graphql.gen";
|
|
8
|
-
import { toProduct } from "./transform";
|
|
9
|
-
|
|
10
|
-
export const MAXIMUM_REQUEST_QUANTITY = 50;
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Fetch every variant for the given product ids (paginating the storefront
|
|
14
|
-
* `products` query) and map them to canonical commerce `Product`s.
|
|
15
|
-
*/
|
|
16
|
-
export const getVariations = async (
|
|
17
|
-
productIds: number[],
|
|
18
|
-
base: URL,
|
|
19
|
-
extraHeaders?: Record<string, string>,
|
|
20
|
-
) => {
|
|
21
|
-
const storefront = getWakeClient();
|
|
22
|
-
const variations: ProductFragment[] = [];
|
|
23
|
-
|
|
24
|
-
const fetchData = async (cursor?: string) => {
|
|
25
|
-
const variables: GetProductsQueryVariables = {
|
|
26
|
-
first: MAXIMUM_REQUEST_QUANTITY,
|
|
27
|
-
filters: { productId: productIds },
|
|
28
|
-
sortDirection: "ASC",
|
|
29
|
-
sortKey: "RANDOM",
|
|
30
|
-
after: cursor,
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
const data = await storefront.query<GetProductsQuery>(GetProducts, variables, extraHeaders);
|
|
34
|
-
|
|
35
|
-
if (data.products?.nodes?.length) {
|
|
36
|
-
variations.push(...data.products.nodes.filter((v): v is ProductFragment => Boolean(v)));
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (data.products?.pageInfo.hasNextPage && data.products.pageInfo.endCursor) {
|
|
40
|
-
await fetchData(data.products.pageInfo.endCursor);
|
|
41
|
-
}
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
await fetchData();
|
|
45
|
-
|
|
46
|
-
return variations.map((i) => toProduct(i, { base }));
|
|
47
|
-
};
|
package/src/utils/graphql.ts
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import { type FetchFn, withFetchTimeout } from "@decocms/blocks/sdk/fetchTimeout";
|
|
2
|
-
import type { InstrumentedFetchInit } from "@decocms/blocks/sdk/instrumentedFetch";
|
|
3
|
-
import { buildQuery, type QueryDefinition } from "./gql";
|
|
4
|
-
import { extractGraphqlOperationName } from "./graphqlOperationName";
|
|
5
|
-
|
|
6
|
-
export { buildQuery, gql, type QueryDefinition } from "./gql";
|
|
7
|
-
|
|
8
|
-
export interface GraphQLClient {
|
|
9
|
-
/**
|
|
10
|
-
* Run a GraphQL operation. `extraHeaders` is merged over the client's
|
|
11
|
-
* base headers for the single call — Wake loaders use it to forward the
|
|
12
|
-
* client IP (`X-Forwarded-For`) via `parseHeaders(...)`.
|
|
13
|
-
*/
|
|
14
|
-
query<T>(
|
|
15
|
-
query: string | QueryDefinition,
|
|
16
|
-
variables?: Record<string, unknown>,
|
|
17
|
-
extraHeaders?: Record<string, string>,
|
|
18
|
-
): Promise<T>;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function createGraphqlClient(
|
|
22
|
-
endpoint: string,
|
|
23
|
-
headers: Record<string, string>,
|
|
24
|
-
fetchFn?: FetchFn,
|
|
25
|
-
): GraphQLClient {
|
|
26
|
-
const _fetch = fetchFn ?? withFetchTimeout();
|
|
27
|
-
return {
|
|
28
|
-
async query<T>(
|
|
29
|
-
queryOrDef: string | QueryDefinition,
|
|
30
|
-
variables?: Record<string, unknown>,
|
|
31
|
-
extraHeaders?: Record<string, string>,
|
|
32
|
-
): Promise<T> {
|
|
33
|
-
const query = typeof queryOrDef === "string" ? queryOrDef : buildQuery(queryOrDef);
|
|
34
|
-
|
|
35
|
-
// Stamp the GraphQL operation as init.operation so the framework's
|
|
36
|
-
// span name becomes `wake.<OperationName>` instead of the generic
|
|
37
|
-
// `wake.storefront.graphql` from the URL router. The extra field is
|
|
38
|
-
// silently dropped by plain `fetch` and read by any `InstrumentedFetch`
|
|
39
|
-
// configured via `setWakeFetch`.
|
|
40
|
-
const operation = extractGraphqlOperationName(query);
|
|
41
|
-
const init: InstrumentedFetchInit = {
|
|
42
|
-
method: "POST",
|
|
43
|
-
headers: {
|
|
44
|
-
// Per-call `extraHeaders` (e.g. forwarded client IP) come first so
|
|
45
|
-
// they can never override the app's auth token or Content-Type.
|
|
46
|
-
...extraHeaders,
|
|
47
|
-
"Content-Type": "application/json",
|
|
48
|
-
...headers,
|
|
49
|
-
},
|
|
50
|
-
body: JSON.stringify({ query, variables }),
|
|
51
|
-
...(operation ? { operation } : {}),
|
|
52
|
-
};
|
|
53
|
-
const response = await _fetch(endpoint, init);
|
|
54
|
-
|
|
55
|
-
if (!response.ok) {
|
|
56
|
-
throw new Error(`Wake GraphQL error: ${response.status} ${response.statusText}`);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const json = (await response.json()) as { data?: T; errors?: Array<{ message: string }> };
|
|
60
|
-
|
|
61
|
-
if (json.errors?.length) {
|
|
62
|
-
throw new Error(`Wake GraphQL errors: ${json.errors.map((e) => e.message).join(", ")}`);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
if (json.data === undefined) {
|
|
66
|
-
throw new Error("Wake GraphQL response missing data");
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return json.data;
|
|
70
|
-
},
|
|
71
|
-
};
|
|
72
|
-
}
|
package/src/utils/httpError.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Minimal HTTP error carrying a status code.
|
|
3
|
-
*
|
|
4
|
-
* The Deno Wake app threw `HttpError` from `deco/utils/http.ts`; in this
|
|
5
|
-
* architecture actions just throw `Error`, so this local class preserves the
|
|
6
|
-
* `.status` field that some callers/handlers inspect.
|
|
7
|
-
*/
|
|
8
|
-
export class HttpError extends Error {
|
|
9
|
-
status: number;
|
|
10
|
-
constructor(status: number, message?: string) {
|
|
11
|
-
super(message ?? `HTTP ${status}`);
|
|
12
|
-
this.name = "HttpError";
|
|
13
|
-
this.status = status;
|
|
14
|
-
}
|
|
15
|
-
}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pre-wired instrumented fetch factory for Wake. Mirrors
|
|
3
|
-
* `apps-shopify/utils/instrumentedFetch.ts`.
|
|
4
|
-
*
|
|
5
|
-
* Bundles `createInstrumentedFetch` (spans, traceparent, URL redaction), the
|
|
6
|
-
* `wakeOperationRouter` URL fallback, and an `onComplete` that records the
|
|
7
|
-
* canonical `http.client.request.duration` histogram with `provider: "wake"`.
|
|
8
|
-
*
|
|
9
|
-
* Sites do:
|
|
10
|
-
* ```ts
|
|
11
|
-
* import { setWakeFetch, createWakeFetch } from "@decocms/apps-wake";
|
|
12
|
-
* setWakeFetch(createWakeFetch());
|
|
13
|
-
* ```
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
import type { FetchFn } from "@decocms/blocks/sdk/fetchTimeout";
|
|
17
|
-
import {
|
|
18
|
-
createInstrumentedFetch,
|
|
19
|
-
type InstrumentedFetch,
|
|
20
|
-
} from "@decocms/blocks/sdk/instrumentedFetch";
|
|
21
|
-
import { recordCommerceMetric } from "@decocms/blocks/sdk/observability";
|
|
22
|
-
import { wakeOperationRouter } from "./operationRouter";
|
|
23
|
-
|
|
24
|
-
export interface CreateWakeFetchOptions {
|
|
25
|
-
baseFetch?: FetchFn;
|
|
26
|
-
disableHistogram?: boolean;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export function createWakeFetch(options: CreateWakeFetchOptions = {}): InstrumentedFetch {
|
|
30
|
-
const { baseFetch, disableHistogram = false } = options;
|
|
31
|
-
return createInstrumentedFetch({
|
|
32
|
-
name: "wake",
|
|
33
|
-
baseFetch,
|
|
34
|
-
resolveOperation: wakeOperationRouter,
|
|
35
|
-
onComplete: disableHistogram
|
|
36
|
-
? undefined
|
|
37
|
-
: ({ operation, status, durationMs, cached }) => {
|
|
38
|
-
recordCommerceMetric(durationMs, {
|
|
39
|
-
provider: "wake",
|
|
40
|
-
operation,
|
|
41
|
-
status_class: `${Math.floor(status / 100)}xx`,
|
|
42
|
-
cached,
|
|
43
|
-
});
|
|
44
|
-
},
|
|
45
|
-
});
|
|
46
|
-
}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* URL-derived operation name router for Wake API calls.
|
|
3
|
-
*
|
|
4
|
-
* Plugged into `@decocms/blocks`'s `createInstrumentedFetch` via the
|
|
5
|
-
* `resolveOperation(url, method)` option. Wake's API surface here is the
|
|
6
|
-
* GraphQL storefront (`/graphql`) plus a couple of checkout REST endpoints.
|
|
7
|
-
* The semantic GraphQL operation name is extracted from the document itself
|
|
8
|
-
* (see `./graphqlOperationName.ts`) and stamped as `init.operation`, which
|
|
9
|
-
* always wins over this router; this router is the fallback.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
type OperationResolver = string | ((match: RegExpMatchArray, method: string) => string);
|
|
13
|
-
|
|
14
|
-
interface Matcher {
|
|
15
|
-
pattern: RegExp;
|
|
16
|
-
operation: OperationResolver;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const m = (pattern: RegExp, operation: OperationResolver): Matcher => ({ pattern, operation });
|
|
20
|
-
|
|
21
|
-
const MATCHERS: ReadonlyArray<Matcher> = [
|
|
22
|
-
m(/^\/graphql/, "storefront.graphql"),
|
|
23
|
-
m(/^\/api\/Login\/Get/, "checkout.login"),
|
|
24
|
-
m(/^\/api\/carrinho/, "checkout.cart"),
|
|
25
|
-
m(/^\/api\//, "checkout.api"),
|
|
26
|
-
m(/^\/Sitemap\.xml/i, "sitemap"),
|
|
27
|
-
];
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Resolve an operation name for a Wake URL. Returns `undefined` if no matcher
|
|
31
|
-
* fires, which causes the framework to fall back to `wake.fetch`.
|
|
32
|
-
*/
|
|
33
|
-
export function wakeOperationRouter(url: string, method: string): string | undefined {
|
|
34
|
-
let pathname: string;
|
|
35
|
-
try {
|
|
36
|
-
pathname = new URL(url).pathname;
|
|
37
|
-
} catch {
|
|
38
|
-
const qs = url.indexOf("?");
|
|
39
|
-
const hash = url.indexOf("#");
|
|
40
|
-
const end = [qs, hash].filter((i) => i >= 0).sort((a, b) => a - b)[0];
|
|
41
|
-
pathname = end === undefined ? url : url.slice(0, end);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const upperMethod = method.toUpperCase();
|
|
45
|
-
for (const { pattern, operation } of MATCHERS) {
|
|
46
|
-
const match = pathname.match(pattern);
|
|
47
|
-
if (!match) continue;
|
|
48
|
-
return typeof operation === "function" ? operation(match, upperMethod) : operation;
|
|
49
|
-
}
|
|
50
|
-
return undefined;
|
|
51
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
export const getClientIP = (headers: Headers) => {
|
|
2
|
-
const cfConnectingIp = headers.get("cf-connecting-ip");
|
|
3
|
-
const xForwardedFor = headers.get("x-forwarded-for");
|
|
4
|
-
|
|
5
|
-
if (cfConnectingIp) return cfConnectingIp;
|
|
6
|
-
|
|
7
|
-
return xForwardedFor;
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Build a headers bag that forwards the originating client IP to Wake.
|
|
12
|
-
* Passed per-call into the storefront GraphQL client.
|
|
13
|
-
*/
|
|
14
|
-
export const parseHeaders = (headers: Headers): Record<string, string> => {
|
|
15
|
-
const clientIP = getClientIP(headers);
|
|
16
|
-
const out: Record<string, string> = {};
|
|
17
|
-
if (clientIP) out["X-Forwarded-For"] = clientIP;
|
|
18
|
-
return out;
|
|
19
|
-
};
|
package/src/utils/partner.ts
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { getCookies, setCookie } from "./cookies";
|
|
2
|
-
import { isSecureRequest } from "./requestCtx";
|
|
3
|
-
|
|
4
|
-
const PARTNER_COOKIE = "partner-token";
|
|
5
|
-
|
|
6
|
-
const TEN_DAYS_MS = 10 * 24 * 3600 * 1_000;
|
|
7
|
-
|
|
8
|
-
export const getPartnerCookie = (headers: Headers): string | undefined => {
|
|
9
|
-
const cookies = getCookies(headers);
|
|
10
|
-
|
|
11
|
-
return cookies[PARTNER_COOKIE];
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
export const setPartnerCookie = (headers: Headers, partnerToken: string) =>
|
|
15
|
-
setCookie(headers, {
|
|
16
|
-
name: PARTNER_COOKIE,
|
|
17
|
-
value: partnerToken,
|
|
18
|
-
path: "/",
|
|
19
|
-
expires: new Date(Date.now() + TEN_DAYS_MS),
|
|
20
|
-
// Server-only token — nothing client-side reads it.
|
|
21
|
-
httpOnly: true,
|
|
22
|
-
// Secure only on HTTPS — a Secure cookie is dropped over http:// (localhost dev).
|
|
23
|
-
secure: isSecureRequest(),
|
|
24
|
-
sameSite: "Lax",
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
export const deletePartnerCookie = (headers: Headers) =>
|
|
28
|
-
setCookie(headers, {
|
|
29
|
-
name: PARTNER_COOKIE,
|
|
30
|
-
value: "",
|
|
31
|
-
path: "/",
|
|
32
|
-
expires: new Date(0),
|
|
33
|
-
maxAge: 0,
|
|
34
|
-
});
|
package/src/utils/requestCtx.ts
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { RequestContext } from "@decocms/blocks/sdk/requestContext";
|
|
2
|
-
import { parseHeaders } from "./parseHeaders";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Request-scoped helpers bridging the Deno `(req, ctx)` convention to this
|
|
6
|
-
* architecture's `RequestContext` singleton. Loaders/actions read the active
|
|
7
|
-
* request's URL/cookies and write Set-Cookie via these.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
export const currentRequest = (): Request | null => RequestContext.current?.request ?? null;
|
|
11
|
-
|
|
12
|
-
export const currentUrl = (): URL => new URL(currentRequest()?.url ?? "https://localhost");
|
|
13
|
-
|
|
14
|
-
export const currentRequestHeaders = (): Headers => currentRequest()?.headers ?? new Headers();
|
|
15
|
-
|
|
16
|
-
export const currentResponseHeaders = (): Headers =>
|
|
17
|
-
RequestContext.current?.responseHeaders ?? new Headers();
|
|
18
|
-
|
|
19
|
-
/** Client-IP forwarding headers for the storefront GraphQL call. */
|
|
20
|
-
export const forwardedHeaders = (): Record<string, string> => {
|
|
21
|
-
const req = currentRequest();
|
|
22
|
-
return req ? parseHeaders(req.headers) : {};
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Whether the active request is HTTPS — drives the `Secure` cookie flag.
|
|
27
|
-
* A `Secure` cookie is rejected by browsers over plain HTTP (e.g. localhost
|
|
28
|
-
* dev), so cookies must only be marked Secure on real HTTPS requests. Defaults
|
|
29
|
-
* to `true` when the request is unknown (prod-safety).
|
|
30
|
-
*/
|
|
31
|
-
export const isSecureRequest = (): boolean => {
|
|
32
|
-
const req = currentRequest();
|
|
33
|
-
if (!req) return true;
|
|
34
|
-
try {
|
|
35
|
-
const proto = req.headers.get("x-forwarded-proto");
|
|
36
|
-
if (proto) return proto.split(",")[0].trim() === "https";
|
|
37
|
-
return new URL(req.url).protocol === "https:";
|
|
38
|
-
} catch {
|
|
39
|
-
return true;
|
|
40
|
-
}
|
|
41
|
-
};
|