Skip to content

fix: RequestEventAction has missed a type resolveValue #7378

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

Open
wants to merge 26 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
c0cd5fa
fix: RequestEventAction has missed a type `resolveValue`
Feb 25, 2025
8bbf36d
update ap
Feb 25, 2025
cf5b192
Create cold-chairs-juggle.md
JerryWu1234 Feb 25, 2025
8fe9b2a
Merge branch 'main' into 7377_fix_ts_error
JerryWu1234 Mar 1, 2025
8c7e7ca
Merge branch 'main' into 7377_fix_ts_error
JerryWu1234 Mar 2, 2025
1839dff
add test, add resolve value
Mar 11, 2025
ffe6146
fix loader
Mar 11, 2025
cb99ba0
Merge branch 'main' into 7377_fix_ts_error
JerryWu1234 Mar 11, 2025
41e7ba1
Merge branch 'main' into 7377_fix_ts_error
JerryWu1234 Mar 12, 2025
234ed37
test
Mar 12, 2025
3e51a2d
Merge branch '7377_fix_ts_error' of https://github.com/JerryWu1234/qw…
Mar 12, 2025
c9a35c8
reset
Mar 12, 2025
c174cf5
test
Mar 12, 2025
ee5f60c
test
Mar 13, 2025
f68a58a
fix error
Mar 13, 2025
4c187e4
Merge branch 'main' into 7377_fix_ts_error
JerryWu1234 Mar 13, 2025
c52798e
Delete .changeset/cold-chairs-juggle.md
JerryWu1234 Apr 6, 2025
0397c56
fix change
JerryWu1234 Apr 6, 2025
dda7bf3
fix change
JerryWu1234 Apr 6, 2025
b1cb828
Update eight-garlics-deliver.md
JerryWu1234 Apr 18, 2025
7c68e6c
Merge branch 'main' into 7377_fix_ts_error
JerryWu1234 Apr 22, 2025
9ab0484
merge
JerryWu1234 Apr 22, 2025
c484ca5
api update
JerryWu1234 Apr 22, 2025
b932995
Merge branch 'main' into 7377_fix_ts_error
gioboa May 2, 2025
e0991b8
fix(qwik-city): add type for `routeAction$`'s `requestEvent.resolveVa…
JerryWu1234 May 7, 2025
88f268c
docs(changeset): remove '@builder.io/qwik' from patch list
JerryWu1234 May 9, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/eight-garlics-deliver.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@builder.io/qwik-city': patch
---

FIX: add type for `routeAction$`'s `requestEvent.resolveValue`
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@
}
],
"kind": "Interface",
"content": "```typescript\nexport interface RequestEventAction<PLATFORM = QwikCityPlatform> extends RequestEventCommon<PLATFORM> \n```\n**Extends:** [RequestEventCommon](#requesteventcommon)<!-- -->&lt;PLATFORM&gt;\n\n\n<table><thead><tr><th>\n\nProperty\n\n\n</th><th>\n\nModifiers\n\n\n</th><th>\n\nType\n\n\n</th><th>\n\nDescription\n\n\n</th></tr></thead>\n<tbody><tr><td>\n\n[fail](#)\n\n\n</td><td>\n\n\n</td><td>\n\n&lt;T extends Record&lt;string, any&gt;&gt;(status: number, returnData: T) =&gt; FailReturn&lt;T&gt;\n\n\n</td><td>\n\n\n</td></tr>\n</tbody></table>",
"content": "```typescript\nexport interface RequestEventAction<PLATFORM = QwikCityPlatform> extends RequestEventCommon<PLATFORM> \n```\n**Extends:** [RequestEventCommon](#requesteventcommon)<!-- -->&lt;PLATFORM&gt;\n\n\n<table><thead><tr><th>\n\nProperty\n\n\n</th><th>\n\nModifiers\n\n\n</th><th>\n\nType\n\n\n</th><th>\n\nDescription\n\n\n</th></tr></thead>\n<tbody><tr><td>\n\n[fail](#)\n\n\n</td><td>\n\n\n</td><td>\n\n&lt;T extends Record&lt;string, any&gt;&gt;(status: number, returnData: T) =&gt; FailReturn&lt;T&gt;\n\n\n</td><td>\n\n\n</td></tr>\n<tr><td>\n\n[resolveValue](#)\n\n\n</td><td>\n\n\n</td><td>\n\n[ResolveValue](#resolvevalue)\n\n\n</td><td>\n\n\n</td></tr>\n</tbody></table>",
"editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik-city/src/middleware/request-handler/types.ts",
"mdFile": "qwik-city.requesteventaction.md"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -863,6 +863,19 @@ Description

