bun-plugin-stx 0.2.320 → 0.2.322
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/dist/serve.d.ts +25 -3
- package/dist/serve.js +83 -7
- package/package.json +1 -1
package/dist/serve.d.ts
CHANGED
|
@@ -227,6 +227,17 @@ export declare function isRenderableCacheCandidate(html: string): boolean;
|
|
|
227
227
|
*/
|
|
228
228
|
export declare function usableErrorPage(rendered: string | null): string | null;
|
|
229
229
|
export declare function bundleBrowserAsset(entrypoint: string): Promise<Response>;
|
|
230
|
+
/**
|
|
231
|
+
* The bind budget from an option or config value. Anything that is not a
|
|
232
|
+
* finite, non-negative number falls back to the default rather than to
|
|
233
|
+
* "forever", since an unbounded wait is the failure the budget exists to stop.
|
|
234
|
+
*/
|
|
235
|
+
export declare function resolveImageWarmupBindBudget(value: unknown): number;
|
|
236
|
+
/**
|
|
237
|
+
* Whether `promise` settles within `ms`. It is not cancelled either way: the
|
|
238
|
+
* caller decides what to do with work that is still running.
|
|
239
|
+
*/
|
|
240
|
+
export declare function settleWithin(promise: Promise<unknown>, ms: number): Promise<boolean>;
|
|
230
241
|
/**
|
|
231
242
|
* Start the STX development server
|
|
232
243
|
* @param options Server options with patterns and port
|
|
@@ -235,9 +246,17 @@ export declare function serve(options: ServeOptions): Promise<void>;
|
|
|
235
246
|
/**
|
|
236
247
|
* Whether any `.stx` template under `root` uses `<StxImage>` or `@image`.
|
|
237
248
|
*
|
|
238
|
-
*
|
|
239
|
-
*
|
|
240
|
-
*
|
|
249
|
+
* Skips dependencies, build output and hidden directories (the stx state
|
|
250
|
+
* directory among them), so it reads the project's own templates, including a
|
|
251
|
+
* framework's templates vendored into it.
|
|
252
|
+
*
|
|
253
|
+
* A component that wraps `<StxImage>` counts only when something renders it.
|
|
254
|
+
* Stacks vendors its `Image.stx` component into every app, twice (the
|
|
255
|
+
* framework defaults and the pantry install tree), so reading a definition as
|
|
256
|
+
* a use turned the pass on for every Stacks app: one that only ever wrote
|
|
257
|
+
* plain `<img>` tags spent over seven minutes of each cold boot encoding
|
|
258
|
+
* 213MB of variants that no page could ask for. A use is followed through
|
|
259
|
+
* wrappers of wrappers, and a page, layout or partial using one counts.
|
|
241
260
|
*/
|
|
242
261
|
export declare function projectUsesImageBuiltins(root: string): Promise<boolean>;
|
|
243
262
|
/**
|
|
@@ -256,6 +275,8 @@ export declare const ROUTE_PARAMS_PLACEHOLDER: '__STX_ROUTE_PARAMS__';
|
|
|
256
275
|
export declare const CSRF_COOKIE: 'X-CSRF-Token';
|
|
257
276
|
/** `/_stx/runtime.js`, `/_stx/router.<16 hex>.js`, … — the shared client scripts. */
|
|
258
277
|
export declare const SHARED_SCRIPT_PATH: unknown;
|
|
278
|
+
/** Default for `imageWarmupBindBudgetMs`: well inside ts-cloud's 180s bind window. */
|
|
279
|
+
export declare const DEFAULT_IMAGE_WARMUP_BIND_BUDGET_MS: unknown;
|
|
259
280
|
/**
|
|
260
281
|
* Build the candidate regexes for a dynamic route file (stacksjs/stx#1927).
|
|
261
282
|
*
|
|
@@ -314,6 +335,7 @@ export declare interface ServeOptions {
|
|
|
314
335
|
cssCacheLimit?: number
|
|
315
336
|
prewarmRenderCache?: boolean | number
|
|
316
337
|
imageWarmupGraceMs?: number
|
|
338
|
+
imageWarmupBindBudgetMs?: number
|
|
317
339
|
imageWarmup?: boolean | 'auto'
|
|
318
340
|
publicDir?: string
|
|
319
341
|
quiet?: boolean
|
package/dist/serve.js
CHANGED
|
@@ -9216,6 +9216,28 @@ async function bundleBrowserAsset(entrypoint) {
|
|
|
9216
9216
|
}
|
|
9217
9217
|
});
|
|
9218
9218
|
}
|
|
9219
|
+
var DEFAULT_IMAGE_WARMUP_BIND_BUDGET_MS = 45000;
|
|
9220
|
+
function resolveImageWarmupBindBudget(value) {
|
|
9221
|
+
const ms = typeof value === "string" && value.trim() !== "" ? Number(value) : value;
|
|
9222
|
+
return typeof ms === "number" && Number.isFinite(ms) && ms >= 0 ? ms : DEFAULT_IMAGE_WARMUP_BIND_BUDGET_MS;
|
|
9223
|
+
}
|
|
9224
|
+
async function settleWithin(promise, ms) {
|
|
9225
|
+
let timer;
|
|
9226
|
+
const settled = promise.then(() => true, () => true);
|
|
9227
|
+
try {
|
|
9228
|
+
return await Promise.race([
|
|
9229
|
+
settled,
|
|
9230
|
+
new Promise((resolve13) => {
|
|
9231
|
+
timer = setTimeout(resolve13, ms, false);
|
|
9232
|
+
})
|
|
9233
|
+
]);
|
|
9234
|
+
} finally {
|
|
9235
|
+
clearTimeout(timer);
|
|
9236
|
+
}
|
|
9237
|
+
}
|
|
9238
|
+
function formatSeconds(ms) {
|
|
9239
|
+
return `${(ms / 1000).toFixed(ms < 1e4 ? 1 : 0)}s`;
|
|
9240
|
+
}
|
|
9219
9241
|
async function serve(options) {
|
|
9220
9242
|
const stxConfig = await loadConfig6({
|
|
9221
9243
|
name: "stx",
|
|
@@ -9260,6 +9282,7 @@ async function serve(options) {
|
|
|
9260
9282
|
let placeholdersAreReady = false;
|
|
9261
9283
|
const imageWarmupSetting = options.imageWarmup ?? stxConfig.imageWarmup ?? "auto";
|
|
9262
9284
|
const imageWarmupGraceMs = Math.max(0, options.imageWarmupGraceMs ?? 1000);
|
|
9285
|
+
const imageWarmupBindBudgetMs = resolveImageWarmupBindBudget(options.imageWarmupBindBudgetMs ?? stxConfig.imageWarmupBindBudgetMs);
|
|
9263
9286
|
function awaitImageWarmup() {
|
|
9264
9287
|
if (placeholdersAreReady || imageWarmupGraceMs === 0)
|
|
9265
9288
|
return;
|
|
@@ -10373,9 +10396,10 @@ ${block}`;
|
|
|
10373
10396
|
const publicRoot = nodePath.resolve(process19.cwd(), publicDir);
|
|
10374
10397
|
const [placeholderResult, deliveryResult] = await Promise.allSettled([
|
|
10375
10398
|
stx.warmImagePlaceholders(publicRoot, {
|
|
10376
|
-
cachePath: imagePlaceholderCachePath
|
|
10399
|
+
cachePath: imagePlaceholderCachePath,
|
|
10400
|
+
offThread: true
|
|
10377
10401
|
}),
|
|
10378
|
-
stx.prepareImageDelivery(publicRoot, imageDeliveryOutputDir)
|
|
10402
|
+
stx.prepareImageDelivery(publicRoot, imageDeliveryOutputDir, { offThread: true })
|
|
10379
10403
|
]);
|
|
10380
10404
|
const derived = placeholderResult.status === "fulfilled" ? placeholderResult.value : 0;
|
|
10381
10405
|
if (derived > 0 && !production)
|
|
@@ -10387,8 +10411,17 @@ ${block}`;
|
|
|
10387
10411
|
markPlaceholdersReady();
|
|
10388
10412
|
}
|
|
10389
10413
|
}
|
|
10390
|
-
if (production)
|
|
10391
|
-
|
|
10414
|
+
if (production) {
|
|
10415
|
+
const warmupStartedAt = performance.now();
|
|
10416
|
+
const warmup = runImageWarmup();
|
|
10417
|
+
const bound = await settleWithin(warmup, imageWarmupBindBudgetMs);
|
|
10418
|
+
if (!bound) {
|
|
10419
|
+
console.warn(`[stx] image warm-up still running after ${formatSeconds(imageWarmupBindBudgetMs)}; binding now and finishing it in the background. Images fall back to the original files until it completes. A cold image cache (first deploy, a cleared STX_IMAGE_CACHE_DIR, an encoder upgrade) is the usual cause; the next start reuses what this one encodes.`);
|
|
10420
|
+
warmup.then(() => {
|
|
10421
|
+
console.log(`[stx] image warm-up finished after ${formatSeconds(performance.now() - warmupStartedAt)}`);
|
|
10422
|
+
});
|
|
10423
|
+
}
|
|
10424
|
+
}
|
|
10392
10425
|
const portAutoIncrement = options.autoIncrementPort ?? true;
|
|
10393
10426
|
const maxPortAttempts = portAutoIncrement === false ? 1 : typeof portAutoIncrement === "number" ? Math.max(1, portAutoIncrement) : 10;
|
|
10394
10427
|
let actualPort = port;
|
|
@@ -11182,8 +11215,20 @@ if (import.meta.main) {
|
|
|
11182
11215
|
}
|
|
11183
11216
|
var IMAGE_BUILTIN_PATTERN = /<(?:StxImage|Image|stx-image|stx-img)[\s/>]|@image\s*\(/;
|
|
11184
11217
|
var TEMPLATE_SCAN_SKIP = new Set(["node_modules", "dist", "build", "coverage", "vendor"]);
|
|
11218
|
+
function escapeRegExp(value) {
|
|
11219
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
11220
|
+
}
|
|
11221
|
+
function kebabCase(name) {
|
|
11222
|
+
return name.replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/([A-Z])([A-Z][a-z])/g, "$1-$2").toLowerCase();
|
|
11223
|
+
}
|
|
11224
|
+
function usesComponent(source, name) {
|
|
11225
|
+
const kebab = kebabCase(name);
|
|
11226
|
+
const spellings = (kebab.includes("-") ? [name, kebab] : [name]).map(escapeRegExp).join("|");
|
|
11227
|
+
return new RegExp(`<(?:${spellings})[\\s/>]|@component\\(\\s*['"](?:${spellings})['"]`).test(source);
|
|
11228
|
+
}
|
|
11185
11229
|
async function projectUsesImageBuiltins(root) {
|
|
11186
11230
|
const { readdir: readdir4, readFile } = await import("fs/promises");
|
|
11231
|
+
const templates = [];
|
|
11187
11232
|
const pending = [root];
|
|
11188
11233
|
while (pending.length > 0) {
|
|
11189
11234
|
const dir = pending.pop();
|
|
@@ -11204,10 +11249,38 @@ async function projectUsesImageBuiltins(root) {
|
|
|
11204
11249
|
}
|
|
11205
11250
|
if (!entry.isFile() || !entry.name.endsWith(".stx"))
|
|
11206
11251
|
continue;
|
|
11252
|
+
let source;
|
|
11207
11253
|
try {
|
|
11208
|
-
|
|
11209
|
-
|
|
11210
|
-
|
|
11254
|
+
source = await readFile(full, "utf8");
|
|
11255
|
+
} catch {
|
|
11256
|
+
continue;
|
|
11257
|
+
}
|
|
11258
|
+
const inComponents = nodePath.relative(root, dir).split(nodePath.sep).includes("components");
|
|
11259
|
+
const component = inComponents ? entry.name.slice(0, -".stx".length) : undefined;
|
|
11260
|
+
if (!component && IMAGE_BUILTIN_PATTERN.test(source))
|
|
11261
|
+
return true;
|
|
11262
|
+
templates.push({ source, component });
|
|
11263
|
+
}
|
|
11264
|
+
}
|
|
11265
|
+
const imageComponents = new Set;
|
|
11266
|
+
const queue = [];
|
|
11267
|
+
for (const template of templates) {
|
|
11268
|
+
if (template.component && IMAGE_BUILTIN_PATTERN.test(template.source) && !imageComponents.has(template.component)) {
|
|
11269
|
+
imageComponents.add(template.component);
|
|
11270
|
+
queue.push(template.component);
|
|
11271
|
+
}
|
|
11272
|
+
}
|
|
11273
|
+
while (queue.length > 0) {
|
|
11274
|
+
const name = queue.shift();
|
|
11275
|
+
for (const template of templates) {
|
|
11276
|
+
if (template.component === name || !usesComponent(template.source, name))
|
|
11277
|
+
continue;
|
|
11278
|
+
if (!template.component)
|
|
11279
|
+
return true;
|
|
11280
|
+
if (!imageComponents.has(template.component)) {
|
|
11281
|
+
imageComponents.add(template.component);
|
|
11282
|
+
queue.push(template.component);
|
|
11283
|
+
}
|
|
11211
11284
|
}
|
|
11212
11285
|
}
|
|
11213
11286
|
return false;
|
|
@@ -11216,12 +11289,14 @@ export {
|
|
|
11216
11289
|
usableErrorPage,
|
|
11217
11290
|
staticCacheControl,
|
|
11218
11291
|
sharedScriptCacheControl,
|
|
11292
|
+
settleWithin,
|
|
11219
11293
|
serverScriptsReadParams,
|
|
11220
11294
|
serve,
|
|
11221
11295
|
serializeRouteParams,
|
|
11222
11296
|
routeSpecificity,
|
|
11223
11297
|
responseTakesCsrfCookie,
|
|
11224
11298
|
resolveServeOpenUrl,
|
|
11299
|
+
resolveImageWarmupBindBudget,
|
|
11225
11300
|
render404Page,
|
|
11226
11301
|
publicFileExists,
|
|
11227
11302
|
projectUsesImageBuiltins,
|
|
@@ -11241,5 +11316,6 @@ export {
|
|
|
11241
11316
|
boundedCache,
|
|
11242
11317
|
SHARED_SCRIPT_PATH,
|
|
11243
11318
|
ROUTE_PARAMS_PLACEHOLDER,
|
|
11319
|
+
DEFAULT_IMAGE_WARMUP_BIND_BUDGET_MS,
|
|
11244
11320
|
CSRF_COOKIE
|
|
11245
11321
|
};
|
package/package.json
CHANGED