@michaelthielemann/kestrel-delivery-static 0.1.0
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/LICENSE +201 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/server/deploy-output.d.ts +139 -0
- package/dist/server/deploy-output.d.ts.map +1 -0
- package/dist/server/deploy-output.js +220 -0
- package/dist/server/deploy-output.js.map +1 -0
- package/dist/server/port.d.ts +21 -0
- package/dist/server/port.d.ts.map +1 -0
- package/dist/server/port.js +46 -0
- package/dist/server/port.js.map +1 -0
- package/dist/server/render-route.d.ts +17 -0
- package/dist/server/render-route.d.ts.map +1 -0
- package/dist/server/render-route.js +20 -0
- package/dist/server/render-route.js.map +1 -0
- package/package.json +50 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
+
Object form, made available under the License, as indicated by a
|
|
37
|
+
copyright notice that is included in or attached to the work
|
|
38
|
+
(an example is provided in the Appendix below).
|
|
39
|
+
|
|
40
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
+
form, that is based on (or derived from) the Work and for which the
|
|
42
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
+
of this License, Derivative Works shall not include works that remain
|
|
45
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
+
the Work and Derivative Works thereof.
|
|
47
|
+
|
|
48
|
+
"Contribution" shall mean any work of authorship, including
|
|
49
|
+
the original version of the Work and any modifications or additions
|
|
50
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
52
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
53
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
54
|
+
means any form of electronic, verbal, or written communication sent
|
|
55
|
+
to the Licensor or its representatives, including but not limited to
|
|
56
|
+
communication on electronic mailing lists, source code control systems,
|
|
57
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
58
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
59
|
+
excluding communication that is conspicuously marked or otherwise
|
|
60
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
61
|
+
|
|
62
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
64
|
+
subsequently incorporated within the Work.
|
|
65
|
+
|
|
66
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
68
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
70
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
71
|
+
Work and such Derivative Works in Source or Object form.
|
|
72
|
+
|
|
73
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
75
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
+
(except as stated in this section) patent license to make, have made,
|
|
77
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
+
where such license applies only to those patent claims licensable
|
|
79
|
+
by such Contributor that are necessarily infringed by their
|
|
80
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
+
institute patent litigation against any entity (including a
|
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
+
or contributory patent infringement, then any patent licenses
|
|
86
|
+
granted to You under this License for that Work shall terminate
|
|
87
|
+
as of the date such litigation is filed.
|
|
88
|
+
|
|
89
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
91
|
+
modifications, and in Source or Object form, provided that You
|
|
92
|
+
meet the following conditions:
|
|
93
|
+
|
|
94
|
+
(a) You must give any other recipients of the Work or
|
|
95
|
+
Derivative Works a copy of this License; and
|
|
96
|
+
|
|
97
|
+
(b) You must cause any modified files to carry prominent notices
|
|
98
|
+
stating that You changed the files; and
|
|
99
|
+
|
|
100
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
+
that You distribute, all copyright, patent, trademark, and
|
|
102
|
+
attribution notices from the Source form of the Work,
|
|
103
|
+
excluding those notices that do not pertain to any part of
|
|
104
|
+
the Derivative Works; and
|
|
105
|
+
|
|
106
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
+
distribution, then any Derivative Works that You distribute must
|
|
108
|
+
include a readable copy of the attribution notices contained
|
|
109
|
+
within such NOTICE file, excluding those notices that do not
|
|
110
|
+
pertain to any part of the Derivative Works, in at least one
|
|
111
|
+
of the following places: within a NOTICE text file distributed
|
|
112
|
+
as part of the Derivative Works; within the Source form or
|
|
113
|
+
documentation, if provided along with the Derivative Works; or,
|
|
114
|
+
within a display generated by the Derivative Works, if and
|
|
115
|
+
wherever such third-party notices normally appear. The contents
|
|
116
|
+
of the NOTICE file are for informational purposes only and
|
|
117
|
+
do not modify the License. You may add Your own attribution
|
|
118
|
+
notices within Derivative Works that You distribute, alongside
|
|
119
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
120
|
+
that such additional attribution notices cannot be construed
|
|
121
|
+
as modifying the License.
|
|
122
|
+
|
|
123
|
+
You may add Your own copyright statement to Your modifications and
|
|
124
|
+
may provide additional or different license terms and conditions
|
|
125
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
126
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
127
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
128
|
+
the conditions stated in this License.
|
|
129
|
+
|
|
130
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
132
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
133
|
+
this License, without any additional terms or conditions.
|
|
134
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
+
the terms of any separate license agreement you may have executed
|
|
136
|
+
with Licensor regarding such Contributions.
|
|
137
|
+
|
|
138
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
139
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
+
except as required for reasonable and customary use in describing the
|
|
141
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
142
|
+
|
|
143
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
145
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
147
|
+
implied, including, without limitation, any warranties or conditions
|
|
148
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
149
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
150
|
+
appropriateness of using or redistributing the Work and assume any
|
|
151
|
+
risks associated with Your exercise of permissions under this License.
|
|
152
|
+
|
|
153
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
155
|
+
unless required by applicable law (such as deliberate and grossly
|
|
156
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
|
+
liable to You for damages, including any direct, indirect, special,
|
|
158
|
+
incidental, or consequential damages of any character arising as a
|
|
159
|
+
result of this License or out of the use or inability to use the
|
|
160
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
161
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
162
|
+
other commercial damages or losses), even if such Contributor
|
|
163
|
+
has been advised of the possibility of such damages.
|
|
164
|
+
|
|
165
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
|
+
or other liability obligations and/or rights consistent with this
|
|
169
|
+
License. However, in accepting such obligations, You may act only
|
|
170
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
171
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
172
|
+
defend, and hold each Contributor harmless for any liability
|
|
173
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
+
of your accepting any such warranty or additional liability.
|
|
175
|
+
|
|
176
|
+
END OF TERMS AND CONDITIONS
|
|
177
|
+
|
|
178
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
179
|
+
|
|
180
|
+
To apply the Apache License to your work, attach the following
|
|
181
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
182
|
+
replaced with your own identifying information. (Don't include
|
|
183
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
184
|
+
comment syntax for the file format. We also recommend that a
|
|
185
|
+
file or class name and description of purpose be included on the
|
|
186
|
+
same "printed page" as the copyright notice for easier
|
|
187
|
+
identification within third-party archives.
|
|
188
|
+
|
|
189
|
+
Copyright [yyyy] [name of copyright owner]
|
|
190
|
+
|
|
191
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
|
+
you may not use this file except in compliance with the License.
|
|
193
|
+
You may obtain a copy of the License at
|
|
194
|
+
|
|
195
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
196
|
+
|
|
197
|
+
Unless required by applicable law or agreed to in writing, software
|
|
198
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
199
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
200
|
+
See the License for the specific language governing permissions and
|
|
201
|
+
limitations under the License.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
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
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
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"}
|
|
@@ -0,0 +1,139 @@
|
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deploy-output.d.ts","sourceRoot":"","sources":["../../src/server/deploy-output.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,220 @@
|
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deploy-output.js","sourceRoot":"","sources":["../../src/server/deploy-output.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@michaelthielemann/kestrel-delivery-static",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "The static delivery adapter: writes/prunes published snapshots through a StorageDriver (the build-time deploy target), plus the S3 static-output shipper.",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"author": "Michael Thielemann <283621694+MichaelThielemann@users.noreply.github.com>",
|
|
7
|
+
"homepage": "https://github.com/MichaelThielemann/kestrel#readme",
|
|
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
|
|
17
|
+
},
|
|
18
|
+
"type": "module",
|
|
19
|
+
"main": "./dist/index.js",
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"types": "./dist/index.d.ts",
|
|
24
|
+
"import": "./dist/index.js"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"sideEffects": false,
|
|
28
|
+
"files": [
|
|
29
|
+
"dist"
|
|
30
|
+
],
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=22.12.0"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@michaelthielemann/kestrel-contracts": "0.1.0",
|
|
36
|
+
"@michaelthielemann/kestrel-publishing": "0.1.0",
|
|
37
|
+
"@michaelthielemann/kestrel-core": "0.1.0",
|
|
38
|
+
"@michaelthielemann/kestrel-delivery-live": "0.1.0"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@types/node": "^26.2.0",
|
|
42
|
+
"typescript": "^6.0.3",
|
|
43
|
+
"vitest": "^4.1.10"
|
|
44
|
+
},
|
|
45
|
+
"scripts": {
|
|
46
|
+
"build": "tsc -p tsconfig.json",
|
|
47
|
+
"typecheck": "tsc --noEmit -p tsconfig.typecheck.json",
|
|
48
|
+
"test": "vitest run --typecheck"
|
|
49
|
+
}
|
|
50
|
+
}
|