-
Notifications
You must be signed in to change notification settings - Fork 21
Fix magento #1229
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Fix magento #1229
Changes from 250 commits
3bc38b1
d991a39
d4a4d64
ff0ee77
58ab6cd
22295f6
25f7eff
2f80287
3d25fb1
c14167d
06bac79
406ba74
bedb938
348f60e
66115eb
2119b8c
b7c2179
c1d38d7
1207c78
748d73e
6439061
5cbe0c4
e75e261
ff812ad
64b2ab8
a374746
51d1f89
16b1870
3469b06
be4c86a
30241f6
155e704
13d1c67
5d6c389
4bc67b9
aa1b847
200fdce
cc22bdf
d75c070
841e1eb
6313069
9b6f63c
363b8ce
62e5b7a
d16bffb
b31f254
3de937e
7cd3d61
0018ed4
c558826
90a522a
05b002a
55b020d
3bb4092
758534e
c1427da
062a7c2
d4afb7a
dedab2a
263d43f
b7a7f8a
641d5e0
dfa0126
4c72d75
aa036a3
15aac88
4a9fc49
2b8efe9
dda9e42
1f50791
b2b8aaa
c9d0886
fe087e6
334a084
547d0b4
c350a50
5979978
680a455
93516e3
a92e6cb
281be66
7327cfa
2e4a54f
23cf73e
5594e9d
4740de7
1862e78
fd76a0f
0c2485d
d1fd632
8b4af89
6859a65
a7d62af
29f66d9
1fe1935
68db6a9
6a57333
36fa306
d77f46f
f5761aa
4f76c23
d4b9fb8
33d2550
c9921b7
a627bc7
8e17c01
a873b2c
54c077a
63292b8
08b4521
207e295
29233a6
77d305e
821ef5d
a5eeed8
1bb38d2
6336f7b
c47574d
c71f5d0
f40988e
52f2004
5a3f049
84bbfa2
ec71a40
53c8444
e178bc0
6ac6d73
5234070
982760e
d2c50be
804e2a5
c6dafb7
d7f0724
ec77d18
d20f382
d672044
9e8d039
00f58c2
1c5ad9e
09d09ea
ddbea98
ecad329
4653ef3
3ee7f21
9028aa2
86fddab
d5489a1
c2c8c0b
6454ce7
ebbe958
eeadf2e
0bfa281
a507126
e802844
9901f65
20e7cba
92bcb5d
001a81b
286f1d4
b506ac1
306b573
bae3a3d
811c72d
c0073d3
a100a28
1e5c5f0
95bc842
fcad203
92f36cc
08212db
8f13eb4
0ab00f5
ab3be7e
916ad95
2dfd29f
4483789
00cfd81
cbb08c8
81eca84
e0b70d9
9002535
c749eda
d810f8c
9ed5b5c
87ca199
bf22b63
79c0c46
26c8958
61547b1
92c5757
851be32
f24844d
7a1dbc9
95fc62f
e9e1ad3
7efc594
6aa264d
63bd5ae
58f886d
d4a4d09
4e5cae5
bf38c84
0d52e33
0e86a21
2eab4d3
577aa36
72c4237
c221f56
5b4c881
6b00b80
8adeb89
c7090fe
ffe62e6
57c4e1c
afd6afc
a0e93d9
df1d8b5
ae2aa3c
050e865
0c858c3
a19a629
2a524c8
cd2ab8f
ed7a706
aee931c
8f4d14a
a1479a3
ce3d492
85d965d
2e0c9c4
5179900
91250e4
af886e4
1c9bb7c
8f677fc
0522ce2
ca00e8d
a5b98a0
c7c5677
21a0599
dce76fe
7a6cff6
7647606
fe156be
d7ce899
10da010
dd97fc1
0bf931a
852da95
acab327
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,9 @@ | ||
| // deno-lint-ignore-file no-explicit-any | ||
| import { DecoRequestInit } from "./fetch.ts"; | ||
| import { createHttpClient, HttpClientOptions } from "./http.ts"; | ||
|
|
||
| const decoCacheArgLength = 55; | ||
|
|
||
| interface GraphqlClientOptions extends Omit<HttpClientOptions, "base"> { | ||
| endpoint: string; | ||
| } | ||
|
|
@@ -10,24 +13,33 @@ interface GraphQLResponse<D> { | |
| errors: unknown[]; | ||
| } | ||
|
|
||
| type GraphQLAPI<D = unknown> = Record<string, { | ||
| response: GraphQLResponse<D>; | ||
| body: { | ||
| query: string; | ||
| variables?: Record<string, unknown>; | ||
| operationName?: string; | ||
| }; | ||
| }>; | ||
| type GraphQLAPI<D = unknown> = Record< | ||
| string, | ||
| { | ||
| response: GraphQLResponse<D>; | ||
| body?: { | ||
| query: string; | ||
| variables?: Record<string, unknown>; | ||
| operationName?: string; | ||
| }; | ||
| } | ||
| >; | ||
|
|
||
| interface GraphQLQueryProps<V> { | ||
| query: string; | ||
| fragments?: string[]; | ||
| variables?: V; | ||
| operationName?: string; | ||
| } | ||
|
|
||
| export const gql = (query: TemplateStringsArray, ...fragments: string[]) => | ||
| query.reduce((a, c, i) => `${a}${fragments[i - 1]}${c}`); | ||
|
|
||
| export const createGraphqlClient = ( | ||
| { endpoint, ...rest }: GraphqlClientOptions, | ||
| ) => { | ||
| export const createGraphqlClient = ({ | ||
| endpoint, | ||
| ...rest | ||
| }: GraphqlClientOptions) => { | ||
| const url = new URL(endpoint); | ||
| const key = `POST ${url.pathname}`; | ||
|
|
||
| const defaultHeaders = new Headers(rest.headers); | ||
| defaultHeaders.set("content-type", "application/json"); | ||
| defaultHeaders.set("accept", "application/json"); | ||
|
|
@@ -40,21 +52,24 @@ export const createGraphqlClient = ( | |
|
|
||
| return { | ||
| query: async <D, V>( | ||
| { query = "", fragments = [], variables, operationName }: { | ||
| query: string; | ||
| fragments?: string[]; | ||
| variables?: V; | ||
| operationName?: string; | ||
| }, | ||
| init?: RequestInit, | ||
| { | ||
| query = "", | ||
| fragments = [], | ||
| variables, | ||
| operationName, | ||
| }: GraphQLQueryProps<V>, | ||
| init?: DecoRequestInit, | ||
| ): Promise<D> => { | ||
| const { data, errors } = await http[key as any]({}, { | ||
| ...init, | ||
| body: { | ||
| query: [query, ...fragments].join("\n"), | ||
| variables: variables as any, | ||
| operationName, | ||
| }, | ||
| const { key, props } = getMethodAndProps<V>({ | ||
| query, | ||
| fragments, | ||
| variables, | ||
| operationName, | ||
| url, | ||
| }); | ||
| const { searchParams, body } = getParamsAndBody({ key, props, init }); | ||
| const { data, errors } = await http[key as any](searchParams, { | ||
| ...body, | ||
| }).then((res) => res.json()); | ||
|
|
||
| if (Array.isArray(errors) && errors.length > 0) { | ||
|
|
@@ -65,3 +80,100 @@ export const createGraphqlClient = ( | |
| }, | ||
| }; | ||
| }; | ||
|
|
||
| const getMethodAndProps = <V>({ | ||
| query, | ||
| fragments, | ||
| url, | ||
| operationName, | ||
| variables, | ||
| }: GraphQLQueryProps<V> & { url: URL }) => { | ||
| const fullQuery = joinQueryArgs({ | ||
| query, | ||
| fragments, | ||
| }); | ||
| const stringfiedVariables = stringfyVariables<V>({ variables }); | ||
| const minifiedQuery = minifyString(fullQuery); | ||
| const postMethodBool = isPostMethodRequired( | ||
| url.href, | ||
| minifiedQuery, | ||
| stringfiedVariables, | ||
| operationName, | ||
| ); | ||
|
|
||
| const { key, executableQuery, executableVariables } = postMethodBool | ||
| ? { | ||
| key: `POST ${url.pathname}`, | ||
| executableQuery: fullQuery, | ||
| executableVariables: variables, | ||
| } | ||
| : { | ||
| key: `GET ${url.pathname}`, | ||
| executableQuery: minifiedQuery, | ||
| executableVariables: stringfiedVariables, | ||
| }; | ||
|
|
||
| return { | ||
| key, | ||
| props: { | ||
| query: executableQuery, | ||
| variables: executableVariables, | ||
| operationName, | ||
| }, | ||
| }; | ||
| }; | ||
|
|
||
| const getParamsAndBody = ({ | ||
| key, | ||
| props, | ||
| init, | ||
| }: ReturnType<typeof getMethodAndProps> & { init?: DecoRequestInit }) => { | ||
| if (key.startsWith("POST")) { | ||
| return { searchParams: {}, body: { body: props, ...init } }; | ||
| } | ||
| return { searchParams: { ...props }, body: { ...init } }; | ||
| }; | ||
|
Comment on lines
+126
to
+135
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion | 🟠 Major Harden GET/POST param packing and avoid
Apply: -const getParamsAndBody = ({
+const getParamsAndBody = ({
key,
props,
init,
}: ReturnType<typeof getMethodAndProps> & { init?: DecoRequestInit }) => {
if (key.startsWith("POST")) {
- return { searchParams: {}, body: { body: props, ...init } };
+ const bodyJson = JSON.stringify(props);
+ return { searchParams: {}, init: { ...(init ?? {}), body: bodyJson } };
}
- return { searchParams: { ...props }, body: { ...init } };
+ const searchParams: Record<string, string> = {};
+ if (props.query) searchParams.query = props.query;
+ if (props.operationName) searchParams.operationName = props.operationName;
+ if (typeof props.variables === "string" && props.variables.length > 0) {
+ searchParams.variables = props.variables;
+ }
+ return { searchParams, init: { ...(init ?? {}) } };
};
🤖 Prompt for AI Agents |
||
|
|
||
| const minifyString = (s: string): string => { | ||
| s = s.replace( | ||
| /\{([^{}]*)\}/g, | ||
| (_, p1) => `{${p1.replace(/\s+/g, " ").trim()}}`, | ||
| ); | ||
| s = s | ||
| .replace(/[\r\n]+/g, " ") | ||
| .replace(/\s+/g, " ") | ||
| .replace(/\s*{\s*/g, "{") | ||
| .replace(/\s*}\s*/g, "}") | ||
| .replace(/\s*:\s*/g, ":") | ||
| .replace(/\s*\(\s*/g, "(") | ||
| .replace(/\s*\)\s*/g, ")"); | ||
| return s.trim(); | ||
| }; | ||
|
|
||
| const joinQueryArgs = ({ | ||
| query, | ||
| fragments = [], | ||
| }: Pick<GraphQLQueryProps<any>, "query" | "fragments">) => | ||
| [query, ...fragments].join("\n"); | ||
|
|
||
| const stringfyVariables = <V>({ | ||
| variables, | ||
| }: Pick<GraphQLQueryProps<V>, "variables">) => JSON.stringify(variables); | ||
|
|
||
| const isPostMethodRequired = ( | ||
| href: string, | ||
| query: string, | ||
| variables: string, | ||
| operationName?: string, | ||
| ): boolean => { | ||
| if (query.startsWith("mutation")) { | ||
| return true; | ||
| } | ||
|
|
||
| const urlLength = `${href}?query=${encodeURI(query)}&variables=${ | ||
| encodeURI( | ||
| variables, | ||
| ) | ||
| }&operationname=${operationName}`; | ||
| return urlLength.length + decoCacheArgLength > 2000; | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| import type { FunctionContext, LoaderFunction } from "@deco/deco"; | ||
| import { RequestURLParam } from "./requestToParam.ts"; | ||
|
|
||
| export interface Props { | ||
| /** | ||
| * @description Path name to remove from the URL - Stringfied regex. Example: "/product/" transform "/product/my-sku" in "my-sku". "/product/|/store" transform "/product/my-sku/store" in "my-sku" | ||
| * @format dynamic-options | ||
| */ | ||
| pathname?: string; | ||
| } | ||
|
Comment on lines
+4
to
+10
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Doc nit: typo and clarify examples. “Stringfied” → “Stringified”. Consider clarifying anchoring (prefix-only vs anywhere) in examples to avoid accidental middle replacements. 🤖 Prompt for AI Agents |
||
|
|
||
| /** | ||
| * @title Get params from request pathname. | ||
| * @description Set a pathname to remove from url and extract a slug | ||
| */ | ||
| const requestToParam: LoaderFunction< | ||
| Props, | ||
| RequestURLParam, | ||
| FunctionContext | ||
| > = (req, ctx) => { | ||
| const url = new URL(req.url); | ||
| const regex = new RegExp("(" + ctx.state.$live.pathname + ")", "g"); | ||
| return { | ||
| data: url.pathname.toString().replace(regex, ""), | ||
| }; | ||
| }; | ||
|
Comment on lines
+16
to
+26
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion | 🟠 Major Props ignored and regex can over‑match; anchor and escape.
Use Apply: -const requestToParam: LoaderFunction<
- Props,
- RequestURLParam,
- FunctionContext
-> = (req, ctx) => {
- const url = new URL(req.url);
- const regex = new RegExp("(" + ctx.state.$live.pathname + ")", "g");
- return {
- data: url.pathname.toString().replace(regex, ""),
- };
-};
+const requestToParam: LoaderFunction<
+ Props,
+ RequestURLParam,
+ FunctionContext
+> = (req, ctx, props) => {
+ const url = new URL(req.url);
+ const pattern = (props?.pathname ?? ctx.state.$live.pathname ?? "").trim();
+ if (!pattern) {
+ // strip leading slash only
+ return { data: url.pathname.replace(/^\/+/, "") };
+ }
+ // Anchor to start. If you intend a true regex, keep as-is; otherwise escape:
+ // const safe = pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
+ const regex = new RegExp(`^(?:${pattern})`);
+ const slug = url.pathname.replace(regex, "").replace(/^\/+/, "");
+ return { data: slug };
+};🤖 Prompt for AI Agents |
||
|
|
||
| export default requestToParam; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -26,6 +26,12 @@ const rankRoute = (pattern: string): number => | |
| pattern | ||
| .split("/") | ||
| .reduce((acc, routePart) => { | ||
| if (routePart.startsWith("}?")) { | ||
| if (routePart.includes("*")) { | ||
| return acc - 2; | ||
| } | ||
| return acc - 1; | ||
| } | ||
|
Comment on lines
+29
to
+34
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chainValidate rank logic for optional/wildcard segments starting with Ensure your route syntax can actually produce segments that begin with 🏁 Script executed: #!/bin/bash
# List route templates that contain '}' at segment start
rg -nP '(^|/)}\?' --type=ts -C2 website | sed -n '1,200p'Length of output: 0 I need to better understand the route template syntax and how segments are constructed. Let me examine the router.ts file and search for test cases or route examples. 🏁 Script executed: #!/bin/bash
# Read the router.ts file to understand context
cat website/handlers/router.tsLength of output: 6575 Let me search for test files and route definition examples to understand what route patterns actually exist: 🏁 Script executed: #!/bin/bash
# Search for test files that might contain route examples
fd -e test.ts -e test.tsx -e _test.ts -o -name '*test*.ts' website | head -20Length of output: 230 🏁 Script executed: #!/bin/bash
# Look for route definitions and examples
rg -n "pathTemplate" --type=ts website | head -30Length of output: 2415 🏁 Script executed: #!/bin/bash
# Search for what patterns actually contain } character
rg -nP '\}' --type=ts website/handlers/router.ts -C3Length of output: 6499 🏁 Script executed: #!/bin/bash
# Look at flags/audience.ts to understand route structure
cat website/flags/audience.ts 2>/dev/null || echo "File not found"Length of output: 1879 Let me search more broadly for any route patterns that might explain the 🏁 Script executed: #!/bin/bash
# Search for actual values assigned to pathTemplate
rg -nP 'pathTemplate\s*[=:]' --type=ts -A1 website | head -50Length of output: 2659 🏁 Script executed: #!/bin/bash
# Look for any patterns with { or } characters that might be encoded or escaped
rg -nP '[{}<>]' --type=ts website/loaders/options/routes.ts -C2Length of output: 814 🏁 Script executed: #!/bin/bash
# Search for imports or usage of URLPattern to understand the syntax
rg -n "URLPattern\|urlPatternCache" --type=ts website -C2 | head -60Length of output: 0 🏁 Script executed: #!/bin/bash
# Check if there are any comments about route syntax or rankRoute logic
rg -n "rankRoute\|route.*syntax\|pattern.*format" --type=ts website -C3Length of output: 0 Let me search for any test files related to routing to confirm there's no test coverage: 🏁 Script executed: #!/bin/bash
# Search for any test or spec files related to routing
find website -type f \( -name "*.test.ts" -o -name "*.spec.ts" -o -name "*_test.ts" \) | head -20Length of output: 0 🏁 Script executed: #!/bin/bash
# Search for rankRoute function usage and calls
rg -n "rankRoute" --type=ts websiteLength of output: 199 🏁 Script executed: #!/bin/bash
# Check if URLPattern is used and what patterns are created
grep -n "URLPattern\|new URLPattern" website/handlers/router.tsLength of output: 200 Remove dead code from rankRoute or add tests demonstrating when The 🤖 Prompt for AI Agents |
||
| if (routePart === "*") { | ||
| return acc; | ||
| } | ||
|
|
@@ -79,17 +85,20 @@ export const router = ( | |
| } | ||
| for (const { pathTemplate: routePath, handler } of routes) { | ||
| const pattern = urlPatternCache[routePath] ??= (() => { | ||
| let url; | ||
| if (URL.canParse(routePath)) { | ||
| url = new URL(routePath); | ||
| } else { | ||
| url = new URL(routePath, "http://localhost:8000"); | ||
| return new URLPattern(routePath); | ||
| } | ||
| const patternWithDefaultOrigin = new URLPattern( | ||
| routePath, | ||
| "http://localhost:8000", | ||
| ); | ||
|
|
||
| return new URLPattern({ | ||
| pathname: url.pathname, | ||
| ...(url.search ? { search: url.search } : {}), | ||
| pathname: patternWithDefaultOrigin.pathname, | ||
| search: patternWithDefaultOrigin.search || "*", | ||
| }); | ||
| })(); | ||
|
|
||
| const res = pattern.exec(req.url); | ||
| const groups = res?.pathname.groups ?? {}; | ||
| if (res !== null) { | ||
|
|
@@ -151,6 +160,7 @@ const prepareRoutes = (audiences: Routes[], ctx: AppContext) => { | |
| ((highPriorityA ? HIGH_PRIORITY_ROUTE_RANK_BASE_VALUE : 0) + | ||
| rankRoute(routeStringA)) | ||
| ); | ||
|
|
||
| return { | ||
| routes: builtRoutes.map((route) => ({ | ||
| pathTemplate: route[0], | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
POST body may not be JSON‑stringified;
init.bodycan override payload.getParamsAndBodyreturns{ body: props, ...init }for POST; ifcreateHttpClientdoesn’t stringify, requests may send[object Object]or fail....initafterbody: propscan replace the computed body.Ensure JSON string body and keep
propsas the final body.Apply:
And update helper (see next comment).