</td><td>

</td></tr>
<tr><td>

[resolveValue](#)

</td><td>

</td><td>

[ResolveValue](#resolvevalue)

</td><td>

</td></tr>
</tbody></table>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ export interface RequestEvent<PLATFORM = QwikCityPlatform> extends RequestEventC
export interface RequestEventAction<PLATFORM = QwikCityPlatform> extends RequestEventCommon<PLATFORM> {
// (undocumented)
fail: <T extends Record<string, any>>(status: number, returnData: T) => FailReturn<T>;
// (undocumented)
resolveValue: ResolveValue;
}

// @public (undocumented)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,6 @@ export function createRequestEvent(
routeModuleIndex = ABORT_INDEX;
return new AbortMessage();
};

const loaders: Record<string, Promise<any>> = {};
const requestEv: RequestEventInternal = {
[RequestEvLoaders]: loaders,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ export function actionsMiddleware(routeActions: ActionInternal[], routeLoaders:
requestEv.exit();
return;
}
let isAction = false;
const { method } = requestEv;
const loaders = getRequestLoaders(requestEv);
const isDev = getRequestMode(requestEv) === 'dev';
Expand All @@ -184,6 +185,48 @@ export function actionsMiddleware(routeActions: ActionInternal[], routeLoaders:
);
}
}

if (routeLoaders.length > 0) {
await Promise.all(routeLoaders.map(loader));
}
function loader(loader: LoaderInternal) {
const loaderId = loader.__id;
loaders[loaderId] = runValidators(
requestEv,
loader.__validators,
undefined, // data
isDev
)
.then((res) => {
if (res.success) {
if (isDev) {
return measure<Promise<unknown>>(
requestEv,
loader.__qrl.getSymbol().split('_', 1)[0],
() => loader.__qrl.call(requestEv, requestEv)
);
} else {
return loader.__qrl.call(requestEv, requestEv);
}
} else {
return requestEv.fail(res.status ?? 500, res.error);
}
})
.then((resolvedLoader) => {
if (typeof resolvedLoader === 'function') {
loaders[loaderId] = resolvedLoader();
} else {
if (isDev) {
verifySerializable(qwikSerializer, resolvedLoader, loader.__qrl);
}
loaders[loaderId] = resolvedLoader;
}
return resolvedLoader;
});

return loaders[loaderId];
}

