@astroscope/node 1.0.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 +21 -0
- package/README.md +286 -0
- package/dist/boot.d.ts +15 -0
- package/dist/boot.d.ts.map +1 -0
- package/dist/boot.js +2 -0
- package/dist/compress-B11aeRHQ.js +58 -0
- package/dist/compress-B11aeRHQ.js.map +1 -0
- package/dist/construct-DgB-jR0a.d.ts +14 -0
- package/dist/construct-DgB-jR0a.d.ts.map +1 -0
- package/dist/context-Bkg-FnnQ.js +20 -0
- package/dist/context-Bkg-FnnQ.js.map +1 -0
- package/dist/csrf-middleware-entrypoint.d.ts +5 -0
- package/dist/csrf-middleware-entrypoint.d.ts.map +1 -0
- package/dist/csrf-middleware-entrypoint.js +30 -0
- package/dist/csrf-middleware-entrypoint.js.map +1 -0
- package/dist/dev-middleware-entrypoint.d.ts +15 -0
- package/dist/dev-middleware-entrypoint.d.ts.map +1 -0
- package/dist/dev-middleware-entrypoint.js +26 -0
- package/dist/dev-middleware-entrypoint.js.map +1 -0
- package/dist/events-CUzQ2_cp.d.ts +20 -0
- package/dist/events-CUzQ2_cp.d.ts.map +1 -0
- package/dist/events-CgoM3Fvu.js +39 -0
- package/dist/events-CgoM3Fvu.js.map +1 -0
- package/dist/excludes-DLF3A_Cf.d.ts +63 -0
- package/dist/excludes-DLF3A_Cf.d.ts.map +1 -0
- package/dist/excludes-pE23EbmQ.js +78 -0
- package/dist/excludes-pE23EbmQ.js.map +1 -0
- package/dist/excludes.d.ts +2 -0
- package/dist/excludes.js +2 -0
- package/dist/generation-Bp2IA0jf.js +45 -0
- package/dist/generation-Bp2IA0jf.js.map +1 -0
- package/dist/health.d.ts +38 -0
- package/dist/health.d.ts.map +1 -0
- package/dist/health.js +31 -0
- package/dist/health.js.map +1 -0
- package/dist/index.d.ts +167 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +732 -0
- package/dist/index.js.map +1 -0
- package/dist/lifecycle/events.d.ts +2 -0
- package/dist/lifecycle/events.js +2 -0
- package/dist/log/index.d.ts +55 -0
- package/dist/log/index.d.ts.map +1 -0
- package/dist/log/index.js +2 -0
- package/dist/log-CSJlKaxY.js +112 -0
- package/dist/log-CSJlKaxY.js.map +1 -0
- package/dist/mime-C_GwZovh.js +39 -0
- package/dist/mime-C_GwZovh.js.map +1 -0
- package/dist/native-mount-hhwWdLtL.js +120 -0
- package/dist/native-mount-hhwWdLtL.js.map +1 -0
- package/dist/native.d.ts +48 -0
- package/dist/native.d.ts.map +1 -0
- package/dist/native.js +2 -0
- package/dist/prepare-Be2GgRyf.js +390 -0
- package/dist/prepare-Be2GgRyf.js.map +1 -0
- package/dist/preview.d.ts +12 -0
- package/dist/preview.d.ts.map +1 -0
- package/dist/preview.js +23 -0
- package/dist/preview.js.map +1 -0
- package/dist/restart-page.html +107 -0
- package/dist/route-middleware-entrypoint.d.ts +13 -0
- package/dist/route-middleware-entrypoint.d.ts.map +1 -0
- package/dist/route-middleware-entrypoint.js +24 -0
- package/dist/route-middleware-entrypoint.js.map +1 -0
- package/dist/server.d.ts +14 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +441 -0
- package/dist/server.js.map +1 -0
- package/dist/store-8pnTxM1x.js +37 -0
- package/dist/store-8pnTxM1x.js.map +1 -0
- package/dist/store-BIUF4lqk.js +29 -0
- package/dist/store-BIUF4lqk.js.map +1 -0
- package/dist/types-D0uMBi2M.d.ts +12 -0
- package/dist/types-D0uMBi2M.d.ts.map +1 -0
- package/package.json +114 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,732 @@
|
|
|
1
|
+
import { n as setBootContext } from "./context-Bkg-FnnQ.js";
|
|
2
|
+
import { a as runShutdown, i as createRequestInstrumentation, o as runStartup, t as preparePlatform } from "./prepare-Be2GgRyf.js";
|
|
3
|
+
import { n as dispatchNativeMount, t as clearNativeMounts } from "./native-mount-hhwWdLtL.js";
|
|
4
|
+
import { n as getCurrentGeneration, r as incrementGeneration, t as GEN_HEADER } from "./generation-Bp2IA0jf.js";
|
|
5
|
+
import { i as STATIC_EXCLUDES, r as RECOMMENDED_EXCLUDES } from "./excludes-pE23EbmQ.js";
|
|
6
|
+
import fs, { readFileSync } from "node:fs";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import { fileURLToPath } from "node:url";
|
|
9
|
+
import { Parser } from "acorn";
|
|
10
|
+
import MagicString from "magic-string";
|
|
11
|
+
//#region src/dev-mode/serialize-error.ts
|
|
12
|
+
function serializeError(error) {
|
|
13
|
+
if (error instanceof Error) return error.stack ?? error.message;
|
|
14
|
+
return JSON.stringify(error);
|
|
15
|
+
}
|
|
16
|
+
//#endregion
|
|
17
|
+
//#region src/dev-mode/scheduler.ts
|
|
18
|
+
const FULL_RELOAD_UNKNOWN = "<unknown>";
|
|
19
|
+
/**
|
|
20
|
+
* Coordinates dev-server restarts: debounces bursts, chains a follow-up if
|
|
21
|
+
* changes arrive mid-restart (vite's `ssrImport` reads disk at import time,
|
|
22
|
+
* so changes during a restart would otherwise be missed), and logs once per
|
|
23
|
+
* restart with what triggered it.
|
|
24
|
+
*
|
|
25
|
+
* One instance per integration, shared across restart-induced configureServer
|
|
26
|
+
* reruns — chain coordination would be lost if recreated each time.
|
|
27
|
+
*/
|
|
28
|
+
var RestartScheduler = class {
|
|
29
|
+
debounceMs;
|
|
30
|
+
logger;
|
|
31
|
+
_inFlight = false;
|
|
32
|
+
_pending = false;
|
|
33
|
+
_debounceTimer;
|
|
34
|
+
_pendingBootDeps = /* @__PURE__ */ new Set();
|
|
35
|
+
_pendingFullReloads = /* @__PURE__ */ new Set();
|
|
36
|
+
_runPromise;
|
|
37
|
+
_lastFailure;
|
|
38
|
+
constructor(debounceMs, logger) {
|
|
39
|
+
this.debounceMs = debounceMs;
|
|
40
|
+
this.logger = logger;
|
|
41
|
+
}
|
|
42
|
+
isRestartPending() {
|
|
43
|
+
return !!this._runPromise || !!this._debounceTimer || !!this._lastFailure;
|
|
44
|
+
}
|
|
45
|
+
getLastFailure() {
|
|
46
|
+
return this._lastFailure;
|
|
47
|
+
}
|
|
48
|
+
recordFailure(message) {
|
|
49
|
+
this._lastFailure = { message };
|
|
50
|
+
}
|
|
51
|
+
clearFailure() {
|
|
52
|
+
this._lastFailure = void 0;
|
|
53
|
+
}
|
|
54
|
+
schedule(server, changedPath) {
|
|
55
|
+
this._pendingBootDeps.add(changedPath);
|
|
56
|
+
this._scheduleRun(server);
|
|
57
|
+
}
|
|
58
|
+
scheduleFullReload(server, triggeredBy) {
|
|
59
|
+
this._pendingFullReloads.add(triggeredBy ?? FULL_RELOAD_UNKNOWN);
|
|
60
|
+
this._scheduleRun(server);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Resolves when no restart is running. Loops to handle back-to-back restarts
|
|
64
|
+
* (e.g. one chain ends and a queued debounce timer immediately fires a new
|
|
65
|
+
* one). Never rejects, so a failed restart still releases the gate — caller
|
|
66
|
+
* proceeds against the broken state rather than hanging forever.
|
|
67
|
+
*/
|
|
68
|
+
async waitForRestart() {
|
|
69
|
+
while (this._runPromise) try {
|
|
70
|
+
await this._runPromise;
|
|
71
|
+
} catch {}
|
|
72
|
+
}
|
|
73
|
+
_scheduleRun(server) {
|
|
74
|
+
clearTimeout(this._debounceTimer);
|
|
75
|
+
this._debounceTimer = setTimeout(() => {
|
|
76
|
+
this._debounceTimer = void 0;
|
|
77
|
+
this._run(server);
|
|
78
|
+
}, this.debounceMs);
|
|
79
|
+
}
|
|
80
|
+
async _run(server) {
|
|
81
|
+
if (this._inFlight) {
|
|
82
|
+
this._pending = true;
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
this._inFlight = true;
|
|
86
|
+
let resolveRun;
|
|
87
|
+
this._runPromise = new Promise((resolve) => {
|
|
88
|
+
resolveRun = resolve;
|
|
89
|
+
});
|
|
90
|
+
try {
|
|
91
|
+
do {
|
|
92
|
+
this._pending = false;
|
|
93
|
+
const bootDeps = [...this._pendingBootDeps].sort();
|
|
94
|
+
const fullReloads = [...this._pendingFullReloads].sort();
|
|
95
|
+
this._pendingBootDeps.clear();
|
|
96
|
+
this._pendingFullReloads.clear();
|
|
97
|
+
if (bootDeps.length > 0 || fullReloads.length > 0) this.logger.info(this._formatReason(server, bootDeps, fullReloads));
|
|
98
|
+
try {
|
|
99
|
+
await server.restart();
|
|
100
|
+
} catch (error) {
|
|
101
|
+
this.logger.error(`error during dev server restart: ${serializeError(error)}`);
|
|
102
|
+
}
|
|
103
|
+
} while (this._pending);
|
|
104
|
+
} finally {
|
|
105
|
+
this._inFlight = false;
|
|
106
|
+
this._runPromise = void 0;
|
|
107
|
+
resolveRun();
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
_formatReason(server, bootDeps, fullReloads) {
|
|
111
|
+
const root = server.config.root;
|
|
112
|
+
const rel = (p) => path.relative(root, p) || p;
|
|
113
|
+
const parts = [];
|
|
114
|
+
if (bootDeps.length === 1) parts.push(`boot dep changed: ${rel(bootDeps[0])}`);
|
|
115
|
+
else if (bootDeps.length > 1) parts.push(`boot deps changed (${bootDeps.length}): ${bootDeps.map(rel).join(", ")}`);
|
|
116
|
+
if (fullReloads.length > 0) {
|
|
117
|
+
const named = fullReloads.filter((p) => p !== FULL_RELOAD_UNKNOWN);
|
|
118
|
+
if (named.length === 0) parts.push("vite SSR full-reload");
|
|
119
|
+
else parts.push(`vite SSR full-reload (triggered by ${named.map(rel).join(", ")})`);
|
|
120
|
+
}
|
|
121
|
+
return `${parts.join(" + ")} — restarting dev server`;
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
//#endregion
|
|
125
|
+
//#region src/dev-mode/vite-env.ts
|
|
126
|
+
/**
|
|
127
|
+
* duck-typed runnable-env check. avoids `isRunnableDevEnvironment` which relies on
|
|
128
|
+
* `instanceof RunnableDevEnvironment` — that breaks when the consumer and vite host
|
|
129
|
+
* resolve to different vite installs (e.g. linked packages outside the workspace).
|
|
130
|
+
*/
|
|
131
|
+
function isRunnable(env) {
|
|
132
|
+
return !!env && typeof env.runner === "object" && typeof env.runner.import === "function";
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* resolve a runnable dev environment — prefers `ssr`, falls back to `astro`.
|
|
136
|
+
* astro 6 exposes a separate `astro` environment when `ssr` isn't runnable
|
|
137
|
+
* (see astro/core/constants.ts ASTRO_VITE_ENVIRONMENT_NAMES).
|
|
138
|
+
*/
|
|
139
|
+
function getRunnableEnv(server) {
|
|
140
|
+
const ssr = server.environments["ssr"];
|
|
141
|
+
if (isRunnable(ssr)) return ssr;
|
|
142
|
+
const astro = server.environments["astro"];
|
|
143
|
+
if (isRunnable(astro)) return astro;
|
|
144
|
+
const names = Object.keys(server.environments);
|
|
145
|
+
throw new Error(`no runnable dev environment found — available: ${names.join(", ")}`);
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* load a module via the Vite Environment API.
|
|
149
|
+
*/
|
|
150
|
+
async function ssrImport(server, moduleId) {
|
|
151
|
+
return getRunnableEnv(server).runner.import(moduleId);
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* return the env whose `hot` channel should receive astro-targeted events.
|
|
155
|
+
* astro listens on the env that backs its middleware runner.
|
|
156
|
+
*/
|
|
157
|
+
function getAstroHotEnv(server) {
|
|
158
|
+
const ssr = server.environments["ssr"];
|
|
159
|
+
if (isRunnable(ssr)) return ssr;
|
|
160
|
+
const astro = server.environments["astro"];
|
|
161
|
+
if (isRunnable(astro)) return astro;
|
|
162
|
+
}
|
|
163
|
+
//#endregion
|
|
164
|
+
//#region src/dev-mode/ignored.ts
|
|
165
|
+
const ignoredSuffixes = [
|
|
166
|
+
".d.ts",
|
|
167
|
+
".d.mts",
|
|
168
|
+
".d.cts",
|
|
169
|
+
".png",
|
|
170
|
+
".jpg",
|
|
171
|
+
".jpeg",
|
|
172
|
+
".gif",
|
|
173
|
+
".svg",
|
|
174
|
+
".webp",
|
|
175
|
+
".avif",
|
|
176
|
+
".ico",
|
|
177
|
+
".woff",
|
|
178
|
+
".woff2",
|
|
179
|
+
".ttf",
|
|
180
|
+
".otf",
|
|
181
|
+
".eot",
|
|
182
|
+
".pdf",
|
|
183
|
+
".mp3",
|
|
184
|
+
".mp4",
|
|
185
|
+
".webm",
|
|
186
|
+
".ogg",
|
|
187
|
+
".wav",
|
|
188
|
+
".json",
|
|
189
|
+
".yaml",
|
|
190
|
+
".yml",
|
|
191
|
+
".toml",
|
|
192
|
+
".md",
|
|
193
|
+
".mdx",
|
|
194
|
+
".txt",
|
|
195
|
+
".css",
|
|
196
|
+
".scss",
|
|
197
|
+
".sass",
|
|
198
|
+
".less"
|
|
199
|
+
];
|
|
200
|
+
//#endregion
|
|
201
|
+
//#region src/dev-mode/watch.ts
|
|
202
|
+
const RESTART_HTML = readFileSync(fileURLToPath(new URL("./restart-page.html", import.meta.url)), "utf8");
|
|
203
|
+
function setupBootWatch(server, entries, scheduler) {
|
|
204
|
+
const entryFilePaths = entries.map((entry) => path.resolve(server.config.root, entry));
|
|
205
|
+
const runnableEnv = getAstroHotEnv(server);
|
|
206
|
+
const bootModuleGraph = runnableEnv?.moduleGraph;
|
|
207
|
+
const collectBootDependencies = () => {
|
|
208
|
+
const deps = /* @__PURE__ */ new Set();
|
|
209
|
+
const seen = /* @__PURE__ */ new Set();
|
|
210
|
+
for (const entryFilePath of entryFilePaths) {
|
|
211
|
+
const entryModules = bootModuleGraph?.getModulesByFile(entryFilePath);
|
|
212
|
+
const entryModule = entryModules ? [...entryModules][0] : void 0;
|
|
213
|
+
if (!entryModule) continue;
|
|
214
|
+
const visit = (mod) => {
|
|
215
|
+
if (!mod?.file || seen.has(mod.file)) return;
|
|
216
|
+
seen.add(mod.file);
|
|
217
|
+
deps.add(mod.file);
|
|
218
|
+
for (const imp of mod.importedModules) visit(imp);
|
|
219
|
+
};
|
|
220
|
+
visit(entryModule);
|
|
221
|
+
}
|
|
222
|
+
return deps;
|
|
223
|
+
};
|
|
224
|
+
const shouldIgnore = (filePath) => {
|
|
225
|
+
const p = filePath.toLowerCase();
|
|
226
|
+
return ignoredSuffixes.some((suffix) => p.endsWith(suffix));
|
|
227
|
+
};
|
|
228
|
+
const onWatcherEvent = (changedPath) => {
|
|
229
|
+
if (shouldIgnore(changedPath)) return;
|
|
230
|
+
if (!collectBootDependencies().has(changedPath)) return;
|
|
231
|
+
scheduler.schedule(server, changedPath);
|
|
232
|
+
};
|
|
233
|
+
server.watcher.on("change", onWatcherEvent);
|
|
234
|
+
server.watcher.on("add", onWatcherEvent);
|
|
235
|
+
server.watcher.on("unlink", onWatcherEvent);
|
|
236
|
+
let handleFullReloads = false;
|
|
237
|
+
if (server.httpServer) server.httpServer.once("listening", () => {
|
|
238
|
+
handleFullReloads = true;
|
|
239
|
+
});
|
|
240
|
+
else handleFullReloads = true;
|
|
241
|
+
const outsideEmitter = (runnableEnv?.hot)?.api?.outsideEmitter;
|
|
242
|
+
if (outsideEmitter) outsideEmitter.on("send", (payload) => {
|
|
243
|
+
if (!handleFullReloads) return;
|
|
244
|
+
if (payload.type !== "full-reload") return;
|
|
245
|
+
const triggeredBy = "triggeredBy" in payload ? payload.triggeredBy : void 0;
|
|
246
|
+
scheduler.scheduleFullReload(server, triggeredBy);
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
const READINESS_PATH = "/__astroscope_boot_ready";
|
|
250
|
+
/**
|
|
251
|
+
* Gate requests: show 503 holding page during restart, readiness probe for the reload.
|
|
252
|
+
*/
|
|
253
|
+
function installBootGate(server, scheduler) {
|
|
254
|
+
server.middlewares.stack.unshift({
|
|
255
|
+
route: "",
|
|
256
|
+
handle: (async (req, res, next) => {
|
|
257
|
+
if (req.url === READINESS_PATH) {
|
|
258
|
+
await scheduler.waitForRestart();
|
|
259
|
+
if (res.headersSent) return;
|
|
260
|
+
const failure = scheduler.getLastFailure();
|
|
261
|
+
if (failure) {
|
|
262
|
+
res.writeHead(503, {
|
|
263
|
+
"cache-control": "no-store",
|
|
264
|
+
"content-type": "application/json"
|
|
265
|
+
});
|
|
266
|
+
res.end(JSON.stringify({ error: failure.message }));
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
res.writeHead(204, { "cache-control": "no-store" });
|
|
270
|
+
res.end();
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
if (isDevInternalPath(req.url)) {
|
|
274
|
+
next();
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
if (scheduler.isRestartPending()) {
|
|
278
|
+
res.writeHead(503, {
|
|
279
|
+
"content-type": "text/html; charset=utf-8",
|
|
280
|
+
"cache-control": "no-store",
|
|
281
|
+
"retry-after": "1"
|
|
282
|
+
});
|
|
283
|
+
res.end(RESTART_HTML);
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
next();
|
|
287
|
+
})
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
function isDevInternalPath(url) {
|
|
291
|
+
if (!url) return false;
|
|
292
|
+
return url.startsWith("/@") || url.startsWith("/__") || url.includes("/node_modules/");
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Stamp the current generation onto every incoming request so the runtime
|
|
296
|
+
* Astro middleware can later detect whether the request belongs to a previous
|
|
297
|
+
* (now torn-down) generation
|
|
298
|
+
*/
|
|
299
|
+
function installGenStamp(server) {
|
|
300
|
+
server.middlewares.stack.unshift({
|
|
301
|
+
route: "",
|
|
302
|
+
handle: ((req, _res, next) => {
|
|
303
|
+
req.headers[GEN_HEADER] = String(getCurrentGeneration());
|
|
304
|
+
next();
|
|
305
|
+
})
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
//#endregion
|
|
309
|
+
//#region src/dev-mode/machinery.ts
|
|
310
|
+
/**
|
|
311
|
+
* Resolve the default host and port from the Astro server config.
|
|
312
|
+
* Falls back to `localhost:4321` when no config is provided.
|
|
313
|
+
*/
|
|
314
|
+
function getServerDefaults(config) {
|
|
315
|
+
return {
|
|
316
|
+
host: typeof config?.server?.host === "string" ? config.server.host : config?.server?.host === true ? "0.0.0.0" : "localhost",
|
|
317
|
+
port: config?.server?.port ?? 4321
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Build a dev-mode boot context from the running server's address,
|
|
322
|
+
* falling back to Astro config defaults if the server isn't listening yet.
|
|
323
|
+
*/
|
|
324
|
+
function resolveBootContext(server, config) {
|
|
325
|
+
const addr = server.httpServer?.address();
|
|
326
|
+
if (addr && typeof addr === "object" && "address" in addr && "port" in addr) return {
|
|
327
|
+
dev: true,
|
|
328
|
+
host: addr.address === "::" || addr.address === "0.0.0.0" ? "localhost" : addr.address,
|
|
329
|
+
port: addr.port
|
|
330
|
+
};
|
|
331
|
+
const { host, port } = getServerDefaults(config);
|
|
332
|
+
return {
|
|
333
|
+
dev: true,
|
|
334
|
+
host,
|
|
335
|
+
port
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* The dev-mode boot machinery: runs the platform seams and the boot lifecycle
|
|
340
|
+
* per restart generation, restarts the dev server when watched dependencies
|
|
341
|
+
* change, and gates requests behind a holding page during restarts.
|
|
342
|
+
*/
|
|
343
|
+
function createDevMachinery(options) {
|
|
344
|
+
const { entry, logger } = options;
|
|
345
|
+
let hasStartupSucceededOnce = false;
|
|
346
|
+
let priorShutdown;
|
|
347
|
+
const scheduler = options.watch ? new RestartScheduler(100, logger) : void 0;
|
|
348
|
+
return [{
|
|
349
|
+
name: "@astroscope/node/dev-gate",
|
|
350
|
+
enforce: "post",
|
|
351
|
+
configureServer(server) {
|
|
352
|
+
if (!scheduler) return;
|
|
353
|
+
return () => {
|
|
354
|
+
installBootGate(server, scheduler);
|
|
355
|
+
installGenStamp(server);
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
}, {
|
|
359
|
+
name: "@astroscope/node/dev-startup",
|
|
360
|
+
enforce: "post",
|
|
361
|
+
async configureServer(server) {
|
|
362
|
+
incrementGeneration();
|
|
363
|
+
if (priorShutdown) {
|
|
364
|
+
await priorShutdown();
|
|
365
|
+
priorShutdown = void 0;
|
|
366
|
+
}
|
|
367
|
+
const bootContext = resolveBootContext(server, options.getConfig());
|
|
368
|
+
let bootModule;
|
|
369
|
+
setBootContext(bootContext);
|
|
370
|
+
try {
|
|
371
|
+
await options.prepare((id) => ssrImport(server, id));
|
|
372
|
+
bootModule = entry ? await ssrImport(server, `/${entry}`) : {};
|
|
373
|
+
await runStartup(bootModule, bootContext);
|
|
374
|
+
} catch (error) {
|
|
375
|
+
logger.error(`Error running startup script: ${serializeError(error)}`);
|
|
376
|
+
if (bootModule) try {
|
|
377
|
+
await runShutdown(bootModule, bootContext);
|
|
378
|
+
} catch {}
|
|
379
|
+
if (hasStartupSucceededOnce) {
|
|
380
|
+
scheduler?.recordFailure(serializeError(error));
|
|
381
|
+
throw error;
|
|
382
|
+
}
|
|
383
|
+
process.exit(1);
|
|
384
|
+
}
|
|
385
|
+
hasStartupSucceededOnce = true;
|
|
386
|
+
scheduler?.clearFailure();
|
|
387
|
+
const startedModule = bootModule;
|
|
388
|
+
let shutdownDone = false;
|
|
389
|
+
const shutdown = async () => {
|
|
390
|
+
if (shutdownDone) return;
|
|
391
|
+
shutdownDone = true;
|
|
392
|
+
try {
|
|
393
|
+
await runShutdown(startedModule, resolveBootContext(server, options.getConfig()));
|
|
394
|
+
} catch (error) {
|
|
395
|
+
logger.error(`Error running shutdown script: ${serializeError(error)}`);
|
|
396
|
+
}
|
|
397
|
+
clearNativeMounts();
|
|
398
|
+
};
|
|
399
|
+
priorShutdown = shutdown;
|
|
400
|
+
server.httpServer?.once("close", () => {
|
|
401
|
+
shutdown();
|
|
402
|
+
});
|
|
403
|
+
if (scheduler) setupBootWatch(server, [...entry ? [entry] : [], ...options.watchEntries], scheduler);
|
|
404
|
+
}
|
|
405
|
+
}];
|
|
406
|
+
}
|
|
407
|
+
//#endregion
|
|
408
|
+
//#region src/excludes/serialize.ts
|
|
409
|
+
/**
|
|
410
|
+
* Serialize exclude patterns to JavaScript code for use in virtual modules.
|
|
411
|
+
* Handles RegExp objects which JSON.stringify cannot serialize.
|
|
412
|
+
*/
|
|
413
|
+
function serializeExcludePatterns(patterns) {
|
|
414
|
+
return `[${patterns.map((p) => "pattern" in p ? `{ pattern: ${p.pattern.toString()} }` : JSON.stringify(p)).join(", ")}]`;
|
|
415
|
+
}
|
|
416
|
+
//#endregion
|
|
417
|
+
//#region src/tweaks/sourcemap.ts
|
|
418
|
+
/**
|
|
419
|
+
* emit sourcemaps only for the SSR build. client bundles are left unmapped
|
|
420
|
+
* so browsers can't fetch source via `//# sourceMappingURL=`.
|
|
421
|
+
*
|
|
422
|
+
* vite 7's `environments.ssr.build.sourcemap` looks cleaner but wholesale
|
|
423
|
+
* replaces astro's SSR build defaults (including entry file naming), which
|
|
424
|
+
* breaks the embedded dev boot machinery. using `isSsrBuild` keeps the
|
|
425
|
+
* rest of astro's SSR config intact.
|
|
426
|
+
*/
|
|
427
|
+
function ssrSourcemapPlugin() {
|
|
428
|
+
return {
|
|
429
|
+
name: "@astroscope/node/tweaks/sourcemap",
|
|
430
|
+
config(_config, { isSsrBuild }) {
|
|
431
|
+
if (isSsrBuild) return { build: { sourcemap: true } };
|
|
432
|
+
return {};
|
|
433
|
+
}
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
//#endregion
|
|
437
|
+
//#region src/tweaks/strip-effects.ts
|
|
438
|
+
const HOOK_NAMES = /* @__PURE__ */ new Set([
|
|
439
|
+
"useEffect",
|
|
440
|
+
"useLayoutEffect",
|
|
441
|
+
"useInsertionEffect"
|
|
442
|
+
]);
|
|
443
|
+
const REACT_SOURCE = /^react(\/.*)?$/;
|
|
444
|
+
const TRANSFORMABLE = /\.(?:[mc]?[jt]sx?)$/;
|
|
445
|
+
const EMPTY_FN = "(()=>{})";
|
|
446
|
+
/**
|
|
447
|
+
* in SSR builds, react effect hooks never execute. emptying their callbacks
|
|
448
|
+
* lets rolldown drop dead branches — including dynamic imports of client-only
|
|
449
|
+
* libs (maplibre-gl, hls.js, etc.) — from the server bundle, which in turn
|
|
450
|
+
* stops nft from tracing them at docker-image time.
|
|
451
|
+
*
|
|
452
|
+
* scope is deliberately narrow: first-party code only (no node_modules), only
|
|
453
|
+
* in the SSR pass, and binding-aware (the React import must resolve to the
|
|
454
|
+
* real react package). raw chunks where bundling has erased the binding are
|
|
455
|
+
* left alone — that's NFT's domain, not ours.
|
|
456
|
+
*/
|
|
457
|
+
function stripSsrEffectsPlugin() {
|
|
458
|
+
return {
|
|
459
|
+
name: "@astroscope/node/tweaks/strip-effects",
|
|
460
|
+
enforce: "post",
|
|
461
|
+
transform(code, id, options) {
|
|
462
|
+
if (!options?.ssr) return null;
|
|
463
|
+
if (id.includes("/node_modules/")) return null;
|
|
464
|
+
const cleanId = id.split("?")[0] ?? id;
|
|
465
|
+
if (!TRANSFORMABLE.test(cleanId)) return null;
|
|
466
|
+
if (!code.includes("useEffect") && !code.includes("useLayoutEffect") && !code.includes("useInsertionEffect")) return null;
|
|
467
|
+
let ast;
|
|
468
|
+
try {
|
|
469
|
+
ast = Parser.parse(code, {
|
|
470
|
+
ecmaVersion: "latest",
|
|
471
|
+
sourceType: "module",
|
|
472
|
+
allowReturnOutsideFunction: true,
|
|
473
|
+
allowAwaitOutsideFunction: true,
|
|
474
|
+
allowImportExportEverywhere: true,
|
|
475
|
+
allowHashBang: true
|
|
476
|
+
});
|
|
477
|
+
} catch {
|
|
478
|
+
return null;
|
|
479
|
+
}
|
|
480
|
+
const directHooks = /* @__PURE__ */ new Set();
|
|
481
|
+
const namespaceHooks = /* @__PURE__ */ new Set();
|
|
482
|
+
walk(ast, (node) => {
|
|
483
|
+
if (node.type !== "ImportDeclaration") return;
|
|
484
|
+
const src = node.source?.value;
|
|
485
|
+
if (typeof src !== "string" || !REACT_SOURCE.test(src)) return;
|
|
486
|
+
for (const spec of node.specifiers ?? []) if (spec.type === "ImportSpecifier") {
|
|
487
|
+
const imported = spec.imported?.name ?? spec.imported?.value;
|
|
488
|
+
if (HOOK_NAMES.has(imported)) directHooks.add(spec.local.name);
|
|
489
|
+
} else if (spec.type === "ImportDefaultSpecifier" || spec.type === "ImportNamespaceSpecifier") namespaceHooks.add(spec.local.name);
|
|
490
|
+
});
|
|
491
|
+
if (directHooks.size === 0 && namespaceHooks.size === 0) return null;
|
|
492
|
+
const replacements = [];
|
|
493
|
+
walk(ast, (node) => {
|
|
494
|
+
if (node.type !== "CallExpression" || !node.arguments?.length) return;
|
|
495
|
+
const callee = node.callee;
|
|
496
|
+
const isDirect = callee.type === "Identifier" && directHooks.has(callee.name);
|
|
497
|
+
const isMember = callee.type === "MemberExpression" && !callee.computed && callee.object?.type === "Identifier" && namespaceHooks.has(callee.object.name) && callee.property?.type === "Identifier" && HOOK_NAMES.has(callee.property.name);
|
|
498
|
+
if (!isDirect && !isMember) return;
|
|
499
|
+
const arg = node.arguments[0];
|
|
500
|
+
replacements.push({
|
|
501
|
+
start: arg.start,
|
|
502
|
+
end: arg.end
|
|
503
|
+
});
|
|
504
|
+
});
|
|
505
|
+
if (replacements.length === 0) return null;
|
|
506
|
+
const s = new MagicString(code);
|
|
507
|
+
for (const { start, end } of replacements) s.overwrite(start, end, EMPTY_FN);
|
|
508
|
+
return {
|
|
509
|
+
code: s.toString(),
|
|
510
|
+
map: s.generateMap({ hires: true })
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
function walk(node, visit) {
|
|
516
|
+
if (!node || typeof node !== "object") return;
|
|
517
|
+
if (typeof node.type === "string") visit(node);
|
|
518
|
+
for (const key of Object.keys(node)) {
|
|
519
|
+
const v = node[key];
|
|
520
|
+
if (Array.isArray(v)) for (const item of v) walk(item, visit);
|
|
521
|
+
else if (v && typeof v === "object") walk(v, visit);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
//#endregion
|
|
525
|
+
//#region src/integration/integration.ts
|
|
526
|
+
const CONFIG_VIRTUAL_MODULE_ID = "virtual:@astroscope/node/config";
|
|
527
|
+
const BOOT_VIRTUAL_MODULE_ID = "virtual:@astroscope/node/boot";
|
|
528
|
+
const CSRF_VIRTUAL_MODULE_ID = "virtual:@astroscope/node/csrf";
|
|
529
|
+
const CONFIG_ENTRY_VIRTUAL_MODULE_ID = "virtual:@astroscope/node/config-entry";
|
|
530
|
+
const INSTRUMENTATION_ENTRY_VIRTUAL_MODULE_ID = "virtual:@astroscope/node/instrumentation-entry";
|
|
531
|
+
const LOG_ENTRY_VIRTUAL_MODULE_ID = "virtual:@astroscope/node/log-entry";
|
|
532
|
+
const RESOLVED_CONFIG_VIRTUAL_MODULE_ID = `\0${CONFIG_VIRTUAL_MODULE_ID}`;
|
|
533
|
+
const RESOLVED_BOOT_VIRTUAL_MODULE_ID = `\0${BOOT_VIRTUAL_MODULE_ID}`;
|
|
534
|
+
const RESOLVED_CSRF_VIRTUAL_MODULE_ID = `\0${CSRF_VIRTUAL_MODULE_ID}`;
|
|
535
|
+
const RESOLVED_CONFIG_ENTRY_VIRTUAL_MODULE_ID = `\0${CONFIG_ENTRY_VIRTUAL_MODULE_ID}`;
|
|
536
|
+
const RESOLVED_INSTRUMENTATION_ENTRY_VIRTUAL_MODULE_ID = `\0${INSTRUMENTATION_ENTRY_VIRTUAL_MODULE_ID}`;
|
|
537
|
+
const RESOLVED_LOG_ENTRY_VIRTUAL_MODULE_ID = `\0${LOG_ENTRY_VIRTUAL_MODULE_ID}`;
|
|
538
|
+
const SERVER_ENVIRONMENTS = [
|
|
539
|
+
"ssr",
|
|
540
|
+
"prerender",
|
|
541
|
+
"astro"
|
|
542
|
+
];
|
|
543
|
+
const DEFAULT_REQUEST_EXCLUDES = [...RECOMMENDED_EXCLUDES, ...STATIC_EXCLUDES];
|
|
544
|
+
function resolveHost(host) {
|
|
545
|
+
if (typeof host === "string") return host;
|
|
546
|
+
return host === true ? "0.0.0.0" : "localhost";
|
|
547
|
+
}
|
|
548
|
+
function resolveBootEntry(root, entry) {
|
|
549
|
+
if (entry) {
|
|
550
|
+
const abs = path.resolve(root, entry);
|
|
551
|
+
if (!fs.existsSync(abs)) throw new Error(`[@astroscope/node] boot entry not found: ${entry}`);
|
|
552
|
+
return abs;
|
|
553
|
+
}
|
|
554
|
+
for (const candidate of ["src/boot/index.ts", "src/boot.ts"]) {
|
|
555
|
+
const abs = path.resolve(root, candidate);
|
|
556
|
+
if (fs.existsSync(abs)) return abs;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
function resolveSeam(root, candidate) {
|
|
560
|
+
const abs = path.resolve(root, candidate);
|
|
561
|
+
return fs.existsSync(abs) ? abs : void 0;
|
|
562
|
+
}
|
|
563
|
+
/**
|
|
564
|
+
* Node adapter for Astro with a first-class server entrypoint: the boot
|
|
565
|
+
* lifecycle, module warmup, health probes, request logging and telemetry run
|
|
566
|
+
* as plain code around `server.listen()` instead of being injected into the
|
|
567
|
+
* build output.
|
|
568
|
+
*/
|
|
569
|
+
function node(options = {}) {
|
|
570
|
+
const bootOptions = options.boot ?? {};
|
|
571
|
+
const healthOptions = options.health ?? {};
|
|
572
|
+
const csrfOptions = options.csrf ?? {};
|
|
573
|
+
const loggingOptions = options.logging ?? {};
|
|
574
|
+
const telemetryOptions = options.telemetry ?? {};
|
|
575
|
+
const loggingExclude = loggingOptions ? loggingOptions.exclude ?? DEFAULT_REQUEST_EXCLUDES : [];
|
|
576
|
+
const telemetryExclude = telemetryOptions ? telemetryOptions.exclude ?? DEFAULT_REQUEST_EXCLUDES : [];
|
|
577
|
+
let astroConfig = null;
|
|
578
|
+
let bootEntry;
|
|
579
|
+
let configSeam;
|
|
580
|
+
let instrumentationSeam;
|
|
581
|
+
let logSeam;
|
|
582
|
+
let isDev = false;
|
|
583
|
+
return {
|
|
584
|
+
name: "@astroscope/node",
|
|
585
|
+
hooks: {
|
|
586
|
+
"astro:config:setup": ({ command, config, updateConfig, addMiddleware, logger }) => {
|
|
587
|
+
isDev = command === "dev";
|
|
588
|
+
if (loggingOptions || telemetryOptions) addMiddleware({
|
|
589
|
+
order: "pre",
|
|
590
|
+
entrypoint: "@astroscope/node/route-middleware"
|
|
591
|
+
});
|
|
592
|
+
if (csrfOptions) addMiddleware({
|
|
593
|
+
order: "pre",
|
|
594
|
+
entrypoint: "@astroscope/node/csrf-middleware"
|
|
595
|
+
});
|
|
596
|
+
const root = fileURLToPath(config.root);
|
|
597
|
+
const watch = bootOptions === false ? false : bootOptions.watch ?? true;
|
|
598
|
+
bootEntry = bootOptions === false ? void 0 : resolveBootEntry(root, bootOptions.entry);
|
|
599
|
+
configSeam = resolveSeam(root, "src/config.ts");
|
|
600
|
+
instrumentationSeam = resolveSeam(root, "src/instrumentation.ts");
|
|
601
|
+
logSeam = resolveSeam(root, "src/log.ts");
|
|
602
|
+
const relativeSeam = (abs) => abs ? path.relative(root, abs).split(path.sep).join("/") : void 0;
|
|
603
|
+
const devMachinery = command === "dev" && bootOptions !== false ? createDevMachinery({
|
|
604
|
+
entry: bootEntry ? path.relative(root, bootEntry) : void 0,
|
|
605
|
+
watch,
|
|
606
|
+
watchEntries: [relativeSeam(configSeam), relativeSeam(logSeam)].filter((entry) => !!entry),
|
|
607
|
+
prepare: (importModule) => preparePlatform({
|
|
608
|
+
dev: true,
|
|
609
|
+
telemetry: telemetryOptions && telemetryOptions.dev ? { prometheus: telemetryOptions.prometheus ?? {} } : false,
|
|
610
|
+
seams: {
|
|
611
|
+
...configSeam && { config: () => importModule(`/${relativeSeam(configSeam)}`) },
|
|
612
|
+
...instrumentationSeam && { instrumentation: () => importModule(`/${relativeSeam(instrumentationSeam)}`) },
|
|
613
|
+
...logSeam && { log: () => importModule(`/${relativeSeam(logSeam)}`) }
|
|
614
|
+
}
|
|
615
|
+
}),
|
|
616
|
+
logger,
|
|
617
|
+
getConfig: () => astroConfig
|
|
618
|
+
}) : [];
|
|
619
|
+
if (command === "dev" && bootOptions !== false && watch) addMiddleware({
|
|
620
|
+
entrypoint: "@astroscope/node/dev-middleware",
|
|
621
|
+
order: "pre"
|
|
622
|
+
});
|
|
623
|
+
updateConfig({
|
|
624
|
+
build: { redirects: false },
|
|
625
|
+
...config.trailingSlash === "ignore" && { trailingSlash: "never" },
|
|
626
|
+
security: {
|
|
627
|
+
...!config.security.allowedDomains?.length && { allowedDomains: [{}] },
|
|
628
|
+
...csrfOptions && { checkOrigin: false }
|
|
629
|
+
},
|
|
630
|
+
image: { endpoint: {
|
|
631
|
+
route: config.image.endpoint.route ?? "_image",
|
|
632
|
+
entrypoint: config.image.endpoint.entrypoint ?? (command === "dev" ? "astro/assets/endpoint/dev" : "astro/assets/endpoint/node")
|
|
633
|
+
} },
|
|
634
|
+
vite: { plugins: [
|
|
635
|
+
...devMachinery,
|
|
636
|
+
ssrSourcemapPlugin(),
|
|
637
|
+
stripSsrEffectsPlugin(),
|
|
638
|
+
{
|
|
639
|
+
name: "@astroscope/node",
|
|
640
|
+
configEnvironment(environmentName) {
|
|
641
|
+
if (SERVER_ENVIRONMENTS.includes(environmentName)) return { resolve: { noExternal: ["@astroscope/node"] } };
|
|
642
|
+
},
|
|
643
|
+
resolveId(id) {
|
|
644
|
+
if (id === "virtual:@astroscope/node/config") return RESOLVED_CONFIG_VIRTUAL_MODULE_ID;
|
|
645
|
+
if (id === "virtual:@astroscope/node/boot") return RESOLVED_BOOT_VIRTUAL_MODULE_ID;
|
|
646
|
+
if (id === "virtual:@astroscope/node/csrf") return RESOLVED_CSRF_VIRTUAL_MODULE_ID;
|
|
647
|
+
if (id === "virtual:@astroscope/node/config-entry") return RESOLVED_CONFIG_ENTRY_VIRTUAL_MODULE_ID;
|
|
648
|
+
if (id === "virtual:@astroscope/node/instrumentation-entry") return RESOLVED_INSTRUMENTATION_ENTRY_VIRTUAL_MODULE_ID;
|
|
649
|
+
if (id === "virtual:@astroscope/node/log-entry") return RESOLVED_LOG_ENTRY_VIRTUAL_MODULE_ID;
|
|
650
|
+
},
|
|
651
|
+
load(id) {
|
|
652
|
+
if (id === RESOLVED_CONFIG_VIRTUAL_MODULE_ID) {
|
|
653
|
+
if (!astroConfig) throw new Error("[@astroscope/node] astro config not resolved yet");
|
|
654
|
+
const runtimeOptions = {
|
|
655
|
+
host: resolveHost(astroConfig.server.host),
|
|
656
|
+
port: astroConfig.server.port ?? 4321,
|
|
657
|
+
client: astroConfig.build.client.toString(),
|
|
658
|
+
server: astroConfig.build.server.toString(),
|
|
659
|
+
bodySizeLimit: options.bodySizeLimit ?? 1024 * 1024 * 1024,
|
|
660
|
+
shutdownTimeout: options.shutdownTimeout ?? 1e4,
|
|
661
|
+
health: healthOptions ? {
|
|
662
|
+
...healthOptions.host !== void 0 && { host: healthOptions.host },
|
|
663
|
+
...healthOptions.port !== void 0 && { port: healthOptions.port },
|
|
664
|
+
...healthOptions.paths && { paths: healthOptions.paths }
|
|
665
|
+
} : false
|
|
666
|
+
};
|
|
667
|
+
const logging = loggingOptions ? `{ exclude: ${serializeExcludePatterns(loggingExclude)}, extended: ${JSON.stringify(loggingOptions.extended ?? false)} }` : "false";
|
|
668
|
+
const telemetry = telemetryOptions ? `{ exclude: ${serializeExcludePatterns(telemetryExclude)}, prometheus: ${JSON.stringify(telemetryOptions.prometheus ?? {})} }` : "false";
|
|
669
|
+
return `export const options = { ...${JSON.stringify(runtimeOptions)}, logging: ${logging}, telemetry: ${telemetry} };`;
|
|
670
|
+
}
|
|
671
|
+
if (id === RESOLVED_BOOT_VIRTUAL_MODULE_ID) return bootEntry ? `export * from ${JSON.stringify(bootEntry)};` : "export {};";
|
|
672
|
+
if (id === RESOLVED_CSRF_VIRTUAL_MODULE_ID) return `export const excludePatterns = ${serializeExcludePatterns(csrfOptions ? csrfOptions.exclude ?? [] : [])};`;
|
|
673
|
+
if (id === RESOLVED_CONFIG_ENTRY_VIRTUAL_MODULE_ID) return configSeam ? `import ${JSON.stringify(configSeam)};\nexport {};` : "export {};";
|
|
674
|
+
if (id === RESOLVED_INSTRUMENTATION_ENTRY_VIRTUAL_MODULE_ID) return instrumentationSeam ? `export * from ${JSON.stringify(instrumentationSeam)};` : "export {};";
|
|
675
|
+
if (id === RESOLVED_LOG_ENTRY_VIRTUAL_MODULE_ID) return logSeam ? `export { default } from ${JSON.stringify(logSeam)};` : "export default undefined;";
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
] }
|
|
679
|
+
});
|
|
680
|
+
},
|
|
681
|
+
"astro:server:setup": ({ server }) => {
|
|
682
|
+
if (!isDev) return;
|
|
683
|
+
const devLogging = loggingOptions && loggingOptions.dev;
|
|
684
|
+
const devTelemetry = telemetryOptions && telemetryOptions.dev;
|
|
685
|
+
const instrument = devLogging || devTelemetry ? createRequestInstrumentation({
|
|
686
|
+
logging: devLogging ? {
|
|
687
|
+
exclude: loggingExclude,
|
|
688
|
+
extended: loggingOptions.extended ?? false
|
|
689
|
+
} : false,
|
|
690
|
+
telemetry: devTelemetry ? { exclude: telemetryExclude } : false
|
|
691
|
+
}) : void 0;
|
|
692
|
+
server.middlewares.use((req, res, next) => {
|
|
693
|
+
const inner = () => {
|
|
694
|
+
if (!dispatchNativeMount(req, res)) next();
|
|
695
|
+
};
|
|
696
|
+
if (instrument) instrument(req, res, inner);
|
|
697
|
+
else inner();
|
|
698
|
+
});
|
|
699
|
+
},
|
|
700
|
+
"astro:config:done": ({ config, setAdapter }) => {
|
|
701
|
+
astroConfig = config;
|
|
702
|
+
setAdapter({
|
|
703
|
+
name: "@astroscope/node",
|
|
704
|
+
entrypointResolution: "auto",
|
|
705
|
+
serverEntrypoint: "@astroscope/node/server",
|
|
706
|
+
previewEntrypoint: "@astroscope/node/preview",
|
|
707
|
+
adapterFeatures: {
|
|
708
|
+
buildOutput: "server",
|
|
709
|
+
middlewareMode: "classic"
|
|
710
|
+
},
|
|
711
|
+
supportedAstroFeatures: {
|
|
712
|
+
hybridOutput: "stable",
|
|
713
|
+
staticOutput: "stable",
|
|
714
|
+
serverOutput: "stable",
|
|
715
|
+
sharpImageService: "stable",
|
|
716
|
+
i18nDomains: "experimental",
|
|
717
|
+
envGetSecret: "stable"
|
|
718
|
+
}
|
|
719
|
+
});
|
|
720
|
+
},
|
|
721
|
+
"astro:build:done": async ({ logger }) => {
|
|
722
|
+
if (!astroConfig) return;
|
|
723
|
+
const { compressClientDir } = await import("./compress-B11aeRHQ.js");
|
|
724
|
+
await compressClientDir(fileURLToPath(astroConfig.build.client), logger);
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
//#endregion
|
|
730
|
+
export { node as default };
|
|
731
|
+
|
|
732
|
+
//# sourceMappingURL=index.js.map
|