Skip to content

Commit bf44c78

Browse files
committed
refactor(qwik-router): lean the major — abort delivery, hang fix, envelope caching, value export move to follow-ups
1 parent fb1415c commit bf44c78

12 files changed

Lines changed: 17 additions & 119 deletions

File tree

e2e/qwik-e2e/apps/qwikrouter-test/src/routes/(common)/actions/abort/index.tsx

Lines changed: 0 additions & 46 deletions
This file was deleted.

e2e/qwik-e2e/tests/qwikrouter/actions.e2e.ts

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -177,22 +177,3 @@ test.describe('actions', () => {
177177
});
178178
}
179179
});
180-
181-
test.describe('action abort semantics (spa)', () => {
182-
test.use({ javaScriptEnabled: true });
183-
test.beforeEach(async ({ page }) => {
184-
await page.goto('/qwikrouter-test/actions/abort/');
185-
});
186-
187-
test('programmatic submit() rejects on thrown error() and records no state', async ({ page }) => {
188-
await page.locator('#abort-run').click();
189-
await expect(page.locator('#abort-caught')).toHaveText('caught:418');
190-
await expect(page.locator('#abort-state')).toHaveText('false:undefined:undefined:false');
191-
});
192-
193-
test('submitcompleted does not fire when the submission aborts', async ({ page }) => {
194-
await page.locator('#abort-form-submit').click();
195-
await expect(page.locator('#abort-state')).toHaveText('false:undefined:undefined:false');
196-
await expect(page.locator('#abort-form-detail')).toHaveText('none');
197-
});
198-
});

e2e/qwik-e2e/tests/qwikrouter/loaders.e2e.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,8 @@ test.describe('loaders', () => {
3535
(r) => r.url().includes('/loaders/loader-fail/') && r.url().includes('q-loader')
3636
);
3737
await page.locator('#link-loader-fail-retry').click();
38-
// Failure envelopes ship HTTP 200 and are never cacheable.
3938
const response = await loaderResponse;
4039
expect(response.status()).toBe(200);
41-
expect(response.headers()['cache-control']).toBeUndefined();
4240
await expect(page.locator('#loader-fail-error')).toHaveText('429 Rate limited');
4341
});
4442

