@michaelthielemann/kestrel-delivery-static 0.1.0 → 5.0.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/README.md +39 -0
- package/dist/impl.d.ts +54 -0
- package/dist/impl.js +377 -0
- package/dist/llms.d.ts +75 -0
- package/dist/llms.js +183 -0
- package/dist/module.d.ts +212 -0
- package/dist/module.js +118 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +32 -32
- package/dist/index.d.ts +0 -4
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -4
- package/dist/index.js.map +0 -1
- package/dist/server/deploy-output.d.ts +0 -139
- package/dist/server/deploy-output.d.ts.map +0 -1
- package/dist/server/deploy-output.js +0 -220
- package/dist/server/deploy-output.js.map +0 -1
- package/dist/server/port.d.ts +0 -21
- package/dist/server/port.d.ts.map +0 -1
- package/dist/server/port.js +0 -46
- package/dist/server/port.js.map +0 -1
- package/dist/server/render-route.d.ts +0 -17
- package/dist/server/render-route.d.ts.map +0 -1
- package/dist/server/render-route.js +0 -20
- package/dist/server/render-route.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,50 +1,50 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@michaelthielemann/kestrel-delivery-static",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "5.0.2",
|
|
4
|
+
"description": "Renders published documents per locale via renderer@1, stores them in blobstore@1 and tracks a publish status.",
|
|
5
|
+
"type": "module",
|
|
5
6
|
"license": "Apache-2.0",
|
|
6
|
-
"
|
|
7
|
-
|
|
8
|
-
"repository": {
|
|
9
|
-
"type": "git",
|
|
10
|
-
"url": "git+https://github.com/MichaelThielemann/kestrel.git",
|
|
11
|
-
"directory": "packages/kestrel-delivery-static"
|
|
12
|
-
},
|
|
13
|
-
"bugs": "https://github.com/MichaelThielemann/kestrel/issues",
|
|
14
|
-
"publishConfig": {
|
|
15
|
-
"access": "public",
|
|
16
|
-
"provenance": true
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=22.13.0"
|
|
17
9
|
},
|
|
18
|
-
"type": "module",
|
|
19
|
-
"main": "./dist/index.js",
|
|
20
|
-
"types": "./dist/index.d.ts",
|
|
21
10
|
"exports": {
|
|
22
11
|
".": {
|
|
23
|
-
"types": "./dist/
|
|
24
|
-
"
|
|
12
|
+
"types": "./dist/module.d.ts",
|
|
13
|
+
"default": "./dist/module.js"
|
|
14
|
+
},
|
|
15
|
+
"./impl": {
|
|
16
|
+
"types": "./dist/impl.d.ts",
|
|
17
|
+
"default": "./dist/impl.js"
|
|
25
18
|
}
|
|
26
19
|
},
|
|
27
|
-
"sideEffects": false,
|
|
28
20
|
"files": [
|
|
29
|
-
"dist"
|
|
21
|
+
"dist",
|
|
22
|
+
"README.md"
|
|
30
23
|
],
|
|
31
|
-
"
|
|
32
|
-
"
|
|
24
|
+
"peerDependencies": {
|
|
25
|
+
"@michaelthielemann/kestrel": "^5.0.2",
|
|
26
|
+
"@michaelthielemann/kestrel-contracts": "^5.0.2"
|
|
33
27
|
},
|
|
34
28
|
"dependencies": {
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"@michaelthielemann/kestrel-core": "0.1.0",
|
|
38
|
-
"@michaelthielemann/kestrel-delivery-live": "0.1.0"
|
|
29
|
+
"turndown": "^7.2.4",
|
|
30
|
+
"zod": "^3.23.8"
|
|
39
31
|
},
|
|
40
32
|
"devDependencies": {
|
|
41
|
-
"@types/
|
|
42
|
-
"
|
|
43
|
-
"
|
|
33
|
+
"@types/turndown": "^5.0.6",
|
|
34
|
+
"vitest": "^3.2.0",
|
|
35
|
+
"@michaelthielemann/kestrel": "5.0.2",
|
|
36
|
+
"@michaelthielemann/kestrel-content-default": "5.0.2",
|
|
37
|
+
"@michaelthielemann/kestrel-contracts": "5.0.2",
|
|
38
|
+
"@michaelthielemann/kestrel-site-default": "5.0.2"
|
|
39
|
+
},
|
|
40
|
+
"repository": {
|
|
41
|
+
"type": "git",
|
|
42
|
+
"url": "git+https://github.com/MichaelThielemann/kestrel.git",
|
|
43
|
+
"directory": "packages/delivery-static"
|
|
44
44
|
},
|
|
45
|
+
"homepage": "https://github.com/MichaelThielemann/kestrel#readme",
|
|
46
|
+
"bugs": "https://github.com/MichaelThielemann/kestrel/issues",
|
|
45
47
|
"scripts": {
|
|
46
|
-
"build": "tsc -
|
|
47
|
-
"typecheck": "tsc --noEmit -p tsconfig.typecheck.json",
|
|
48
|
-
"test": "vitest run --typecheck"
|
|
48
|
+
"build": "tsc -b tsconfig.json"
|
|
49
49
|
}
|
|
50
50
|
}
|
package/dist/index.d.ts
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
export { createStaticDeliveryPort, deliveryPortFor } from './server/port.js';
|
|
2
|
-
export { renderRoute } from './server/render-route.js';
|
|
3
|
-
export { isEnvTrue, planS3Deploy, resolveOutputTarget, resolveOutputCreds, isCompressedSidecar, isPermanentError, deployStaticOutput, recordRouteDiscovery, readRouteDiscovery, type OutputTarget, type OutputConfig, type DeployResult, type DeployDriver, type RouteDiscovery, type DeployOptions, } from './server/deploy-output.js';
|
|
4
|
-
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAC5E,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AACtD,OAAO,EACL,SAAS,EACT,YAAY,EACZ,mBAAmB,EACnB,kBAAkB,EAClB,mBAAmB,EACnB,gBAAgB,EAChB,kBAAkB,EAClB,oBAAoB,EACpB,kBAAkB,EAClB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,aAAa,GACnB,MAAM,2BAA2B,CAAA"}
|
package/dist/index.js
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
export { createStaticDeliveryPort, deliveryPortFor } from './server/port.js';
|
|
2
|
-
export { renderRoute } from './server/render-route.js';
|
|
3
|
-
export { isEnvTrue, planS3Deploy, resolveOutputTarget, resolveOutputCreds, isCompressedSidecar, isPermanentError, deployStaticOutput, recordRouteDiscovery, readRouteDiscovery, } from './server/deploy-output.js';
|
|
4
|
-
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAC5E,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AACtD,OAAO,EACL,SAAS,EACT,YAAY,EACZ,mBAAmB,EACnB,kBAAkB,EAClB,mBAAmB,EACnB,gBAAgB,EAChB,kBAAkB,EAClB,oBAAoB,EACpB,kBAAkB,GAOnB,MAAM,2BAA2B,CAAA"}
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
import type { StorageDriver } from '@michaelthielemann/kestrel-core';
|
|
2
|
-
/** A boolean env flag is on for any of the common truthy spellings (case-insensitive, trimmed).
|
|
3
|
-
* @public */
|
|
4
|
-
export declare function isEnvTrue(value: string | undefined): boolean;
|
|
5
|
-
/** @public */
|
|
6
|
-
export declare function planS3Deploy(opts: {
|
|
7
|
-
isStaticGenerate: boolean;
|
|
8
|
-
dryRun: boolean;
|
|
9
|
-
bucket: string;
|
|
10
|
-
accessKeyId: string;
|
|
11
|
-
secretAccessKey: string;
|
|
12
|
-
prefix?: string;
|
|
13
|
-
/**
|
|
14
|
-
* `output.auto` — the running server publishes to this bucket/prefix itself. Build-time route seeding is
|
|
15
|
-
* then disabled (a prerendered file would shadow the live route), so `.output/public` holds only what
|
|
16
|
-
* Nitro's crawler reached: shipping it would reconcile the server's live pages, sitemap and robots away.
|
|
17
|
-
*/
|
|
18
|
-
autoPublish?: boolean;
|
|
19
|
-
/** Bucket the S3 media driver writes live uploads to (empty when media isn't on S3 — then irrelevant). */
|
|
20
|
-
mediaBucket?: string;
|
|
21
|
-
/** Key prefix of those live media objects, to keep the reconcile from deleting them. */
|
|
22
|
-
mediaPrefix?: string;
|
|
23
|
-
}): 'skip' | 'deploy';
|
|
24
|
-
/** @public */
|
|
25
|
-
export interface OutputTarget {
|
|
26
|
-
driver: 'local' | 's3';
|
|
27
|
-
/** Non-secret S3 settings; credentials are resolved separately by `resolveOutputCreds`. */
|
|
28
|
-
s3: {
|
|
29
|
-
bucket: string;
|
|
30
|
-
region: string;
|
|
31
|
-
endpoint: string;
|
|
32
|
-
prefix: string;
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
/** @public */
|
|
36
|
-
export type OutputConfig = {
|
|
37
|
-
driver?: string;
|
|
38
|
-
s3?: {
|
|
39
|
-
bucket?: string;
|
|
40
|
-
region?: string;
|
|
41
|
-
endpoint?: string;
|
|
42
|
-
prefix?: string;
|
|
43
|
-
};
|
|
44
|
-
};
|
|
45
|
-
/**
|
|
46
|
-
* Resolve where `nuxt generate`'s static output is shipped: a local directory (default) or an S3
|
|
47
|
-
* bucket. Precedence per setting: `KESTREL_OUTPUT_*` env → config → default (the documented Kestrel
|
|
48
|
-
* order, same as the runtime publisher). Pure, so the precedence is unit-testable; secrets are resolved
|
|
49
|
-
* by `resolveOutputCreds`, never here.
|
|
50
|
-
* @public
|
|
51
|
-
*/
|
|
52
|
-
export declare function resolveOutputTarget(out?: OutputConfig, env?: Record<string, string | undefined>): OutputTarget;
|
|
53
|
-
/**
|
|
54
|
-
* S3 credentials for the output deploy: the output-specific `KESTREL_OUTPUT_S3_*`, falling back to the
|
|
55
|
-
* shared media `KESTREL_S3_*` so a single S3 account "just works" — identical to the runtime publisher's
|
|
56
|
-
* resolution. Required keys default to '' (not undefined) so `planS3Deploy` detects a missing credential.
|
|
57
|
-
* @public
|
|
58
|
-
*/
|
|
59
|
-
export declare function resolveOutputCreds(env?: Record<string, string | undefined>): {
|
|
60
|
-
accessKeyId: string;
|
|
61
|
-
secretAccessKey: string;
|
|
62
|
-
sessionToken?: string;
|
|
63
|
-
};
|
|
64
|
-
/**
|
|
65
|
-
* A `*.gz`/`*.br` file is a pre-compressed sidecar (from `nitro.compressPublicAssets`) when its
|
|
66
|
-
* uncompressed base is also in the output. On a plain static S3 host these are dead weight — the host
|
|
67
|
-
* does no `Accept-Encoding` negotiation, so nobody fetches `index.html.gz`, and storing it *as*
|
|
68
|
-
* `index.html` with `Content-Encoding` would collide with the real file — so the deploy skips them.
|
|
69
|
-
* A standalone `*.gz` archive (no base file) is genuine content and is kept.
|
|
70
|
-
* @public
|
|
71
|
-
*/
|
|
72
|
-
export declare function isCompressedSidecar(key: string, all: Set<string>): boolean;
|
|
73
|
-
/** @public */
|
|
74
|
-
export interface DeployResult {
|
|
75
|
-
pruned: number;
|
|
76
|
-
keys: string[];
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* What build-time route discovery enumerated for this generate, plus WHY that enumeration is known to be
|
|
80
|
-
* short of the site's pages (`incomplete` unset ⇒ it is complete). The runtime publisher's
|
|
81
|
-
* `{ routes, failed }` is the same contract: an enumeration that came up short must never be the
|
|
82
|
-
* authority for what gets deleted.
|
|
83
|
-
*/
|
|
84
|
-
/** @public */
|
|
85
|
-
export interface RouteDiscovery {
|
|
86
|
-
routes: string[];
|
|
87
|
-
incomplete?: string;
|
|
88
|
-
}
|
|
89
|
-
/** The prerender and deploy modules are loaded separately and share no module scope — but they are handed
|
|
90
|
-
* the same Nuxt instance, so that object is what carries the signal from the one to the other.
|
|
91
|
-
* @public */
|
|
92
|
-
export declare function recordRouteDiscovery(nuxt: object, discovery: RouteDiscovery): void;
|
|
93
|
-
/** `undefined` ⇒ discovery never reported anything, which is not itself evidence of a degraded build.
|
|
94
|
-
* @public */
|
|
95
|
-
export declare function readRouteDiscovery(nuxt: object): RouteDiscovery | undefined;
|
|
96
|
-
/** The deploy needs `put` always, and `list`+`delete` for the reconcile; both are optional on the driver.
|
|
97
|
-
* @public */
|
|
98
|
-
export type DeployDriver = Pick<StorageDriver, 'put'> & Partial<Pick<StorageDriver, 'list' | 'delete'>>;
|
|
99
|
-
/** @public */
|
|
100
|
-
export interface DeployOptions {
|
|
101
|
-
dryRun?: boolean;
|
|
102
|
-
log?: (msg: string) => void;
|
|
103
|
-
/** Max operations in flight at once (default 8). Bounded so a large tree never opens thousands of sockets. */
|
|
104
|
-
concurrency?: number;
|
|
105
|
-
/** Total attempts per operation before giving up (default 3); retries ride out transient S3 errors. */
|
|
106
|
-
retries?: number;
|
|
107
|
-
/** Sleep between retries (exponential backoff); injectable so tests run without real delay. */
|
|
108
|
-
sleep?: (ms: number) => Promise<void>;
|
|
109
|
-
/**
|
|
110
|
-
* Why a build step REPORTED that this output tree is short of pages (routes that failed to prerender,
|
|
111
|
-
* route discovery that could not read the DB). Set ⇒ upload but never reconcile: pages missing from the
|
|
112
|
-
* input are indistinguishable from pages removed from the CMS, so the prune would delete live ones.
|
|
113
|
-
* Only a step's own failure report belongs here: the SHAPE of the output cannot tell a degraded build
|
|
114
|
-
* from a small site, so inferring it would strand the pages an editor deleted.
|
|
115
|
-
*/
|
|
116
|
-
incomplete?: string;
|
|
117
|
-
}
|
|
118
|
-
/**
|
|
119
|
-
* A permanent client error — bad/forbidden credentials, missing bucket, denied access (400/401/403/404).
|
|
120
|
-
* These never become successful on a later attempt, so retrying only delays the inevitable loud failure.
|
|
121
|
-
* @public
|
|
122
|
-
*/
|
|
123
|
-
export declare function isPermanentError(err: unknown): boolean;
|
|
124
|
-
/**
|
|
125
|
-
* Upload every file under `dir` to `driver`, keyed by its POSIX-relative path, with a content type
|
|
126
|
-
* inferred from the extension — the S3 equivalent of `aws s3 sync` (overwrites). Then reconciles: deletes
|
|
127
|
-
* remote objects under the prefix that this generate didn't produce (`--delete`), so pages removed from
|
|
128
|
-
* the CMS stop being served (output ≡ DB, no toggle). The reconcile is skipped when the driver can't
|
|
129
|
-
* list+delete and when the caller flagged the tree `incomplete` — a build step reported it is short of
|
|
130
|
-
* pages, so their absence says nothing about the CMS. Uploads still run then: shipping the pages that did
|
|
131
|
-
* render is safe, deleting on their absence is not.
|
|
132
|
-
* Uploads and deletes run with bounded concurrency and each is retried with
|
|
133
|
-
* backoff, so a transient 5xx/throttle/dropped-connection no longer aborts the deploy mid-way and
|
|
134
|
-
* half-updates the bucket; only an operation that fails every attempt rejects (true rollback isn't
|
|
135
|
-
* possible on S3). With `dryRun`, walks/lists and reports without writing.
|
|
136
|
-
* @public
|
|
137
|
-
*/
|
|
138
|
-
export declare function deployStaticOutput(dir: string, driver: DeployDriver, opts?: DeployOptions): Promise<DeployResult>;
|
|
139
|
-
//# sourceMappingURL=deploy-output.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"deploy-output.d.ts","sourceRoot":"","sources":["../../src/server/deploy-output.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAA;AAEpE;aACa;AACb,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAG5D;AA0BD,cAAc;AACd,wBAAgB,YAAY,CAAC,IAAI,EAAE;IACjC,gBAAgB,EAAE,OAAO,CAAA;IACzB,MAAM,EAAE,OAAO,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,MAAM,CAAA;IACnB,eAAe,EAAE,MAAM,CAAA;IACvB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,0GAA0G;IAC1G,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,wFAAwF;IACxF,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,GAAG,MAAM,GAAG,QAAQ,CAmCpB;AAED,cAAc;AACd,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,OAAO,GAAG,IAAI,CAAA;IACtB,2FAA2F;IAC3F,EAAE,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;CACzE;AAED,cAAc;AACd,MAAM,MAAM,YAAY,GAAG;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAAA;AAG7H;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,GAAE,YAAiB,EAAE,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAM,GAAG,YAAY,CAItH;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAM,GAAG;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,CAMxJ;AAYD;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,OAAO,CAG1E;AAED,cAAc;AACd,MAAM,WAAW,YAAY;IAAG,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE;AAEhE;;;;;GAKG;AACH,cAAc;AACd,MAAM,WAAW,cAAc;IAAG,MAAM,EAAE,MAAM,EAAE,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE;AAIzE;;cAEc;AACd,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,cAAc,GAAG,IAAI,CAElF;AAED;aACa;AACb,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAE3E;AAED;aACa;AACb,MAAM,MAAM,YAAY,GAAG,IAAI,CAAC,aAAa,EAAE,KAAK,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,aAAa,EAAE,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAA;AAEvG,cAAc;AACd,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;IAC3B,8GAA8G;IAC9G,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,uGAAuG;IACvG,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,+FAA+F;IAC/F,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACrC;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAmBD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAGtD;AAiBD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,YAAY,EACpB,IAAI,GAAE,aAAkB,GACvB,OAAO,CAAC,YAAY,CAAC,CAqCvB"}
|
|
@@ -1,220 +0,0 @@
|
|
|
1
|
-
import { readdirSync, readFileSync } from 'node:fs';
|
|
2
|
-
import { join, relative, sep } from 'node:path';
|
|
3
|
-
import { setTimeout as sleepFor } from 'node:timers/promises';
|
|
4
|
-
import { resolveS3Settings, contentTypeFor, cacheControlFor } from '@michaelthielemann/kestrel-core';
|
|
5
|
-
/** A boolean env flag is on for any of the common truthy spellings (case-insensitive, trimmed).
|
|
6
|
-
* @public */
|
|
7
|
-
export function isEnvTrue(value) {
|
|
8
|
-
const t = value?.trim().toLowerCase();
|
|
9
|
-
return t === 'true' || t === '1' || t === 'yes' || t === 'on';
|
|
10
|
-
}
|
|
11
|
-
/**
|
|
12
|
-
* Decide what the deploy hook should do, kept pure so the trigger guard is unit-testable. Returns
|
|
13
|
-
* `'skip'` when this isn't a static `nuxt generate` — a plain `nuxt build` (or dev) must never deploy,
|
|
14
|
-
* even with `driver: s3` configured — and when `output.auto` hands publishing to the running server
|
|
15
|
-
* (see `autoPublish`). For a real (non-dry-run) static generate it throws when the
|
|
16
|
-
* bucket or `KESTREL_S3_*` credentials are missing, so a misconfigured deploy fails the generate
|
|
17
|
-
* loudly instead of warning and exiting 0 with a stale live bucket. A dry-run needs neither.
|
|
18
|
-
*/
|
|
19
|
-
/** Strip leading/trailing slashes so prefixes compare on bare path segments. */
|
|
20
|
-
const normPrefix = (p) => p.replace(/^\/+|\/+$/g, '');
|
|
21
|
-
/**
|
|
22
|
-
* Do two S3 key prefixes share keyspace on a path-segment boundary — i.e. is one equal to, or nested
|
|
23
|
-
* under, the other? Catches both nesting directions: a prune of `a` reaches everything under `a/b`, and
|
|
24
|
-
* a prune of `a/b` reaches the `a/b/…` slice of media stored under `a`. A pure-string collision like
|
|
25
|
-
* `site` vs `siteother` is *not* an overlap. Empty prefixes never overlap (handled by their own guards).
|
|
26
|
-
*/
|
|
27
|
-
function prefixesOverlap(a, b) {
|
|
28
|
-
const x = normPrefix(a);
|
|
29
|
-
const y = normPrefix(b);
|
|
30
|
-
if (!x || !y)
|
|
31
|
-
return false;
|
|
32
|
-
return x === y || x.startsWith(`${y}/`) || y.startsWith(`${x}/`);
|
|
33
|
-
}
|
|
34
|
-
/** @public */
|
|
35
|
-
export function planS3Deploy(opts) {
|
|
36
|
-
if (!opts.isStaticGenerate)
|
|
37
|
-
return 'skip';
|
|
38
|
-
if (opts.autoPublish)
|
|
39
|
-
return 'skip';
|
|
40
|
-
if (!opts.dryRun) {
|
|
41
|
-
const missing = [];
|
|
42
|
-
if (!opts.bucket)
|
|
43
|
-
missing.push('bucket (KESTREL_OUTPUT_S3_BUCKET)');
|
|
44
|
-
if (!opts.accessKeyId)
|
|
45
|
-
missing.push('KESTREL_OUTPUT_S3_ACCESS_KEY_ID / KESTREL_S3_ACCESS_KEY_ID');
|
|
46
|
-
if (!opts.secretAccessKey)
|
|
47
|
-
missing.push('KESTREL_OUTPUT_S3_SECRET_ACCESS_KEY / KESTREL_S3_SECRET_ACCESS_KEY');
|
|
48
|
-
if (missing.length) {
|
|
49
|
-
throw new Error(`[kestrel] output.driver=s3 but ${missing.join(', ')} ${missing.length > 1 ? 'are' : 'is'} missing`
|
|
50
|
-
+ ' — refusing to finish `nuxt generate` with a broken S3 deploy.');
|
|
51
|
-
}
|
|
52
|
-
// The deploy always reconciles (deletes every remote object under the prefix not in this generate —
|
|
53
|
-
// output ≡ DB). With an empty prefix that's the whole bucket root, which may hold the media bucket's
|
|
54
|
-
// objects too. Refuse rather than risk wiping it: an S3 deploy must target a dedicated prefix.
|
|
55
|
-
if (!opts.prefix) {
|
|
56
|
-
throw new Error('[kestrel] output.s3.prefix is empty — refusing to deploy (the always-on reconcile would delete the'
|
|
57
|
-
+ ' whole bucket root, and a shared/media bucket could be wiped). Set KESTREL_OUTPUT_S3_PREFIX to a'
|
|
58
|
-
+ ' prefix dedicated to the generated site.');
|
|
59
|
-
}
|
|
60
|
-
// Same bucket as the S3 media driver AND overlapping prefixes → the reconcile would delete live uploads.
|
|
61
|
-
if (opts.mediaBucket && opts.mediaBucket === opts.bucket
|
|
62
|
-
&& opts.mediaPrefix && prefixesOverlap(opts.prefix, opts.mediaPrefix)) {
|
|
63
|
-
throw new Error(`[kestrel] the output reconcile would delete live media: output prefix "${opts.prefix}" overlaps the`
|
|
64
|
-
+ ` S3 media prefix "${opts.mediaPrefix}" in the same bucket "${opts.bucket}". Give the generated`
|
|
65
|
-
+ ' site its own bucket or a non-overlapping KESTREL_OUTPUT_S3_PREFIX.');
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
return 'deploy';
|
|
69
|
-
}
|
|
70
|
-
const clean = (v) => (typeof v === 'string' && v.trim() ? v.trim() : undefined);
|
|
71
|
-
/**
|
|
72
|
-
* Resolve where `nuxt generate`'s static output is shipped: a local directory (default) or an S3
|
|
73
|
-
* bucket. Precedence per setting: `KESTREL_OUTPUT_*` env → config → default (the documented Kestrel
|
|
74
|
-
* order, same as the runtime publisher). Pure, so the precedence is unit-testable; secrets are resolved
|
|
75
|
-
* by `resolveOutputCreds`, never here.
|
|
76
|
-
* @public
|
|
77
|
-
*/
|
|
78
|
-
export function resolveOutputTarget(out = {}, env = {}) {
|
|
79
|
-
const driver = (clean(env.KESTREL_OUTPUT_DRIVER) ?? clean(out.driver))?.toLowerCase() === 's3' ? 's3' : 'local';
|
|
80
|
-
// Same non-secret resolver media uses, under the KESTREL_OUTPUT_S3_* prefix (own bucket, own creds).
|
|
81
|
-
return { driver, s3: resolveS3Settings(out.s3, env, 'KESTREL_OUTPUT_S3') };
|
|
82
|
-
}
|
|
83
|
-
/**
|
|
84
|
-
* S3 credentials for the output deploy: the output-specific `KESTREL_OUTPUT_S3_*`, falling back to the
|
|
85
|
-
* shared media `KESTREL_S3_*` so a single S3 account "just works" — identical to the runtime publisher's
|
|
86
|
-
* resolution. Required keys default to '' (not undefined) so `planS3Deploy` detects a missing credential.
|
|
87
|
-
* @public
|
|
88
|
-
*/
|
|
89
|
-
export function resolveOutputCreds(env = {}) {
|
|
90
|
-
return {
|
|
91
|
-
accessKeyId: clean(env.KESTREL_OUTPUT_S3_ACCESS_KEY_ID) ?? clean(env.KESTREL_S3_ACCESS_KEY_ID) ?? '',
|
|
92
|
-
secretAccessKey: clean(env.KESTREL_OUTPUT_S3_SECRET_ACCESS_KEY) ?? clean(env.KESTREL_S3_SECRET_ACCESS_KEY) ?? '',
|
|
93
|
-
sessionToken: clean(env.KESTREL_OUTPUT_S3_SESSION_TOKEN) ?? clean(env.KESTREL_S3_SESSION_TOKEN),
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
/** Every file under `dir` as `{ abs, key }`, the key being its POSIX-relative path (stable order). */
|
|
97
|
-
function listFiles(dir) {
|
|
98
|
-
return readdirSync(dir, { recursive: true, withFileTypes: true })
|
|
99
|
-
.filter((e) => e.isFile())
|
|
100
|
-
.map((e) => {
|
|
101
|
-
const abs = join(e.parentPath, e.name);
|
|
102
|
-
return { abs, key: relative(dir, abs).split(sep).join('/') };
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
106
|
-
* A `*.gz`/`*.br` file is a pre-compressed sidecar (from `nitro.compressPublicAssets`) when its
|
|
107
|
-
* uncompressed base is also in the output. On a plain static S3 host these are dead weight — the host
|
|
108
|
-
* does no `Accept-Encoding` negotiation, so nobody fetches `index.html.gz`, and storing it *as*
|
|
109
|
-
* `index.html` with `Content-Encoding` would collide with the real file — so the deploy skips them.
|
|
110
|
-
* A standalone `*.gz` archive (no base file) is genuine content and is kept.
|
|
111
|
-
* @public
|
|
112
|
-
*/
|
|
113
|
-
export function isCompressedSidecar(key, all) {
|
|
114
|
-
const m = /\.(?:gz|br)$/.exec(key);
|
|
115
|
-
return m ? all.has(key.slice(0, m.index)) : false;
|
|
116
|
-
}
|
|
117
|
-
/** The prerender and deploy modules are loaded separately and share no module scope — but they are handed
|
|
118
|
-
* the same Nuxt instance, so that object is what carries the signal from the one to the other.
|
|
119
|
-
* @public */
|
|
120
|
-
export function recordRouteDiscovery(nuxt, discovery) {
|
|
121
|
-
nuxt._kestrelRouteDiscovery = discovery;
|
|
122
|
-
}
|
|
123
|
-
/** `undefined` ⇒ discovery never reported anything, which is not itself evidence of a degraded build.
|
|
124
|
-
* @public */
|
|
125
|
-
export function readRouteDiscovery(nuxt) {
|
|
126
|
-
return nuxt._kestrelRouteDiscovery;
|
|
127
|
-
}
|
|
128
|
-
async function runPool(items, concurrency, task) {
|
|
129
|
-
let next = 0;
|
|
130
|
-
const worker = async () => {
|
|
131
|
-
while (next < items.length) {
|
|
132
|
-
const i = next++;
|
|
133
|
-
await task(items[i]);
|
|
134
|
-
}
|
|
135
|
-
};
|
|
136
|
-
await Promise.all(Array.from({ length: Math.min(concurrency, items.length) }, worker));
|
|
137
|
-
}
|
|
138
|
-
/** HTTP statuses the S3 driver surfaces in its error message (e.g. `S3 put failed (403) for x`). */
|
|
139
|
-
function statusFromError(err) {
|
|
140
|
-
const m = /\((\d{3})\)/.exec(err?.message ?? '');
|
|
141
|
-
return m ? Number(m[1]) : undefined;
|
|
142
|
-
}
|
|
143
|
-
/**
|
|
144
|
-
* A permanent client error — bad/forbidden credentials, missing bucket, denied access (400/401/403/404).
|
|
145
|
-
* These never become successful on a later attempt, so retrying only delays the inevitable loud failure.
|
|
146
|
-
* @public
|
|
147
|
-
*/
|
|
148
|
-
export function isPermanentError(err) {
|
|
149
|
-
const s = statusFromError(err);
|
|
150
|
-
return s === 400 || s === 401 || s === 403 || s === 404;
|
|
151
|
-
}
|
|
152
|
-
/** Retry `fn` with exponential backoff up to `retries` total attempts, surfacing the last error. */
|
|
153
|
-
async function withRetry(fn, label, retries, sleep, log) {
|
|
154
|
-
for (let attempt = 1;; attempt++) {
|
|
155
|
-
try {
|
|
156
|
-
await fn();
|
|
157
|
-
return;
|
|
158
|
-
}
|
|
159
|
-
catch (err) {
|
|
160
|
-
// A permanent 4xx can't be ridden out — re-throw at once so the deploy fails loudly, not slowly.
|
|
161
|
-
if (isPermanentError(err) || attempt >= retries)
|
|
162
|
-
throw err;
|
|
163
|
-
log?.(`retry ${attempt}/${retries - 1} for ${label}: ${err.message}`);
|
|
164
|
-
await sleep(100 * 2 ** (attempt - 1));
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
/**
|
|
169
|
-
* Upload every file under `dir` to `driver`, keyed by its POSIX-relative path, with a content type
|
|
170
|
-
* inferred from the extension — the S3 equivalent of `aws s3 sync` (overwrites). Then reconciles: deletes
|
|
171
|
-
* remote objects under the prefix that this generate didn't produce (`--delete`), so pages removed from
|
|
172
|
-
* the CMS stop being served (output ≡ DB, no toggle). The reconcile is skipped when the driver can't
|
|
173
|
-
* list+delete and when the caller flagged the tree `incomplete` — a build step reported it is short of
|
|
174
|
-
* pages, so their absence says nothing about the CMS. Uploads still run then: shipping the pages that did
|
|
175
|
-
* render is safe, deleting on their absence is not.
|
|
176
|
-
* Uploads and deletes run with bounded concurrency and each is retried with
|
|
177
|
-
* backoff, so a transient 5xx/throttle/dropped-connection no longer aborts the deploy mid-way and
|
|
178
|
-
* half-updates the bucket; only an operation that fails every attempt rejects (true rollback isn't
|
|
179
|
-
* possible on S3). With `dryRun`, walks/lists and reports without writing.
|
|
180
|
-
* @public
|
|
181
|
-
*/
|
|
182
|
-
export async function deployStaticOutput(dir, driver, opts = {}) {
|
|
183
|
-
const concurrency = Math.max(1, opts.concurrency ?? 8);
|
|
184
|
-
const retries = Math.max(1, opts.retries ?? 3);
|
|
185
|
-
const sleep = opts.sleep ?? ((ms) => sleepFor(ms));
|
|
186
|
-
const all = listFiles(dir);
|
|
187
|
-
const allKeys = new Set(all.map((f) => f.key));
|
|
188
|
-
const files = all.filter((f) => !isCompressedSidecar(f.key, allKeys));
|
|
189
|
-
const skipped = all.length - files.length;
|
|
190
|
-
if (skipped)
|
|
191
|
-
opts.log?.(`skipping ${skipped} pre-compressed sidecar(s) (.gz/.br)`);
|
|
192
|
-
const keys = files.map((f) => f.key);
|
|
193
|
-
if (!opts.dryRun) {
|
|
194
|
-
// `files` already excludes pre-compressed sidecars (above), so anything ending `.gz`/`.br` here is a
|
|
195
|
-
// standalone archive shipped with no `Content-Encoding` — keep its own extension in the type lookup.
|
|
196
|
-
await runPool(files, concurrency, (f) => withRetry(() => driver.put(f.key, readFileSync(f.abs), contentTypeFor(f.key, false), { cacheControl: cacheControlFor(f.key) }), f.key, retries, sleep, opts.log));
|
|
197
|
-
}
|
|
198
|
-
opts.log?.(`${opts.dryRun ? 'Would deploy' : 'Deployed'} ${keys.length} file(s)`);
|
|
199
|
-
// Reconcile (output ≡ DB) — skipped when the driver lacks list/delete (e.g. a put-only target) and when
|
|
200
|
-
// a build step reported that this generate is short of pages.
|
|
201
|
-
let pruned = 0;
|
|
202
|
-
if (!driver.list || !driver.delete) {
|
|
203
|
-
opts.log?.('driver cannot list/delete — skipping reconcile');
|
|
204
|
-
}
|
|
205
|
-
else if (opts.incomplete) {
|
|
206
|
-
opts.log?.(`skipping reconcile — ${opts.incomplete}; the missing pages would read as stale and be deleted`);
|
|
207
|
-
}
|
|
208
|
-
else {
|
|
209
|
-
const keep = new Set(keys);
|
|
210
|
-
const stale = (await driver.list()).filter((k) => !keep.has(k));
|
|
211
|
-
if (!opts.dryRun) {
|
|
212
|
-
const del = driver.delete;
|
|
213
|
-
await runPool(stale, concurrency, (k) => withRetry(() => del(k), `delete ${k}`, retries, sleep, opts.log));
|
|
214
|
-
pruned = stale.length;
|
|
215
|
-
}
|
|
216
|
-
opts.log?.(`${opts.dryRun ? 'Would prune' : 'Pruned'} ${stale.length} stale object(s)`);
|
|
217
|
-
}
|
|
218
|
-
return { pruned, keys };
|
|
219
|
-
}
|
|
220
|
-
//# sourceMappingURL=deploy-output.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"deploy-output.js","sourceRoot":"","sources":["../../src/server/deploy-output.ts"],"names":[],"mappings":"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"}
|
package/dist/server/port.d.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import type { DeliveryPort } from '@michaelthielemann/kestrel-contracts';
|
|
2
|
-
import type { StorageDriver } from '@michaelthielemann/kestrel-core';
|
|
3
|
-
/**
|
|
4
|
-
* The static delivery adapter's `DeliveryPort` (ADR-0013 §3.3): writes/prunes files through a
|
|
5
|
-
* `StorageDriver` (local dir or S3) — the same output the runtime publisher and the build-time deploy have
|
|
6
|
-
* always written to. `publishSnapshot`/`rebuildAll` take the snapshot's `html` as-is (already fully
|
|
7
|
-
* populated, media fixed by the producer at snapshot-creation time) and never re-render or touch the DB.
|
|
8
|
-
* @public
|
|
9
|
-
*/
|
|
10
|
-
export declare function createStaticDeliveryPort(driver: StorageDriver): DeliveryPort;
|
|
11
|
-
/**
|
|
12
|
-
* The `delivery` config selection seam (`kestrel.delivery: 'static' | 'live'`, default `'static'` —
|
|
13
|
-
* see `resolveKestrel`): `'static'` returns the file-backed adapter above, `'live'` returns
|
|
14
|
-
* `delivery-live`'s adapter (serves `published_snapshots` at request time instead of writing files —
|
|
15
|
-
* see `createLiveDeliveryPort`'s own TSDoc). The runtime publisher/queue do not construct `DeliveryPort`s
|
|
16
|
-
* directly yet (they still write through `StorageDriver` in `publisher.ts`, and that keeps running under
|
|
17
|
-
* `'live'` too — see `KestrelConfig.delivery`), so nothing calls this seam in production yet.
|
|
18
|
-
* @public
|
|
19
|
-
*/
|
|
20
|
-
export declare function deliveryPortFor(delivery: 'static' | 'live', driver: StorageDriver): DeliveryPort;
|
|
21
|
-
//# sourceMappingURL=port.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"port.d.ts","sourceRoot":"","sources":["../../src/server/port.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAqB,MAAM,sCAAsC,CAAA;AAC3F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAA;AAKpE;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,aAAa,GAAG,YAAY,CAqB5E;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,EAAE,MAAM,EAAE,aAAa,GAAG,YAAY,CAGhG"}
|
package/dist/server/port.js
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { contentTypeFor, cacheControlFor } from '@michaelthielemann/kestrel-core';
|
|
2
|
-
import { createLiveDeliveryPort } from '@michaelthielemann/kestrel-delivery-live';
|
|
3
|
-
import { htmlKeyForRoute } from '@michaelthielemann/kestrel-publishing';
|
|
4
|
-
/**
|
|
5
|
-
* The static delivery adapter's `DeliveryPort` (ADR-0013 §3.3): writes/prunes files through a
|
|
6
|
-
* `StorageDriver` (local dir or S3) — the same output the runtime publisher and the build-time deploy have
|
|
7
|
-
* always written to. `publishSnapshot`/`rebuildAll` take the snapshot's `html` as-is (already fully
|
|
8
|
-
* populated, media fixed by the producer at snapshot-creation time) and never re-render or touch the DB.
|
|
9
|
-
* @public
|
|
10
|
-
*/
|
|
11
|
-
export function createStaticDeliveryPort(driver) {
|
|
12
|
-
async function writeSnapshot(snapshot) {
|
|
13
|
-
const key = htmlKeyForRoute(snapshot.route);
|
|
14
|
-
await driver.put(key, Buffer.from(snapshot.html, 'utf8'), contentTypeFor(key), { cacheControl: cacheControlFor(key) });
|
|
15
|
-
}
|
|
16
|
-
return {
|
|
17
|
-
async publishSnapshot(snapshot) {
|
|
18
|
-
await writeSnapshot(snapshot);
|
|
19
|
-
},
|
|
20
|
-
async removeRoutes(routes) {
|
|
21
|
-
for (const route of routes) {
|
|
22
|
-
await driver.delete(htmlKeyForRoute(route), { pruneEmptyDirs: true });
|
|
23
|
-
}
|
|
24
|
-
},
|
|
25
|
-
async rebuildAll(iter) {
|
|
26
|
-
for await (const snapshot of iter) {
|
|
27
|
-
await writeSnapshot(snapshot);
|
|
28
|
-
}
|
|
29
|
-
},
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* The `delivery` config selection seam (`kestrel.delivery: 'static' | 'live'`, default `'static'` —
|
|
34
|
-
* see `resolveKestrel`): `'static'` returns the file-backed adapter above, `'live'` returns
|
|
35
|
-
* `delivery-live`'s adapter (serves `published_snapshots` at request time instead of writing files —
|
|
36
|
-
* see `createLiveDeliveryPort`'s own TSDoc). The runtime publisher/queue do not construct `DeliveryPort`s
|
|
37
|
-
* directly yet (they still write through `StorageDriver` in `publisher.ts`, and that keeps running under
|
|
38
|
-
* `'live'` too — see `KestrelConfig.delivery`), so nothing calls this seam in production yet.
|
|
39
|
-
* @public
|
|
40
|
-
*/
|
|
41
|
-
export function deliveryPortFor(delivery, driver) {
|
|
42
|
-
if (delivery === 'live')
|
|
43
|
-
return createLiveDeliveryPort();
|
|
44
|
-
return createStaticDeliveryPort(driver);
|
|
45
|
-
}
|
|
46
|
-
//# sourceMappingURL=port.js.map
|
package/dist/server/port.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"port.js","sourceRoot":"","sources":["../../src/server/port.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAA;AACjF,OAAO,EAAE,sBAAsB,EAAE,MAAM,0CAA0C,CAAA;AACjF,OAAO,EAAE,eAAe,EAAE,MAAM,uCAAuC,CAAA;AAEvE;;;;;;GAMG;AACH,MAAM,UAAU,wBAAwB,CAAC,MAAqB;IAC5D,KAAK,UAAU,aAAa,CAAC,QAA2B;QACtD,MAAM,GAAG,GAAG,eAAe,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;QAC3C,MAAM,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,cAAc,CAAC,GAAG,CAAC,EAAE,EAAE,YAAY,EAAE,eAAe,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;IACxH,CAAC;IAED,OAAO;QACL,KAAK,CAAC,eAAe,CAAC,QAAQ;YAC5B,MAAM,aAAa,CAAC,QAAQ,CAAC,CAAA;QAC/B,CAAC;QACD,KAAK,CAAC,YAAY,CAAC,MAAM;YACvB,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;gBAC3B,MAAM,MAAM,CAAC,MAAM,CAAC,eAAe,CAAC,KAAK,CAAC,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,CAAA;YACvE,CAAC;QACH,CAAC;QACD,KAAK,CAAC,UAAU,CAAC,IAAI;YACnB,IAAI,KAAK,EAAE,MAAM,QAAQ,IAAI,IAAI,EAAE,CAAC;gBAClC,MAAM,aAAa,CAAC,QAAQ,CAAC,CAAA;YAC/B,CAAC;QACH,CAAC;KACF,CAAA;AACH,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,eAAe,CAAC,QAA2B,EAAE,MAAqB;IAChF,IAAI,QAAQ,KAAK,MAAM;QAAE,OAAO,sBAAsB,EAAE,CAAA;IACxD,OAAO,wBAAwB,CAAC,MAAM,CAAC,CAAA;AACzC,CAAC"}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The static delivery adapter's per-route content lookup: reads the route's current published snapshot
|
|
3
|
-
* and hands back its HTML as a body/status pair, in the same shape the producer's live render uses —
|
|
4
|
-
* so the rest of the publisher (write-to-driver, prune, meta) does not need to know which one produced it.
|
|
5
|
-
* No live server round trip, no DB populate: a route with no current snapshot (never published, or
|
|
6
|
-
* retracted — see `retractSnapshot`) is a plain 404, not an error.
|
|
7
|
-
*
|
|
8
|
-
* An intentional seam, not dead code: `publisher.ts` still writes files by rendering live and calling
|
|
9
|
-
* `driver.put` directly rather than through this function, the same not-yet-wired state `deliveryPortFor`
|
|
10
|
-
* documents for the port it belongs to.
|
|
11
|
-
* @public
|
|
12
|
-
*/
|
|
13
|
-
export declare function renderRoute(route: string): {
|
|
14
|
-
body: Buffer | null;
|
|
15
|
-
status: number;
|
|
16
|
-
};
|
|
17
|
-
//# sourceMappingURL=render-route.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"render-route.d.ts","sourceRoot":"","sources":["../../src/server/render-route.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAIlF"}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { currentSnapshot, usePublishingDb } from '@michaelthielemann/kestrel-publishing';
|
|
2
|
-
/**
|
|
3
|
-
* The static delivery adapter's per-route content lookup: reads the route's current published snapshot
|
|
4
|
-
* and hands back its HTML as a body/status pair, in the same shape the producer's live render uses —
|
|
5
|
-
* so the rest of the publisher (write-to-driver, prune, meta) does not need to know which one produced it.
|
|
6
|
-
* No live server round trip, no DB populate: a route with no current snapshot (never published, or
|
|
7
|
-
* retracted — see `retractSnapshot`) is a plain 404, not an error.
|
|
8
|
-
*
|
|
9
|
-
* An intentional seam, not dead code: `publisher.ts` still writes files by rendering live and calling
|
|
10
|
-
* `driver.put` directly rather than through this function, the same not-yet-wired state `deliveryPortFor`
|
|
11
|
-
* documents for the port it belongs to.
|
|
12
|
-
* @public
|
|
13
|
-
*/
|
|
14
|
-
export function renderRoute(route) {
|
|
15
|
-
const snapshot = currentSnapshot(usePublishingDb().db, route);
|
|
16
|
-
if (!snapshot)
|
|
17
|
-
return { body: null, status: 404 };
|
|
18
|
-
return { body: Buffer.from(snapshot.html, 'utf8'), status: 200 };
|
|
19
|
-
}
|
|
20
|
-
//# sourceMappingURL=render-route.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"render-route.js","sourceRoot":"","sources":["../../src/server/render-route.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,uCAAuC,CAAA;AAExF;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,MAAM,QAAQ,GAAG,eAAe,CAAC,eAAe,EAAE,CAAC,EAAE,EAAE,KAAK,CAAC,CAAA;IAC7D,IAAI,CAAC,QAAQ;QAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,CAAA;IACjD,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,CAAA;AAClE,CAAC"}
|