@getstrata/bootstrap 2.1.0 → 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +32 -0
- package/README.md +1 -1
- package/dist/bootstrap/app.d.ts +7 -1
- package/dist/bootstrap/config.d.ts +1 -1
- package/dist/bootstrap/context.d.ts +8 -2
- package/dist/bootstrap/dependencies.d.ts +1 -1
- package/dist/bootstrap/discoverJobs.d.ts +11 -3
- package/dist/bootstrap/discoverListeners.d.ts +7 -3
- package/dist/bootstrap/discoverModels.d.ts +8 -0
- package/dist/bootstrap/discoverModules.d.ts +4 -1
- package/dist/bootstrap/httpKernel.d.ts +11 -2
- package/dist/bootstrap/inProcessCron.d.ts +2 -1
- package/dist/bootstrap/infrastructureDiscovery.d.ts +9 -0
- package/dist/bootstrap/modules.d.ts +1 -1
- package/dist/bootstrap/providers/tracing.d.ts +3 -0
- package/dist/bootstrap/public-api.d.ts +9 -4
- package/dist/bootstrap/web/index.d.ts +1 -1
- package/dist/bootstrap/web/server.d.ts +15 -7
- package/dist/entries/buildModuleRoutes.js +218 -71
- package/dist/entries/buildWebModuleRoutes.js +218 -71
- package/dist/entries/cache/modelCacheTags.js +157 -51
- package/dist/entries/config.js +8 -0
- package/dist/entries/context.js +427 -188
- package/dist/entries/createWebRoutes.js +220 -73
- package/dist/entries/dependencies.js +429 -190
- package/dist/entries/discoverJobs.js +132 -50
- package/dist/entries/discoverListeners.js +85 -37
- package/dist/entries/discoverModels.js +39 -0
- package/dist/entries/discoverModules.js +158 -51
- package/dist/entries/health.js +4 -0
- package/dist/entries/httpKernel.js +58 -17
- package/dist/entries/listeners/invalidateCacheOnModelWrite.js +157 -51
- package/dist/entries/providers.js +308 -164
- package/dist/entries/secretsGuard.js +2 -2
- package/dist/entries/web/routing.js +58 -17
- package/dist/entries/web/server.js +71 -14
- package/dist/index.js +600 -222
- package/package.json +8 -3
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// ../../src/bootstrap/createWebRoutes.ts
|
|
3
|
-
import { join as
|
|
3
|
+
import { join as join3 } from "path";
|
|
4
4
|
import { isViewsEnabled as isViewsEnabled2 } from "@getstrata/core/runtime/frontendMode";
|
|
5
5
|
import { assertUrlPathUnderRoot } from "@getstrata/core/security/safePath";
|
|
6
6
|
import { notFoundHtmlResponse } from "@getstrata/core/view";
|
|
@@ -59,8 +59,12 @@ import {
|
|
|
59
59
|
CORE_CACHE_TOKEN,
|
|
60
60
|
CORE_CONFIG_TOKEN,
|
|
61
61
|
CORE_EVENT_BUS_TOKEN,
|
|
62
|
+
CORE_HTTP_CLEANUP_TOKEN,
|
|
62
63
|
CORE_POLICY_GATE_TOKEN,
|
|
64
|
+
CORE_PUBLIC_TENANCY_TOKEN,
|
|
63
65
|
CORE_QUEUE_TOKEN,
|
|
66
|
+
CORE_TENANT_RESOLVER_TOKEN,
|
|
67
|
+
CORE_THROTTLE_QUOTA_POLICY_TOKEN,
|
|
64
68
|
CORE_TOKEN_SERVICE_TOKEN
|
|
65
69
|
} from "@getstrata/core/contracts/serviceTokens";
|
|
66
70
|
var APP_PORT_CONFIG_KEY = "app.port";
|
|
@@ -126,8 +130,39 @@ function resolveRegisterRateLimit() {
|
|
|
126
130
|
// ../../src/bootstrap/httpKernel.ts
|
|
127
131
|
class HttpKernel {
|
|
128
132
|
dependencies;
|
|
133
|
+
ownedThrottles = new Set;
|
|
134
|
+
disposed = false;
|
|
129
135
|
constructor(dependencies) {
|
|
130
136
|
this.dependencies = dependencies;
|
|
137
|
+
if (dependencies.container.has(CORE_HTTP_CLEANUP_TOKEN)) {
|
|
138
|
+
dependencies.container.resolve(CORE_HTTP_CLEANUP_TOKEN)(() => this.dispose());
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
dispose() {
|
|
142
|
+
this.disposed = true;
|
|
143
|
+
const owned = [...this.ownedThrottles];
|
|
144
|
+
this.ownedThrottles.clear();
|
|
145
|
+
const errors = [];
|
|
146
|
+
for (const throttle of owned.reverse()) {
|
|
147
|
+
try {
|
|
148
|
+
throttle.dispose();
|
|
149
|
+
} catch (error) {
|
|
150
|
+
errors.push(error);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
if (errors.length)
|
|
154
|
+
throw new AggregateError(errors, "HTTP throttle cleanup failed.");
|
|
155
|
+
}
|
|
156
|
+
ownThrottle(throttle) {
|
|
157
|
+
if (this.disposed) {
|
|
158
|
+
if ("dispose" in throttle && typeof throttle.dispose === "function")
|
|
159
|
+
throttle.dispose();
|
|
160
|
+
throw new Error("HTTP kernel is disposed.");
|
|
161
|
+
}
|
|
162
|
+
if ("dispose" in throttle && typeof throttle.dispose === "function") {
|
|
163
|
+
this.ownedThrottles.add(throttle);
|
|
164
|
+
}
|
|
165
|
+
return throttle;
|
|
131
166
|
}
|
|
132
167
|
globalMiddleware(errorFormat = "json") {
|
|
133
168
|
const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
|
|
@@ -142,7 +177,10 @@ class HttpKernel {
|
|
|
142
177
|
errorFormat === "json" ? createJsonErrorMiddleware() : createWebErrorMiddleware(),
|
|
143
178
|
createAuthMiddleware(auth),
|
|
144
179
|
createMembershipMiddleware(),
|
|
145
|
-
createTenantMiddleware(
|
|
180
|
+
createTenantMiddleware({
|
|
181
|
+
publicTenancy: this.dependencies.container.has(CORE_PUBLIC_TENANCY_TOKEN) ? this.dependencies.container.resolve(CORE_PUBLIC_TENANCY_TOKEN) : undefined,
|
|
182
|
+
resolveTenant: this.dependencies.container.has(CORE_TENANT_RESOLVER_TOKEN) ? this.dependencies.container.resolve(CORE_TENANT_RESOLVER_TOKEN) : undefined
|
|
183
|
+
})
|
|
146
184
|
];
|
|
147
185
|
}
|
|
148
186
|
group(name) {
|
|
@@ -157,25 +195,28 @@ class HttpKernel {
|
|
|
157
195
|
if (!this.dependencies.container.has(CORE_CONFIG_TOKEN)) {
|
|
158
196
|
return [csrf];
|
|
159
197
|
}
|
|
198
|
+
const quotaPolicy = this.dependencies.container.has(CORE_THROTTLE_QUOTA_POLICY_TOKEN) ? this.dependencies.container.resolve(CORE_THROTTLE_QUOTA_POLICY_TOKEN) : undefined;
|
|
160
199
|
const config = this.dependencies.container.resolve(CORE_CONFIG_TOKEN);
|
|
161
200
|
const redisUrl = config.get(REDIS_URL_CONFIG_KEY)?.trim() ?? "";
|
|
162
201
|
if (!redisUrl) {
|
|
163
202
|
const maxAttempts = Number(process.env.RATE_LIMIT_PER_MINUTE ?? "120");
|
|
164
203
|
return [
|
|
165
|
-
createMemoryThrottleMiddleware({
|
|
204
|
+
this.ownThrottle(createMemoryThrottleMiddleware({
|
|
166
205
|
maxAttempts: Number.isFinite(maxAttempts) ? maxAttempts : 120,
|
|
167
|
-
decaySeconds: 60
|
|
168
|
-
|
|
206
|
+
decaySeconds: 60,
|
|
207
|
+
quotaPolicy
|
|
208
|
+
})),
|
|
169
209
|
csrf
|
|
170
210
|
];
|
|
171
211
|
}
|
|
172
212
|
const maxAttempts = Number(process.env.RATE_LIMIT_PER_MINUTE ?? "120");
|
|
173
213
|
return [
|
|
174
|
-
createThrottleMiddleware({
|
|
214
|
+
this.ownThrottle(createThrottleMiddleware({
|
|
175
215
|
redisUrl,
|
|
176
216
|
maxAttempts: Number.isFinite(maxAttempts) ? maxAttempts : 120,
|
|
177
|
-
decaySeconds: 60
|
|
178
|
-
|
|
217
|
+
decaySeconds: 60,
|
|
218
|
+
quotaPolicy
|
|
219
|
+
})),
|
|
179
220
|
csrf
|
|
180
221
|
];
|
|
181
222
|
}
|
|
@@ -212,8 +253,8 @@ class HttpKernel {
|
|
|
212
253
|
return handler(request);
|
|
213
254
|
});
|
|
214
255
|
}
|
|
215
|
-
wrapWebPublicRead(handler) {
|
|
216
|
-
if (isPublicReadsEnabled()) {
|
|
256
|
+
wrapWebPublicRead(handler, options = {}) {
|
|
257
|
+
if (options.allowAnonymous ?? isPublicReadsEnabled()) {
|
|
217
258
|
return this.wrapWeb(handler);
|
|
218
259
|
}
|
|
219
260
|
return this.wrapWebAuthenticated(handler);
|
|
@@ -259,8 +300,8 @@ class HttpKernel {
|
|
|
259
300
|
const middleware = [...this.group("authenticated"), ...this.verifiedMiddleware()];
|
|
260
301
|
return withMiddleware(...middleware)(handler);
|
|
261
302
|
}
|
|
262
|
-
wrapPublicRead(handler) {
|
|
263
|
-
if (isPublicReadsEnabled()) {
|
|
303
|
+
wrapPublicRead(handler, options = {}) {
|
|
304
|
+
if (options.allowAnonymous ?? isPublicReadsEnabled()) {
|
|
264
305
|
return handler;
|
|
265
306
|
}
|
|
266
307
|
return this.wrapAuthenticated(handler);
|
|
@@ -305,25 +346,25 @@ class HttpKernel {
|
|
|
305
346
|
const memoryKeyPrefix = scope === "login" ? "login-throttle:" : "register-throttle:";
|
|
306
347
|
const redisUrl = this.dependencies.container.has(CORE_CONFIG_TOKEN) ? this.dependencies.container.resolve(CORE_CONFIG_TOKEN).get(REDIS_URL_CONFIG_KEY)?.trim() ?? "" : "";
|
|
307
348
|
if (scope === "login") {
|
|
308
|
-
return withMiddleware(createLoginThrottleMiddleware({
|
|
349
|
+
return withMiddleware(this.ownThrottle(createLoginThrottleMiddleware({
|
|
309
350
|
...redisUrl ? { redisUrl } : {},
|
|
310
351
|
maxAttempts: rateLimit.maxAttempts,
|
|
311
352
|
decaySeconds: rateLimit.decaySeconds
|
|
312
|
-
}))(handler);
|
|
353
|
+
})))(handler);
|
|
313
354
|
}
|
|
314
355
|
if (redisUrl) {
|
|
315
|
-
return withMiddleware(createThrottleMiddleware({
|
|
356
|
+
return withMiddleware(this.ownThrottle(createThrottleMiddleware({
|
|
316
357
|
redisUrl,
|
|
317
358
|
maxAttempts: rateLimit.maxAttempts,
|
|
318
359
|
decaySeconds: rateLimit.decaySeconds,
|
|
319
360
|
keyPrefix: memoryKeyPrefix
|
|
320
|
-
}))(handler);
|
|
361
|
+
})))(handler);
|
|
321
362
|
}
|
|
322
|
-
return withMiddleware(createMemoryThrottleMiddleware({
|
|
363
|
+
return withMiddleware(this.ownThrottle(createMemoryThrottleMiddleware({
|
|
323
364
|
maxAttempts: rateLimit.maxAttempts,
|
|
324
365
|
decaySeconds: rateLimit.decaySeconds,
|
|
325
366
|
keyPrefix: memoryKeyPrefix
|
|
326
|
-
}))(handler);
|
|
367
|
+
})))(handler);
|
|
327
368
|
}
|
|
328
369
|
}
|
|
329
370
|
function createHttpKernel(dependencies) {
|
|
@@ -331,74 +372,180 @@ function createHttpKernel(dependencies) {
|
|
|
331
372
|
}
|
|
332
373
|
|
|
333
374
|
// ../../src/bootstrap/discoverModules.ts
|
|
334
|
-
import {
|
|
335
|
-
import { join } from "path";
|
|
375
|
+
import { readdir as readdir2 } from "fs/promises";
|
|
376
|
+
import { join as join2, resolve } from "path";
|
|
377
|
+
|
|
378
|
+
// ../../src/bootstrap/infrastructureDiscovery.ts
|
|
379
|
+
import { readdir } from "fs/promises";
|
|
380
|
+
import { join, relative } from "path";
|
|
336
381
|
import { pathToFileURL } from "url";
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
382
|
+
async function infrastructureFiles(directory, exclude = []) {
|
|
383
|
+
const excluded = new Set([
|
|
384
|
+
"index.ts",
|
|
385
|
+
"index.js",
|
|
386
|
+
"index.mts",
|
|
387
|
+
"index.mjs",
|
|
388
|
+
"register.ts",
|
|
389
|
+
"register.js",
|
|
390
|
+
"register.mts",
|
|
391
|
+
"register.mjs",
|
|
392
|
+
...exclude
|
|
393
|
+
]);
|
|
394
|
+
const files = [];
|
|
395
|
+
async function visit(path) {
|
|
396
|
+
const entries = await readdir(path, { withFileTypes: true });
|
|
397
|
+
entries.sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
|
|
398
|
+
for (const entry of entries) {
|
|
399
|
+
const file = join(path, entry.name);
|
|
400
|
+
const key = relative(directory, file).replaceAll("\\", "/");
|
|
401
|
+
if (excluded.has(entry.name) || excluded.has(key))
|
|
402
|
+
continue;
|
|
403
|
+
if (entry.isDirectory())
|
|
404
|
+
await visit(file);
|
|
405
|
+
else if (entry.isFile() && /\.(ts|js|mts|mjs)$/.test(entry.name) && !/\.(d|test|spec)\.[cm]?[jt]s$/.test(entry.name))
|
|
406
|
+
files.push(file);
|
|
407
|
+
}
|
|
359
408
|
}
|
|
360
|
-
|
|
361
|
-
|
|
409
|
+
try {
|
|
410
|
+
await visit(directory);
|
|
411
|
+
} catch (error) {
|
|
412
|
+
if (error.code !== "ENOENT")
|
|
413
|
+
throw error;
|
|
414
|
+
if (error.path !== directory)
|
|
415
|
+
throw error;
|
|
362
416
|
}
|
|
363
|
-
|
|
417
|
+
return files;
|
|
364
418
|
}
|
|
365
|
-
async function
|
|
366
|
-
const modulesDirectory = resolveModulesDirectory(options);
|
|
367
|
-
let moduleNames;
|
|
419
|
+
async function importInfrastructure(file) {
|
|
368
420
|
try {
|
|
369
|
-
|
|
421
|
+
return await import(pathToFileURL(file).href);
|
|
370
422
|
} catch (error) {
|
|
371
|
-
|
|
372
|
-
|
|
423
|
+
throw new Error(`Cannot load infrastructure module ${file}`, { cause: error });
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// ../../src/bootstrap/discoverModules.ts
|
|
428
|
+
var KEY = Symbol.for("@getstrata/discoverModulesState");
|
|
429
|
+
function state() {
|
|
430
|
+
const globals = globalThis;
|
|
431
|
+
globals[KEY] ??= { appModules: [] };
|
|
432
|
+
return globals[KEY];
|
|
433
|
+
}
|
|
434
|
+
function configureModulesDirectory(modulesDir) {
|
|
435
|
+
const current = state();
|
|
436
|
+
const directory = resolve(modulesDir);
|
|
437
|
+
if (current.configuredModulesDir !== directory || current.configuredManifest) {
|
|
438
|
+
current.appModules.length = 0;
|
|
439
|
+
current.modulesReady = undefined;
|
|
440
|
+
current.source = undefined;
|
|
441
|
+
}
|
|
442
|
+
current.configuredManifest = undefined;
|
|
443
|
+
current.configuredModulesDir = directory;
|
|
444
|
+
}
|
|
445
|
+
function configureModulesManifest(manifest) {
|
|
446
|
+
const current = state();
|
|
447
|
+
if (current.configuredManifest !== manifest) {
|
|
448
|
+
current.appModules.length = 0;
|
|
449
|
+
current.modulesReady = undefined;
|
|
450
|
+
current.source = undefined;
|
|
451
|
+
}
|
|
452
|
+
current.configuredManifest = manifest;
|
|
453
|
+
}
|
|
454
|
+
function validateModule(value, location) {
|
|
455
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
456
|
+
throw new TypeError(`Invalid application module at ${location}.`);
|
|
457
|
+
const module = value;
|
|
458
|
+
if (typeof module.name !== "string" || !module.name.trim())
|
|
459
|
+
throw new TypeError(`Invalid module name at ${location}.`);
|
|
460
|
+
if (module.order !== undefined && (typeof module.order !== "number" || !Number.isFinite(module.order)))
|
|
461
|
+
throw new TypeError(`Invalid module order at ${location}.`);
|
|
462
|
+
for (const hook of ["routes", "webRoutes"])
|
|
463
|
+
if (module[hook] !== undefined && typeof module[hook] !== "function")
|
|
464
|
+
throw new TypeError(`Invalid ${hook} at ${location}.`);
|
|
465
|
+
if (module.providers !== undefined) {
|
|
466
|
+
if (!Array.isArray(module.providers))
|
|
467
|
+
throw new TypeError(`Invalid providers at ${location}.`);
|
|
468
|
+
for (const provider of module.providers) {
|
|
469
|
+
if (!provider || typeof provider.name !== "string" || !provider.name.trim() || provider.register !== undefined && typeof provider.register !== "function" || provider.boot !== undefined && typeof provider.boot !== "function")
|
|
470
|
+
throw new TypeError(`Invalid provider at ${location}.`);
|
|
373
471
|
}
|
|
374
|
-
throw error;
|
|
375
472
|
}
|
|
376
|
-
const modules = await Promise.all(moduleNames.map(async (moduleName) => {
|
|
377
|
-
const moduleUrl = pathToFileURL(join(modulesDirectory, moduleName, "index.ts")).href;
|
|
378
|
-
const loaded = await import(moduleUrl);
|
|
379
|
-
return loaded.default;
|
|
380
|
-
}));
|
|
381
|
-
return modules.filter((module) => module?.name !== undefined).sort((left, right) => (left.order ?? 100) - (right.order ?? 100));
|
|
382
473
|
}
|
|
383
|
-
async function
|
|
384
|
-
const
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
474
|
+
async function load(options) {
|
|
475
|
+
const manifest = options.manifest ?? (options.modulesDir === undefined ? state().configuredManifest : undefined);
|
|
476
|
+
const entries = [];
|
|
477
|
+
if (manifest)
|
|
478
|
+
for (const [index, value] of manifest.entries())
|
|
479
|
+
entries.push({ value, location: `module manifest[${index}]` });
|
|
480
|
+
else {
|
|
481
|
+
const directory = options.modulesDir ?? state().configuredModulesDir;
|
|
482
|
+
if (!directory)
|
|
483
|
+
throw new Error("configureModulesDirectory() or configureModulesManifest() must be called before discovering modules.");
|
|
484
|
+
let folders;
|
|
485
|
+
try {
|
|
486
|
+
folders = await readdir2(directory, { withFileTypes: true });
|
|
487
|
+
} catch (error) {
|
|
488
|
+
if (error.code === "ENOENT")
|
|
489
|
+
return [];
|
|
490
|
+
throw error;
|
|
491
|
+
}
|
|
492
|
+
const excluded = new Set(options.exclude ?? []);
|
|
493
|
+
for (const folder of folders.filter((entry) => entry.isDirectory() && !excluded.has(entry.name)).sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0)) {
|
|
494
|
+
const path = join2(directory, folder.name);
|
|
495
|
+
const files = await readdir2(path);
|
|
496
|
+
const candidates = files.filter((file) => /^index\.(ts|js|mts|mjs)$/.test(file));
|
|
497
|
+
if (candidates.length !== 1)
|
|
498
|
+
throw new Error(`Expected one module entrypoint at ${path}; found ${candidates.length}.`);
|
|
499
|
+
const file = join2(path, candidates[0]);
|
|
500
|
+
const exports = await importInfrastructure(file);
|
|
501
|
+
entries.push({ value: exports.default, location: file });
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
const names = new Map;
|
|
505
|
+
const modules = [];
|
|
506
|
+
for (const { value, location } of entries) {
|
|
507
|
+
validateModule(value, location);
|
|
508
|
+
const previous = names.get(value.name);
|
|
509
|
+
if (previous)
|
|
510
|
+
throw new Error(`Duplicate module "${value.name}" at ${location}; first declared at ${previous}.`);
|
|
511
|
+
names.set(value.name, location);
|
|
512
|
+
modules.push(value);
|
|
513
|
+
}
|
|
514
|
+
return modules.sort((left, right) => (left.order ?? 100) - (right.order ?? 100));
|
|
515
|
+
}
|
|
516
|
+
function ensureModulesLoaded(options = {}) {
|
|
517
|
+
const current = state();
|
|
518
|
+
const key = options.manifest ?? (options.modulesDir === undefined ? current.configuredManifest : undefined) ?? JSON.stringify([
|
|
519
|
+
options.modulesDir ? resolve(options.modulesDir) : current.configuredModulesDir,
|
|
520
|
+
[...options.exclude ?? []].sort()
|
|
521
|
+
]);
|
|
522
|
+
if (current.modulesReady && current.source === key)
|
|
523
|
+
return current.modulesReady;
|
|
524
|
+
current.appModules.length = 0;
|
|
525
|
+
current.source = key;
|
|
526
|
+
const pending = load(options).then((modules) => {
|
|
527
|
+
if (current.modulesReady !== pending)
|
|
528
|
+
return modules;
|
|
529
|
+
current.appModules.splice(0, current.appModules.length, ...modules);
|
|
530
|
+
return current.appModules;
|
|
531
|
+
}).catch((error) => {
|
|
532
|
+
if (current.modulesReady === pending)
|
|
533
|
+
current.modulesReady = undefined;
|
|
534
|
+
throw error;
|
|
391
535
|
});
|
|
392
|
-
|
|
536
|
+
current.modulesReady = pending;
|
|
537
|
+
return pending;
|
|
393
538
|
}
|
|
394
539
|
function discoverModules() {
|
|
395
|
-
return
|
|
540
|
+
return state().appModules;
|
|
396
541
|
}
|
|
397
542
|
function resetDiscoverModulesForTests() {
|
|
398
|
-
const
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
543
|
+
const current = state();
|
|
544
|
+
current.configuredModulesDir = undefined;
|
|
545
|
+
current.configuredManifest = undefined;
|
|
546
|
+
current.appModules.length = 0;
|
|
547
|
+
current.modulesReady = undefined;
|
|
548
|
+
current.source = undefined;
|
|
402
549
|
}
|
|
403
550
|
// ../../src/bootstrap/prefixRouteMap.ts
|
|
404
551
|
function prefixRouteMap(prefix, routes) {
|
|
@@ -532,7 +679,7 @@ function createWebRoutes(dependencies, options = {}) {
|
|
|
532
679
|
registerRoute("GET", "/assets/*", ["global", "web"]);
|
|
533
680
|
const pathname = new URL(request.url).pathname;
|
|
534
681
|
try {
|
|
535
|
-
const filePath = assertUrlPathUnderRoot(
|
|
682
|
+
const filePath = assertUrlPathUnderRoot(join3(process.cwd(), "public"), pathname);
|
|
536
683
|
const file = Bun.file(filePath);
|
|
537
684
|
if (!await file.exists()) {
|
|
538
685
|
return notFoundHtmlResponse();
|