packages/docs/src/routes/docs/(qwikrouter)/action/index.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -439,7 +439,7 @@ export const useDeletePost = routeAction$(
439439
);
440440
```
441441

442-
A thrown `error()` never lands on `action.error`: the action records no state, a programmatic `action.submit()` rejects with the error, and a `<Form>` submission resets without firing `submitcompleted` — use `fail()` for anything the UI should display. Plain (non-`error()`) throws behave like unexpected errors and result in a sanitized 500.
442+
A thrown `error()` never lands on `action.error` — use `fail()` for anything the UI should display. Plain (non-`error()`) throws behave like unexpected errors and result in a sanitized 500.
443443

444444
## Previous form state
445445

packages/qwik-router/src/middleware/request-handler/handlers/json-request-wrapper.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,23 +57,21 @@ export function jsonRequestWrapper(): RequestHandler {
5757
throw err;
5858
}
5959
} else if (err instanceof ServerError) {
60-
requestEv.headers.delete('Cache-Control');
6160
if (isLoader) {
6261
await sendJsonResponse(requestEv, { e: err, a: 1 });
6362
} else {
64-
await sendActionResponse(requestEv, { aborted: err, s: err.status });
63+
await sendActionResponse(requestEv, { e: err, s: err.status });
6564
}
6665
} else if (err instanceof Error) {
6766
console.error('JSON request error:', err);
68-
requestEv.headers.delete('Cache-Control');
6967
const message = isDev
7068
? `${err.message}\n(this is only visible in dev mode)`
7169
: 'Internal Server Error';
7270
const se = new ServerError(500, message);
7371
if (isLoader) {
7472
await sendJsonResponse(requestEv, { e: se, a: 1 });
7573
} else {
76-
await sendActionResponse(requestEv, { aborted: se, s: 500 });
74+
await sendActionResponse(requestEv, { e: se, s: 500 });
7775
}
7876
} else {
7977
throw err; // AbortMessage etc.

packages/qwik-router/src/middleware/request-handler/handlers/loader-handler.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ export function loaderHandler(
109109
return;
110110
}
111111

112-
await sendLoaderResponse(requestEv, data, loader, !responseData.r && !responseData.e);
112+
await sendLoaderResponse(requestEv, data, loader);
113113
};
114114
}
115115

@@ -139,12 +139,11 @@ function resolvePreETag(
139139
async function sendLoaderResponse(
140140
requestEv: RequestEventInternal,
141141
data: string,
142-
loader?: LoaderInternal,
143-
cacheable = true
142+
loader?: LoaderInternal
144143
) {
145144
requestEv.headers.set('Content-Type', 'application/json; charset=utf-8');
146145
addVaryHeader(requestEv, FULLPATH_HEADER);
147-
if (cacheable && loader?.__expires && loader.__expires > 0) {
146+
if (loader?.__expires && loader.__expires > 0) {
148147
requestEv.cacheControl({ maxAge: Math.ceil(loader.__expires / 1000) });
149148
}
150149
requestEv.send(200, data);

packages/qwik-router/src/runtime/src/form-component.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { useNavigate } from './use-functions';
66
/** @public */
77
export interface FormSubmitCompletedDetail<T, ERROR = unknown> {
88
status: number;
9-
/** The action's successful return value. `undefined` when the action failed or aborted. */
9+
/** The action's successful return value. `undefined` when the action failed. */
1010
value: T | undefined;
1111
/** The `ServerError` from a returned `fail()` or a failed validator. */
1212
error: ServerError<ERROR> | undefined;

packages/qwik-router/src/runtime/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ export {
9090
zodQrl,
9191
} from './server-functions';
9292
export { routeLoader$, routeLoaderQrl } from './route-loaders';
93-
export { ServerError } from '../../middleware/request-handler/server-error';
93+
export type { ServerError } from '../../middleware/request-handler/server-error';
9494
export { ServiceWorkerRegister } from './sw-component';
9595
export {
9696
useContent,

packages/qwik-router/src/runtime/src/qwik-router-component.tsx

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -560,36 +560,26 @@ export const useQwikRouter = (props?: QwikRouterProps) => {
560560
const result = await submitAction(action, trackUrl.pathname);
561561
if (!result) {
562562
// HTTP redirect happened — bail
563-
routeLocation.isNavigating = false;
564563
routeInternal.untrackedValue = { type: navType, dest: trackUrl };
565564
return;
566565
}
567566

568-
if (!result.aborted) {
569-
actionData = {
570-
status: result.status,
571-
action: action.id,
572-
actionResult: result.error ?? result.result,
573-
};
574-
}
567+
actionData = {
568+
status: result.status,
569+
action: action.id,
570+
actionResult: result.error ?? result.result,
571+
};
575572

576573
// Resolve the action promise and free the closure
577574
if (action.resolve) {
578575
action.resolve({
579576
status: result.status,
580577
result: result.result,
581578
error: result.error,
582-
aborted: result.aborted,
583579
});
584580
action.resolve = undefined;
585581
}
586582

587-
if (result.aborted) {
588-
routeLocation.isNavigating = false;
589-
routeInternal.untrackedValue = { type: navType, dest: trackUrl };
590-
return;
591-
}
592-
593583
actionLoaderHashes = result.loaderHashes;
594584
shouldInvalidateActionLoaders = true;
595585
}

packages/qwik-router/src/runtime/src/server-functions.ts

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -116,15 +116,8 @@ Action.run() can only be called on the browser, for example when a user clicks a
116116
id,
117117
resolve: noSerialize(resolve),
118118
};
119-
}).then(({ result, status, error, aborted }) => {
119+
}).then(({ result, status, error }) => {
120120
state.isRunning = false;
121-
if (aborted) {
122-
if (form) {
123-
// The submission aborted — submitcompleted does not fire.
124-
return { status, value: undefined, error: undefined };
125-
}
126-
throw aborted;
127-
}
128121
state.status = status;
129122
if (error) {
130123
state.error = error;

0 commit comments

Comments
 (0)