if (method === 'POST') {
const selectedActionId = requestEv.query.get(QACTION_KEY);
if (selectedActionId) {
Expand All @@ -205,6 +248,7 @@ export function actionsMiddleware(routeActions: ActionInternal[], routeLoaders:
if (!result.success) {
loaders[selectedActionId] = requestEv.fail(result.status ?? 500, result.error);
} else {
//@ts-ignore
const actionResolved = isDev
? await measure(requestEv, action.__qrl.getSymbol().split('_', 1)[0], () =>
action.__qrl.call(requestEv, result.data as JSONObject, requestEv)
Expand All @@ -213,53 +257,17 @@ export function actionsMiddleware(routeActions: ActionInternal[], routeLoaders:
if (isDev) {
verifySerializable(qwikSerializer, actionResolved, action.__qrl);
}

loaders[selectedActionId] = actionResolved;
isAction = true;
}
}
}
}

if (routeLoaders.length > 0) {
const resolvedLoadersPromises = routeLoaders.map((loader) => {
const loaderId = loader.__id;
loaders[loaderId] = runValidators(
requestEv,
loader.__validators,
undefined, // data
isDev
)
.then((res) => {
if (res.success) {
if (isDev) {
return measure<Promise<unknown>>(
requestEv,
loader.__qrl.getSymbol().split('_', 1)[0],
() => loader.__qrl.call(requestEv, requestEv)
);
} else {
return loader.__qrl.call(requestEv, requestEv);
}
} else {
return requestEv.fail(res.status ?? 500, res.error);
}
})
.then((resolvedLoader) => {
if (typeof resolvedLoader === 'function') {
loaders[loaderId] = resolvedLoader();
} else {
if (isDev) {
verifySerializable(qwikSerializer, resolvedLoader, loader.__qrl);
}
loaders[loaderId] = resolvedLoader;
}
return resolvedLoader;
});

return loaders[loaderId];
});

await Promise.all(resolvedLoadersPromises);
if (routeLoaders.length > 0 && isAction) {
await Promise.all(routeLoaders.map(loader));
}
isAction = false;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to reset it to false?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to prevent it from running multiple times.

};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,7 @@ declare global {
/** @public */
export interface RequestEventAction<PLATFORM = QwikCityPlatform>
extends RequestEventCommon<PLATFORM> {
resolveValue: ResolveValue;
fail: <T extends Record<string, any>>(status: number, returnData: T) => FailReturn<T>;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { component$ } from "@builder.io/qwik";
import {
routeAction$,
Form,
routeLoader$,
zod$,
z,
globalAction$,
} from "@builder.io/qwik-city";

const useUser = routeLoader$(() => {
const user = {
firstName: "",
lastName: "",
test: 11,
};

return user;
});

const useUserGlobal = routeLoader$(() => {
const user = {
firstName: "",
lastName: "",
test: 11,
};

return user;
});

export const useAddUser = routeAction$(
async (data, requestEvent) => {
const res = await requestEvent.resolveValue(useUser);
res.firstName = data.firstName;
res.lastName = data.lastName;
return {
success: true,
userID: res,
};
},
zod$({
firstName: z.string(),
lastName: z.string(),
}),
);

export const globalAction = globalAction$(
async (data, requestEvent) => {
const res = await requestEvent.resolveValue(useUserGlobal);
res.firstName = data.globalfirstName;
res.lastName = data.globallastName;
return {
success: true,
userID: res,
};
},
zod$({
globalfirstName: z.string(),
globallastName: z.string(),
}),
);

export default component$(() => {
const action = useAddUser();
const globalstate = globalAction();
return (
<>
<div id="action">
<Form action={action}>
<input name="firstName" />
<input name="lastName" />
<button type="submit" id="actionButton">
Add user
</button>
</Form>
{action.value?.success && (
<>
<p>User {action.value.userID.firstName} added successfully</p>
<p>User {action.value.userID.lastName} added successfully</p>
<p>User {action.value.userID.test} added successfully</p>
</>
)}
</div>
<div id="global">
<Form action={globalstate}>
<input name="globalfirstName" />
<input name="globallastName" />
<button type="submit" id="globalButton">
Add user
</button>
</Form>
{globalstate.value?.success && (
<>
<p>User {globalstate.value.userID.firstName} added successfully</p>
<p>User {globalstate.value.userID.lastName} added successfully</p>
<p>User {globalstate.value.userID.test} added successfully</p>
</>
)}
</div>
</>
);
});
57 changes: 57 additions & 0 deletions starters/e2e/qwikcity/resolve-value.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { expect, test } from "@playwright/test";

test.describe("revolve value", () => {
test.describe("mpa", () => {
test.use({ javaScriptEnabled: false });
tests();
});

test.describe("spa", () => {
test.use({ javaScriptEnabled: true });
tests();
});

function tests() {
test("should submit valid form data", async ({ page }) => {
await page.goto("/qwikcity-test/resolve-value");

await page.fill('input[name="firstName"]', "NewFirstName");
await page.fill('input[name="lastName"]', "NewLastName");
await page.click('button[id="actionButton"]');

const successMessage = page.locator(
'p:has-text("User NewFirstName added successfully")',
);
const lastNameSuccess = page.locator(
'p:has-text("User NewLastName added successfully")',
);
const userIdDisplay = page.locator(
'p:has-text("User 11 added successfully")',
);
await expect(successMessage).toBeVisible();
await expect(lastNameSuccess).toBeVisible();
await expect(userIdDisplay).toBeVisible();
});

test("should submit global form data", async ({ page }) => {
await page.goto("/qwikcity-test/resolve-value");

await page.fill('input[name="globalfirstName"]', "GlobalFirstName");
await page.fill('input[name="globallastName"]', "GlobalLastName");
await page.click('button[id="globalButton"]');

const globalSuccess = page.locator(
'p:has-text("User GlobalFirstName added successfully")',
);
const globalLastName = page.locator(
'p:has-text("User GlobalLastName added successfully")',
);
const globalUserId = page.locator(
'p:has-text("User 11 added successfully")',
);
await expect(globalSuccess).toBeVisible();
await expect(globalLastName).toBeVisible();
await expect(globalUserId).toBeVisible();
});
}
});