@speedkit/cli 4.23.1 → 4.23.3
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/CHANGELOG.md +15 -0
- package/README.md +1 -1
- package/dist/services/deploy/checks/post-deploy/refresh-deployment-job-check.js +4 -3
- package/dist/services/deploy/checks/post-deploy/refresh-deployment-job-check.spec.d.ts +1 -0
- package/dist/services/deploy/checks/post-deploy/refresh-deployment-job-check.spec.js +33 -0
- package/dist/services/document-handler-runtime/document-handler-abort.d.ts +58 -0
- package/dist/services/document-handler-runtime/document-handler-abort.js +66 -0
- package/dist/services/document-handler-runtime/document-handler-abort.spec.d.ts +1 -0
- package/dist/services/document-handler-runtime/document-handler-abort.spec.js +84 -0
- package/dist/services/document-handler-runtime/document-handler-server.js +2 -1
- package/dist/services/document-handler-runtime/templates/test.js +3 -1
- package/dist/services/onboarding/error/document-handler-transform-error.d.ts +6 -0
- package/dist/services/onboarding/error/document-handler-transform-error.js +8 -0
- package/dist/services/onboarding/virtual-orestes-app/index.d.ts +8 -0
- package/dist/services/onboarding/virtual-orestes-app/index.js +21 -1
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
## [4.23.3](https://gitlab.orestes.info/baqend/speed-kit-cli/compare/v4.23.2...v4.23.3) (2026-08-25)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **deploy:** match refresh job names by prefix ([c593d9d](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/c593d9d69db8e05ccb71b320cb4f9c7cbda5cdac))
|
|
7
|
+
|
|
8
|
+
## [4.23.2](https://gitlab.orestes.info/baqend/speed-kit-cli/compare/v4.23.1...v4.23.2) (2026-08-21)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* **onboarding:** answer a Document Handler abort with 400, not 500 ([c4473ee](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/c4473eee65011c260caddbd073098cb21e652ae8))
|
|
14
|
+
* **onboarding:** send the abort as errorcode;desc=303, not an invented cause ([0e3beb0](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/0e3beb034ff5a47a4acffcb86191fc9a8f5683ea))
|
|
15
|
+
|
|
1
16
|
## [4.23.1](https://gitlab.orestes.info/baqend/speed-kit-cli/compare/v4.23.0...v4.23.1) (2026-08-21)
|
|
2
17
|
|
|
3
18
|
|
package/README.md
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { Set } from "baqend";
|
|
2
1
|
import ApplicationError from "../../../error-handling/error/application-error.js";
|
|
3
2
|
export class RefreshDeploymentJobCheck {
|
|
4
3
|
entityManager;
|
|
@@ -12,8 +11,10 @@ export class RefreshDeploymentJobCheck {
|
|
|
12
11
|
.find()
|
|
13
12
|
.resultList();
|
|
14
13
|
const requiredJobs = ["HTML", "Landing Page", "CSS, JS, Font"];
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
// Job names are free text and sometimes suffixed (e.g. "Landing Page (RegExp)"),
|
|
15
|
+
// so match on the prefix instead of the full name.
|
|
16
|
+
const foundJobNames = jobs.map((job) => (job?.data?.name || "").trim().toLowerCase());
|
|
17
|
+
const missingRequiredJobs = requiredJobs.filter((required) => !foundJobNames.some((name) => name.startsWith(required.toLowerCase())));
|
|
17
18
|
// Deployment jobs: soft warning only
|
|
18
19
|
const deploymentJobsMissing = jobs.filter((job) => job.type === "Deploymentjob" && job.created !== true);
|
|
19
20
|
if (deploymentJobsMissing.length > 0) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { expect } from "chai";
|
|
2
|
+
import { describe, it } from "mocha";
|
|
3
|
+
import { RefreshDeploymentJobCheck } from "./refresh-deployment-job-check.js";
|
|
4
|
+
import ApplicationError from "../../../error-handling/error/application-error.js";
|
|
5
|
+
function entityManagerWithJobs(names) {
|
|
6
|
+
const jobs = names.map((name) => ({ data: { name }, type: "Refreshjob" }));
|
|
7
|
+
return {
|
|
8
|
+
"jobs.Definition": {
|
|
9
|
+
find: () => ({ resultList: async () => jobs }),
|
|
10
|
+
},
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
const cli = { writeWarning: () => undefined };
|
|
14
|
+
async function runCheck(names) {
|
|
15
|
+
try {
|
|
16
|
+
await new RefreshDeploymentJobCheck(entityManagerWithJobs(names), cli).check();
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
return error;
|
|
20
|
+
}
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
describe("RefreshDeploymentJobCheck", () => {
|
|
24
|
+
it("accepts jobs whose names carry a suffix", async () => {
|
|
25
|
+
// As used by zeg and tectake.
|
|
26
|
+
expect(await runCheck(["HTML", "Landing Page (RegExp)", "CSS, JS, Font"])).to.equal(undefined);
|
|
27
|
+
});
|
|
28
|
+
it("reports jobs that are missing entirely", async () => {
|
|
29
|
+
const error = await runCheck(["HTML", "Images"]);
|
|
30
|
+
expect(error).to.be.instanceOf(ApplicationError);
|
|
31
|
+
expect(error.suggestions[0]).to.equal("Missing required jobs: Landing Page, CSS, JS, Font");
|
|
32
|
+
});
|
|
33
|
+
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Baqend's runtime exposes `Abort` as a global to Document Handler code. Throwing it
|
|
3
|
+
* signals a deliberate stop — a `blacklistHTML` guard, or a pre-render that came back
|
|
4
|
+
* non-200 — rather than a fault. Baqend extracts the message and answers a Bad Request,
|
|
5
|
+
* and the service worker falls back to origin.
|
|
6
|
+
*
|
|
7
|
+
* The shape follows `typings/Abort/index.d.ts` in the document-handler repository:
|
|
8
|
+
* `name` is always `"Abort"`, plus a `status` and an optional `data` payload.
|
|
9
|
+
*/
|
|
10
|
+
export declare class DocumentHandlerAbort<T = unknown> extends Error {
|
|
11
|
+
readonly name = "Abort";
|
|
12
|
+
status: number;
|
|
13
|
+
data?: T;
|
|
14
|
+
/**
|
|
15
|
+
* Creates a Document Handler abort.
|
|
16
|
+
*
|
|
17
|
+
* @param message - Reason the Document Handler stopped.
|
|
18
|
+
* @param data - Optional abort payload.
|
|
19
|
+
*/
|
|
20
|
+
constructor(message?: string, data?: T);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Status code Baqend answers a Document Handler abort with. It must stay below 500:
|
|
24
|
+
* the service worker treats 5xx from the Asset API as an infrastructure failure and
|
|
25
|
+
* disconnects Speed Kit, while a client error is classified from `server-timing`.
|
|
26
|
+
*/
|
|
27
|
+
export declare const ABORT_STATUS_CODE = 400;
|
|
28
|
+
/**
|
|
29
|
+
* Speed Kit error code for a Document Handler abort. Baqend reports the numeric code,
|
|
30
|
+
* not a cause name, and the backend resolves 303 to `ServerDocumentHandlerAbort`.
|
|
31
|
+
*
|
|
32
|
+
* Sibling code 314 is `ServerDocumentHandlerError`, which is what a Document Handler
|
|
33
|
+
* that throws an ordinary error produces. Keeping the two apart is the point of this
|
|
34
|
+
* module.
|
|
35
|
+
*/
|
|
36
|
+
export declare const ABORT_ERROR_CODE = "303";
|
|
37
|
+
/**
|
|
38
|
+
* `server-timing` value that makes the service worker classify a response as an abort.
|
|
39
|
+
*
|
|
40
|
+
* Only the `errorcode` metric is sent, which is what Baqend sends. The service worker
|
|
41
|
+
* reads `errorCause` and `errorcode` separately and builds the cause as
|
|
42
|
+
* `` `${errorCause?.desc || ClientError}${errorCode ? `-${errorCode.desc}` : ''}` ``
|
|
43
|
+
* (`AssetAPIHandler.ts:200-205`). With no `errorCause` metric it therefore reports
|
|
44
|
+
* `ClientError-303`, and the cause name is resolved from the code further downstream.
|
|
45
|
+
* Sending an `errorCause` metric instead would invent a cause the backend never sends.
|
|
46
|
+
*/
|
|
47
|
+
export declare const ABORT_SERVER_TIMING = "errorcode;desc=303";
|
|
48
|
+
/**
|
|
49
|
+
* Whether an error is a Document Handler abort rather than a failure.
|
|
50
|
+
*
|
|
51
|
+
* Checks the `name` marker instead of `instanceof`: the error is constructed inside a
|
|
52
|
+
* `vm` context and is re-wrapped by `DocumentHandlerTransformError` on the way out, so
|
|
53
|
+
* neither the realm nor the prototype chain survives reliably. `isAbort` covers the
|
|
54
|
+
* wrapped case.
|
|
55
|
+
*
|
|
56
|
+
* @param error - Error to classify.
|
|
57
|
+
*/
|
|
58
|
+
export declare function isDocumentHandlerAbort(error?: Error): boolean;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Baqend's runtime exposes `Abort` as a global to Document Handler code. Throwing it
|
|
3
|
+
* signals a deliberate stop — a `blacklistHTML` guard, or a pre-render that came back
|
|
4
|
+
* non-200 — rather than a fault. Baqend extracts the message and answers a Bad Request,
|
|
5
|
+
* and the service worker falls back to origin.
|
|
6
|
+
*
|
|
7
|
+
* The shape follows `typings/Abort/index.d.ts` in the document-handler repository:
|
|
8
|
+
* `name` is always `"Abort"`, plus a `status` and an optional `data` payload.
|
|
9
|
+
*/
|
|
10
|
+
export class DocumentHandlerAbort extends Error {
|
|
11
|
+
name = "Abort";
|
|
12
|
+
status = ABORT_STATUS_CODE;
|
|
13
|
+
data;
|
|
14
|
+
/**
|
|
15
|
+
* Creates a Document Handler abort.
|
|
16
|
+
*
|
|
17
|
+
* @param message - Reason the Document Handler stopped.
|
|
18
|
+
* @param data - Optional abort payload.
|
|
19
|
+
*/
|
|
20
|
+
constructor(message, data) {
|
|
21
|
+
super(message);
|
|
22
|
+
this.data = data;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Status code Baqend answers a Document Handler abort with. It must stay below 500:
|
|
27
|
+
* the service worker treats 5xx from the Asset API as an infrastructure failure and
|
|
28
|
+
* disconnects Speed Kit, while a client error is classified from `server-timing`.
|
|
29
|
+
*/
|
|
30
|
+
export const ABORT_STATUS_CODE = 400;
|
|
31
|
+
/**
|
|
32
|
+
* Speed Kit error code for a Document Handler abort. Baqend reports the numeric code,
|
|
33
|
+
* not a cause name, and the backend resolves 303 to `ServerDocumentHandlerAbort`.
|
|
34
|
+
*
|
|
35
|
+
* Sibling code 314 is `ServerDocumentHandlerError`, which is what a Document Handler
|
|
36
|
+
* that throws an ordinary error produces. Keeping the two apart is the point of this
|
|
37
|
+
* module.
|
|
38
|
+
*/
|
|
39
|
+
export const ABORT_ERROR_CODE = "303";
|
|
40
|
+
/**
|
|
41
|
+
* `server-timing` value that makes the service worker classify a response as an abort.
|
|
42
|
+
*
|
|
43
|
+
* Only the `errorcode` metric is sent, which is what Baqend sends. The service worker
|
|
44
|
+
* reads `errorCause` and `errorcode` separately and builds the cause as
|
|
45
|
+
* `` `${errorCause?.desc || ClientError}${errorCode ? `-${errorCode.desc}` : ''}` ``
|
|
46
|
+
* (`AssetAPIHandler.ts:200-205`). With no `errorCause` metric it therefore reports
|
|
47
|
+
* `ClientError-303`, and the cause name is resolved from the code further downstream.
|
|
48
|
+
* Sending an `errorCause` metric instead would invent a cause the backend never sends.
|
|
49
|
+
*/
|
|
50
|
+
export const ABORT_SERVER_TIMING = `errorcode;desc=${ABORT_ERROR_CODE}`;
|
|
51
|
+
/**
|
|
52
|
+
* Whether an error is a Document Handler abort rather than a failure.
|
|
53
|
+
*
|
|
54
|
+
* Checks the `name` marker instead of `instanceof`: the error is constructed inside a
|
|
55
|
+
* `vm` context and is re-wrapped by `DocumentHandlerTransformError` on the way out, so
|
|
56
|
+
* neither the realm nor the prototype chain survives reliably. `isAbort` covers the
|
|
57
|
+
* wrapped case.
|
|
58
|
+
*
|
|
59
|
+
* @param error - Error to classify.
|
|
60
|
+
*/
|
|
61
|
+
export function isDocumentHandlerAbort(error) {
|
|
62
|
+
if (!error) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
return (error.name === "Abort" || error.isAbort === true);
|
|
66
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { expect } from "chai";
|
|
2
|
+
import { describe, it } from "mocha";
|
|
3
|
+
import { ABORT_ERROR_CODE, ABORT_SERVER_TIMING, ABORT_STATUS_CODE, DocumentHandlerAbort, isDocumentHandlerAbort, } from "./document-handler-abort.js";
|
|
4
|
+
import { DocumentHandlerTransformError } from "../onboarding/error/document-handler-transform-error.js";
|
|
5
|
+
/**
|
|
6
|
+
* Mirrors the service worker's `parseServerTimingHeader`, so the header this module
|
|
7
|
+
* emits is asserted against the parser that has to read it.
|
|
8
|
+
*/
|
|
9
|
+
function parseServerTiming(header) {
|
|
10
|
+
const metrics = {};
|
|
11
|
+
header.split(",").forEach((metric) => {
|
|
12
|
+
const key = metric.includes(";") ? metric.trim().split(";")[0] : metric;
|
|
13
|
+
const descMatch = metric.match(/desc=([^;]*)/);
|
|
14
|
+
metrics[key] = { desc: descMatch ? descMatch[1] : "" };
|
|
15
|
+
});
|
|
16
|
+
return metrics;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Mirrors how the service worker builds the response cause in
|
|
20
|
+
* `AssetAPIHandler.ts:200-205`, to prove which cause the emitted header produces.
|
|
21
|
+
*/
|
|
22
|
+
function composeResponseCause(header) {
|
|
23
|
+
const metrics = parseServerTiming(header);
|
|
24
|
+
const errorCode = metrics.errorcode;
|
|
25
|
+
const errorCodeString = errorCode ? `-${errorCode.desc}` : "";
|
|
26
|
+
return `${metrics.errorCause?.desc || "ClientError"}${errorCodeString}`;
|
|
27
|
+
}
|
|
28
|
+
describe("DocumentHandlerAbort", () => {
|
|
29
|
+
it("matches the runtime contract the Document Handler expects", () => {
|
|
30
|
+
const abort = new DocumentHandlerAbort("blacklisted", { url: "/a" });
|
|
31
|
+
expect(abort.name).to.equal("Abort");
|
|
32
|
+
expect(abort.message).to.equal("blacklisted");
|
|
33
|
+
expect(abort.status).to.equal(ABORT_STATUS_CODE);
|
|
34
|
+
expect(abort.data).to.deep.equal({ url: "/a" });
|
|
35
|
+
expect(abort).to.be.instanceOf(Error);
|
|
36
|
+
});
|
|
37
|
+
it("keeps the abort status below the range that disconnects Speed Kit", () => {
|
|
38
|
+
expect(ABORT_STATUS_CODE).to.be.lessThan(500);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
describe("isDocumentHandlerAbort", () => {
|
|
42
|
+
it("recognises an abort by its name marker", () => {
|
|
43
|
+
expect(isDocumentHandlerAbort(new DocumentHandlerAbort("stop"))).to.equal(true);
|
|
44
|
+
});
|
|
45
|
+
it("recognises an abort that only carries the wrapped flag", () => {
|
|
46
|
+
const wrapped = Object.assign(new Error("stop"), { isAbort: true });
|
|
47
|
+
expect(isDocumentHandlerAbort(wrapped)).to.equal(true);
|
|
48
|
+
});
|
|
49
|
+
it("does not treat an ordinary failure as an abort", () => {
|
|
50
|
+
expect(isDocumentHandlerAbort(new Error("boom"))).to.equal(false);
|
|
51
|
+
expect(isDocumentHandlerAbort(undefined)).to.equal(false);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
describe("DocumentHandlerTransformError", () => {
|
|
55
|
+
it("carries the abort marker across the wrapper", () => {
|
|
56
|
+
const wrapped = new DocumentHandlerTransformError(new DocumentHandlerAbort("No category grid"));
|
|
57
|
+
expect(wrapped.isAbort).to.equal(true);
|
|
58
|
+
expect(wrapped.message).to.equal("No category grid");
|
|
59
|
+
expect(isDocumentHandlerAbort(wrapped)).to.equal(true);
|
|
60
|
+
});
|
|
61
|
+
it("leaves an ordinary failure unmarked", () => {
|
|
62
|
+
const wrapped = new DocumentHandlerTransformError(new Error("boom"));
|
|
63
|
+
expect(wrapped.isAbort).to.equal(false);
|
|
64
|
+
expect(isDocumentHandlerAbort(wrapped)).to.equal(false);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
describe("ABORT_SERVER_TIMING", () => {
|
|
68
|
+
it("sends the error code under the metric name Baqend uses", () => {
|
|
69
|
+
const metrics = parseServerTiming(ABORT_SERVER_TIMING);
|
|
70
|
+
expect(metrics.errorcode.desc).to.equal(ABORT_ERROR_CODE);
|
|
71
|
+
});
|
|
72
|
+
it("sends no errorCause metric, which Baqend does not send either", () => {
|
|
73
|
+
expect(parseServerTiming(ABORT_SERVER_TIMING).errorCause).to.equal(undefined);
|
|
74
|
+
});
|
|
75
|
+
it("makes the service worker report ClientError-303", () => {
|
|
76
|
+
expect(composeResponseCause(ABORT_SERVER_TIMING)).to.equal("ClientError-303");
|
|
77
|
+
});
|
|
78
|
+
it("is matched by the prewarm error-code selector when a metric follows", () => {
|
|
79
|
+
// TIMING_HEADER_ERROR_CODE_SELECTOR expects a trailing comma, as production
|
|
80
|
+
// sends further metrics after the code.
|
|
81
|
+
const [, code] = `${ABORT_SERVER_TIMING},dur=0`.match(/errorcode;desc=([^,]*),/) || [];
|
|
82
|
+
expect(code).to.equal(ABORT_ERROR_CODE);
|
|
83
|
+
});
|
|
84
|
+
});
|
|
@@ -8,6 +8,7 @@ import { INTEGRATION_FILES } from "../../models/files.js";
|
|
|
8
8
|
import fetch from "node-fetch";
|
|
9
9
|
import { Request } from "./server/request.js";
|
|
10
10
|
import { DocumentHandlerResponse } from "./server/document-handler-response.js";
|
|
11
|
+
import { DocumentHandlerAbort } from "./document-handler-abort.js";
|
|
11
12
|
import { safe } from "../../helpers/safe.js";
|
|
12
13
|
import { VmEmptyResponseError } from "../onboarding/error/vm-empty-response-error.js";
|
|
13
14
|
import { DocumentHandlerTransformError } from "../onboarding/error/document-handler-transform-error.js";
|
|
@@ -46,7 +47,7 @@ export class DocumentHandlerServer {
|
|
|
46
47
|
skInternalResponse,
|
|
47
48
|
skInternalHtml: "",
|
|
48
49
|
skInternalError: {},
|
|
49
|
-
Abort:
|
|
50
|
+
Abort: DocumentHandlerAbort,
|
|
50
51
|
APP: this.customerConfig.app,
|
|
51
52
|
DYNAMIC_FETCHER: await this.getDynamicFetcher(),
|
|
52
53
|
DYNAMIC_FETCHER_CONFIG: await this.getDynamicFetcherConfig(),
|
|
@@ -10,7 +10,9 @@ const database = require('./src/database-mock');
|
|
|
10
10
|
const documentHandler = require('./src/testDocumentHandler');
|
|
11
11
|
const fs = require('fs');
|
|
12
12
|
const {resolve} = require("path");
|
|
13
|
-
global.Abort = Error
|
|
13
|
+
global.Abort = class Abort extends Error {
|
|
14
|
+
constructor(message, data) { super(message); this.name = 'Abort'; this.status = 400; this.data = data; }
|
|
15
|
+
};
|
|
14
16
|
|
|
15
17
|
const url = '{{domain}}';
|
|
16
18
|
const file = '{{name}}';
|
|
@@ -1,3 +1,9 @@
|
|
|
1
1
|
export declare class DocumentHandlerTransformError extends Error {
|
|
2
|
+
/**
|
|
3
|
+
* True when the Document Handler stopped deliberately by throwing `Abort`, so the
|
|
4
|
+
* caller can answer with a client error instead of a 500. The wrapper drops the
|
|
5
|
+
* original prototype, so the flag has to be carried explicitly.
|
|
6
|
+
*/
|
|
7
|
+
readonly isAbort: boolean;
|
|
2
8
|
constructor(error: Error);
|
|
3
9
|
}
|
|
@@ -1,7 +1,15 @@
|
|
|
1
|
+
import { isDocumentHandlerAbort } from "../../document-handler-runtime/document-handler-abort.js";
|
|
1
2
|
export class DocumentHandlerTransformError extends Error {
|
|
3
|
+
/**
|
|
4
|
+
* True when the Document Handler stopped deliberately by throwing `Abort`, so the
|
|
5
|
+
* caller can answer with a client error instead of a 500. The wrapper drops the
|
|
6
|
+
* original prototype, so the flag has to be carried explicitly.
|
|
7
|
+
*/
|
|
8
|
+
isAbort;
|
|
2
9
|
constructor(error) {
|
|
3
10
|
super(error.message);
|
|
4
11
|
this.stack = error.stack;
|
|
5
12
|
this.cause = error.cause;
|
|
13
|
+
this.isAbort = isDocumentHandlerAbort(error);
|
|
6
14
|
}
|
|
7
15
|
}
|
|
@@ -52,6 +52,14 @@ export declare class VirtualOrestesApp {
|
|
|
52
52
|
private recordLocalCacheError;
|
|
53
53
|
private clearLocalCacheError;
|
|
54
54
|
private emitLocalCacheErrors;
|
|
55
|
+
/**
|
|
56
|
+
* Answers a Document Handler abort the way Baqend does: a Bad Request carrying the
|
|
57
|
+
* Speed Kit error code in `server-timing`. The service worker reports
|
|
58
|
+
* `ClientError-303` and falls back to origin, and the backend resolves 303 to
|
|
59
|
+
* `ServerDocumentHandlerAbort`. Answering 500 instead makes the service worker treat
|
|
60
|
+
* the page as an infrastructure failure and disconnect Speed Kit.
|
|
61
|
+
*/
|
|
62
|
+
private returnAbortResponse;
|
|
55
63
|
private returnErrorResponse;
|
|
56
64
|
private convertResponseHeadersToOrestesFormat;
|
|
57
65
|
private returnRedirect;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { BaqendResponse } from "../browser/baqend-response.js";
|
|
2
|
+
import { ABORT_SERVER_TIMING, ABORT_STATUS_CODE, isDocumentHandlerAbort, } from "../../document-handler-runtime/document-handler-abort.js";
|
|
2
3
|
import { safe } from "../../../helpers/safe.js";
|
|
3
4
|
import { META_CONTENT_TYPE_REGEX, } from "../onboarding-model.js";
|
|
4
5
|
import iconv from "iconv-lite";
|
|
@@ -142,7 +143,10 @@ export class VirtualOrestesApp {
|
|
|
142
143
|
}
|
|
143
144
|
const customResponse = await safe(this.documentHandler.transform(`text/html;charset=${textEncoding}`, responseContent, variation, originUrl, this.convertResponseHeadersToOrestesFormat(response)));
|
|
144
145
|
if (customResponse.success === false) {
|
|
145
|
-
|
|
146
|
+
// An abort is a decision, not a fault, so it must not surface as a cache error.
|
|
147
|
+
if (!isDocumentHandlerAbort(customResponse.errorObj)) {
|
|
148
|
+
this.recordLocalCacheError(originUrl, requestUrl, variation, customResponse);
|
|
149
|
+
}
|
|
146
150
|
return this.returnErrorResponse(customResponse, customHeaders);
|
|
147
151
|
}
|
|
148
152
|
this.clearLocalCacheError(originUrl, variation);
|
|
@@ -231,7 +235,23 @@ export class VirtualOrestesApp {
|
|
|
231
235
|
data: [...this.localCacheErrors.values()],
|
|
232
236
|
});
|
|
233
237
|
}
|
|
238
|
+
/**
|
|
239
|
+
* Answers a Document Handler abort the way Baqend does: a Bad Request carrying the
|
|
240
|
+
* Speed Kit error code in `server-timing`. The service worker reports
|
|
241
|
+
* `ClientError-303` and falls back to origin, and the backend resolves 303 to
|
|
242
|
+
* `ServerDocumentHandlerAbort`. Answering 500 instead makes the service worker treat
|
|
243
|
+
* the page as an infrastructure failure and disconnect Speed Kit.
|
|
244
|
+
*/
|
|
245
|
+
returnAbortResponse(customResponse, customHeaders) {
|
|
246
|
+
const message = customResponse.error;
|
|
247
|
+
this.cli.writeWarning(`[VirtualOrestes]: Document Handler aborted: ${message}`);
|
|
248
|
+
customHeaders.push({ name: "x-error", value: message }, { name: "server-timing", value: ABORT_SERVER_TIMING });
|
|
249
|
+
return new BaqendResponse(message, null, customHeaders, ABORT_STATUS_CODE);
|
|
250
|
+
}
|
|
234
251
|
returnErrorResponse(customResponse, customHeaders) {
|
|
252
|
+
if (isDocumentHandlerAbort(customResponse.errorObj)) {
|
|
253
|
+
return this.returnAbortResponse(customResponse, customHeaders);
|
|
254
|
+
}
|
|
235
255
|
const message = `Could not transform html: - ${customResponse.error}`;
|
|
236
256
|
this.cli.writeError(`[VirtualOrestes]: ${message}`);
|
|
237
257
|
this.cli.comment(customResponse.errorObj.stack);
|
package/oclif.manifest.json
CHANGED