@jk2908/solas 0.4.3 → 0.5.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/CHANGELOG.md +20 -0
- package/README.md +200 -8
- package/dist/adapters/bun.d.ts +12 -0
- package/dist/adapters/bun.js +39 -0
- package/dist/adapters/node.d.ts +11 -0
- package/dist/adapters/node.js +34 -0
- package/dist/index.js +15 -8
- package/dist/internal/browser-router/link.js +2 -0
- package/dist/internal/{prefetcher.d.ts → browser-router/response-cache.d.ts} +12 -5
- package/dist/internal/{prefetcher.js → browser-router/response-cache.js} +11 -4
- package/dist/internal/browser-router/router.d.ts +1 -0
- package/dist/internal/browser-router/router.js +45 -13
- package/dist/internal/browser-router/shared.d.ts +1 -0
- package/dist/internal/browser-router/shared.js +1 -0
- package/dist/internal/browser-router/use-router.d.ts +1 -0
- package/dist/internal/build.js +14 -13
- package/dist/internal/codegen/config.js +3 -2
- package/dist/internal/codegen/environments.d.ts +2 -1
- package/dist/internal/codegen/environments.js +6 -4
- package/dist/internal/codegen/maps.js +7 -5
- package/dist/internal/env/rsc.d.ts +1 -0
- package/dist/internal/env/rsc.js +1 -0
- package/dist/internal/http-router/router.js +8 -7
- package/dist/internal/postbuild.js +9 -8
- package/dist/internal/prerender.js +14 -13
- package/dist/internal/runtimes/bun.d.ts +9 -0
- package/dist/internal/runtimes/bun.js +33 -0
- package/dist/internal/runtimes/node.d.ts +9 -0
- package/dist/internal/runtimes/node.js +31 -0
- package/dist/internal/runtimes/runtime.d.ts +29 -0
- package/dist/internal/runtimes/runtime.js +32 -0
- package/dist/runtimes/bun.d.ts +13 -0
- package/dist/runtimes/bun.js +39 -0
- package/dist/solas.d.ts +4 -1
- package/dist/solas.js +27 -6
- package/dist/types.d.ts +6 -1
- package/dist/utils/compress.js +2 -2
- package/dist/utils/export-reader.js +69 -58
- package/package.json +9 -5
|
@@ -24,6 +24,7 @@ var BrowserRouter;
|
|
|
24
24
|
*/
|
|
25
25
|
function toTarget(path, params, query) {
|
|
26
26
|
const used = new Set();
|
|
27
|
+
// first, replace all the :param parts with the corresponding params
|
|
27
28
|
let to = path.replaceAll(/:([A-Za-z0-9_]+)/g, (_, key) => {
|
|
28
29
|
const value = params?.[key];
|
|
29
30
|
if (value == null) {
|
package/dist/internal/build.js
CHANGED
|
@@ -4,6 +4,7 @@ import { Logger } from '../utils/logger.js';
|
|
|
4
4
|
import { Solas } from '../solas.js';
|
|
5
5
|
import { normalisePathname } from './http-router/utils.js';
|
|
6
6
|
import { Prerender } from './prerender.js';
|
|
7
|
+
import { Runtime } from './runtimes/runtime.js';
|
|
7
8
|
export { Build };
|
|
8
9
|
/**
|
|
9
10
|
* Types, constants, and the Finder class for route discovery and manifest generation.
|
|
@@ -345,7 +346,7 @@ var Build;
|
|
|
345
346
|
currentPrerenderMode = flag;
|
|
346
347
|
}
|
|
347
348
|
const shellImport = Finder.getImportPath(shellPath);
|
|
348
|
-
const shellId = `${Build.EntryKind.SHELL}${
|
|
349
|
+
const shellId = `${Build.EntryKind.SHELL}${Runtime.hash(shellImport)}`;
|
|
349
350
|
const layoutIds = [];
|
|
350
351
|
const unauthorisedIds = [];
|
|
351
352
|
const forbiddenIds = [];
|
|
@@ -366,7 +367,7 @@ var Build;
|
|
|
366
367
|
continue;
|
|
367
368
|
}
|
|
368
369
|
const layoutImport = Finder.getImportPath(layoutPath);
|
|
369
|
-
const layoutId = `${Build.EntryKind.LAYOUT}${
|
|
370
|
+
const layoutId = `${Build.EntryKind.LAYOUT}${Runtime.hash(layoutImport)}`;
|
|
370
371
|
if (!processed.has(layoutPath)) {
|
|
371
372
|
prerenderCache.set(layoutPath, await Prerender.Build.getStaticFlag(layoutPath, this.buildContext));
|
|
372
373
|
imports.components.dynamic.set(layoutId, layoutImport);
|
|
@@ -381,7 +382,7 @@ var Build;
|
|
|
381
382
|
continue;
|
|
382
383
|
}
|
|
383
384
|
const unauthorisedImport = Finder.getImportPath(unauthorisedPath);
|
|
384
|
-
const unauthorisedId = `${Build.EntryKind['401']}${
|
|
385
|
+
const unauthorisedId = `${Build.EntryKind['401']}${Runtime.hash(unauthorisedImport)}`;
|
|
385
386
|
unauthorisedIds.push(unauthorisedId);
|
|
386
387
|
if (!processed.has(unauthorisedPath)) {
|
|
387
388
|
imports.components.dynamic.set(unauthorisedId, unauthorisedImport);
|
|
@@ -394,7 +395,7 @@ var Build;
|
|
|
394
395
|
continue;
|
|
395
396
|
}
|
|
396
397
|
const forbiddenImport = Finder.getImportPath(forbiddenPath);
|
|
397
|
-
const forbiddenId = `${Build.EntryKind['403']}${
|
|
398
|
+
const forbiddenId = `${Build.EntryKind['403']}${Runtime.hash(forbiddenImport)}`;
|
|
398
399
|
forbiddenIds.push(forbiddenId);
|
|
399
400
|
if (!processed.has(forbiddenPath)) {
|
|
400
401
|
imports.components.dynamic.set(forbiddenId, forbiddenImport);
|
|
@@ -409,7 +410,7 @@ var Build;
|
|
|
409
410
|
continue;
|
|
410
411
|
}
|
|
411
412
|
const notFoundImport = Finder.getImportPath(notFoundPath);
|
|
412
|
-
const notFoundId = `${Build.EntryKind['404']}${
|
|
413
|
+
const notFoundId = `${Build.EntryKind['404']}${Runtime.hash(notFoundImport)}`;
|
|
413
414
|
notFoundIds.push(notFoundId);
|
|
414
415
|
// dedupe imports but still assign the slot for this route
|
|
415
416
|
if (!processed.has(notFoundPath)) {
|
|
@@ -423,7 +424,7 @@ var Build;
|
|
|
423
424
|
continue;
|
|
424
425
|
}
|
|
425
426
|
const serverErrorImport = Finder.getImportPath(serverErrorPath);
|
|
426
|
-
const serverErrorId = `${Build.EntryKind['500']}${
|
|
427
|
+
const serverErrorId = `${Build.EntryKind['500']}${Runtime.hash(serverErrorImport)}`;
|
|
427
428
|
serverErrorIds.push(serverErrorId);
|
|
428
429
|
if (!processed.has(serverErrorPath)) {
|
|
429
430
|
imports.components.dynamic.set(serverErrorId, serverErrorImport);
|
|
@@ -438,7 +439,7 @@ var Build;
|
|
|
438
439
|
continue;
|
|
439
440
|
}
|
|
440
441
|
const loaderImport = Finder.getImportPath(loaderPath);
|
|
441
|
-
const loaderId = `${Build.EntryKind.LOADING}${
|
|
442
|
+
const loaderId = `${Build.EntryKind.LOADING}${Runtime.hash(loaderImport)}`;
|
|
442
443
|
loadingIds.push(loaderId);
|
|
443
444
|
// dedupe imports but still assign the slot for this route
|
|
444
445
|
if (!processed.has(loaderPath)) {
|
|
@@ -452,7 +453,7 @@ var Build;
|
|
|
452
453
|
continue;
|
|
453
454
|
}
|
|
454
455
|
const middlewareImport = Finder.getImportPath(middlewarePath);
|
|
455
|
-
const middlewareId = `${Build.EntryKind.MIDDLEWARE}${
|
|
456
|
+
const middlewareId = `${Build.EntryKind.MIDDLEWARE}${Runtime.hash(middlewareImport)}`;
|
|
456
457
|
middlewareIds.push(middlewareId);
|
|
457
458
|
if (!processed.has(middlewarePath)) {
|
|
458
459
|
// route scanning only tells us this is a +middleware file path
|
|
@@ -466,8 +467,8 @@ var Build;
|
|
|
466
467
|
}
|
|
467
468
|
// generate entry id based on page if exists, otherwise dir
|
|
468
469
|
const entryId = pagePath
|
|
469
|
-
? `${Build.EntryKind.PAGE}${
|
|
470
|
-
: `${Build.EntryKind.PAGE}${
|
|
470
|
+
? `${Build.EntryKind.PAGE}${Runtime.hash(Finder.getImportPath(pagePath))}`
|
|
471
|
+
: `${Build.EntryKind.PAGE}${Runtime.hash(route)}`;
|
|
471
472
|
if (pagePath) {
|
|
472
473
|
const pagePrerender = await Prerender.Build.getStaticFlag(pagePath, this.buildContext);
|
|
473
474
|
applyPrerenderMode(pagePrerender);
|
|
@@ -568,12 +569,12 @@ var Build;
|
|
|
568
569
|
continue;
|
|
569
570
|
}
|
|
570
571
|
const m = method.toLowerCase();
|
|
571
|
-
const endpointId = `${Build.EntryKind.ENDPOINT}${
|
|
572
|
+
const endpointId = `${Build.EntryKind.ENDPOINT}${Runtime.hash(Finder.getImportPath(endpointFilePath))}_${m}`;
|
|
572
573
|
const middlewareIds = await Promise.all(endpointMiddlewarePaths.map(async middlewarePath => {
|
|
573
574
|
if (!middlewarePath)
|
|
574
575
|
return null;
|
|
575
576
|
const middlewareImport = Finder.getImportPath(middlewarePath);
|
|
576
|
-
const middlewareId = `${Build.EntryKind.MIDDLEWARE}${
|
|
577
|
+
const middlewareId = `${Build.EntryKind.MIDDLEWARE}${Runtime.hash(middlewareImport)}`;
|
|
577
578
|
if (!processed.has(middlewarePath)) {
|
|
578
579
|
// endpoint middleware discovery gives us file paths, not proof
|
|
579
580
|
// of the export so check the module shape first
|
|
@@ -605,7 +606,7 @@ var Build;
|
|
|
605
606
|
modules[route] = {
|
|
606
607
|
...(modules[route] ?? {}),
|
|
607
608
|
middlewareIds: endpointMiddlewarePaths.map(middlewarePath => middlewarePath
|
|
608
|
-
? `${Build.EntryKind.MIDDLEWARE}${
|
|
609
|
+
? `${Build.EntryKind.MIDDLEWARE}${Runtime.hash(Finder.getImportPath(middlewarePath))}`
|
|
609
610
|
: null),
|
|
610
611
|
};
|
|
611
612
|
}
|
|
@@ -4,14 +4,15 @@ import { AUTOGEN_MSG, source, toSourceLiteral } from './utils.js';
|
|
|
4
4
|
* Generates the code to create an exported config object
|
|
5
5
|
*/
|
|
6
6
|
export function writeConfig(config) {
|
|
7
|
+
const { runtime: _runtime, ...runtimeConfig } = config;
|
|
7
8
|
const loggerLevel = config.logger?.level;
|
|
8
9
|
const importLines = [
|
|
9
|
-
`import type {
|
|
10
|
+
`import type { RuntimeConfig } from '${Solas.Config.PKG_NAME}'`,
|
|
10
11
|
loggerLevel ? `import { Logger } from '${Solas.Config.PKG_NAME}/utils/logger'` : '',
|
|
11
12
|
]
|
|
12
13
|
.filter(Boolean)
|
|
13
14
|
.join('\n');
|
|
14
|
-
const configStatement = `const config = ${toSourceLiteral(
|
|
15
|
+
const configStatement = `const config = ${toSourceLiteral(runtimeConfig)} as const satisfies RuntimeConfig`;
|
|
15
16
|
const loggerStatement = loggerLevel
|
|
16
17
|
? `Logger.defaultLevel = ${toSourceLiteral(loggerLevel)}`
|
|
17
18
|
: '';
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import type { ConfiguredPluginConfig } from '../../types.js';
|
|
1
2
|
/**
|
|
2
3
|
* Generates the RSC entry code
|
|
3
4
|
*/
|
|
4
|
-
export declare function writeRSCEntry(): string;
|
|
5
|
+
export declare function writeRSCEntry(config: ConfiguredPluginConfig): string;
|
|
5
6
|
/**
|
|
6
7
|
* Generates the SSR entry code
|
|
7
8
|
*/
|
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
import { Solas } from '../../solas.js';
|
|
2
|
-
import { AUTOGEN_MSG, source } from './utils.js';
|
|
2
|
+
import { AUTOGEN_MSG, source, toStringLiteral } from './utils.js';
|
|
3
3
|
/**
|
|
4
4
|
* Generates the RSC entry code
|
|
5
5
|
*/
|
|
6
|
-
export function writeRSCEntry() {
|
|
6
|
+
export function writeRSCEntry(config) {
|
|
7
|
+
const runtime = toStringLiteral(config.runtime);
|
|
7
8
|
return source `
|
|
8
9
|
${AUTOGEN_MSG}
|
|
9
10
|
|
|
10
|
-
import { createHandler } from '${Solas.Config.PKG_NAME}/env/rsc'
|
|
11
|
-
import { Solas } from '${Solas.Config.PKG_NAME}'
|
|
11
|
+
import { createHandler, Runtime } from '${Solas.Config.PKG_NAME}/env/rsc'
|
|
12
|
+
import { Solas } from '${Solas.Config.PKG_NAME}/$'
|
|
12
13
|
|
|
13
14
|
import { manifest } from './manifest.js'
|
|
14
15
|
import { importMap } from './maps.js'
|
|
15
16
|
import { config } from './config.js'
|
|
16
17
|
|
|
18
|
+
Runtime.runtime = Solas.Runtime.create(${runtime})
|
|
17
19
|
const runtimeManifest = await Solas.Runtime.loadManifest(Solas.Config.OUT_DIR)
|
|
18
20
|
|
|
19
21
|
export default createHandler(config, manifest, importMap, runtimeManifest)
|
|
@@ -37,8 +37,8 @@ export function writeMaps(imports, modules) {
|
|
|
37
37
|
parts.push(`endpoint: ${toIdentifier(m.endpointId, `endpoint id for ${moduleId}`)}`);
|
|
38
38
|
}
|
|
39
39
|
if (m['401Ids']?.length) {
|
|
40
|
-
const
|
|
41
|
-
parts.push(`'401s': [${
|
|
40
|
+
const unauthorised = toIdentifierList(m['401Ids'], `401s for ${moduleId}`);
|
|
41
|
+
parts.push(`'401s': [${unauthorised}]`);
|
|
42
42
|
}
|
|
43
43
|
if (m['403Ids']?.length) {
|
|
44
44
|
const forbidden = toIdentifierList(m['403Ids'], `403s for ${moduleId}`);
|
|
@@ -70,13 +70,15 @@ export function writeMaps(imports, modules) {
|
|
|
70
70
|
${AUTOGEN_MSG}
|
|
71
71
|
|
|
72
72
|
import type { ImportMap } from '${Solas.Config.PKG_NAME}'
|
|
73
|
-
|
|
73
|
+
|
|
74
|
+
${importLines
|
|
74
75
|
? `
|
|
75
|
-
${importLines}
|
|
76
|
+
${importLines}
|
|
77
|
+
`
|
|
76
78
|
: ''}
|
|
77
79
|
|
|
78
80
|
export const importMap = {
|
|
79
|
-
${entries}
|
|
81
|
+
${entries}
|
|
80
82
|
} as const satisfies ImportMap
|
|
81
83
|
`;
|
|
82
84
|
}
|
|
@@ -2,6 +2,7 @@ import type { ReactFormState } from 'react-dom/client';
|
|
|
2
2
|
import type { ImportMap, Manifest, RuntimeConfig } from '../../types.js';
|
|
3
3
|
import { Solas } from '../../solas.js';
|
|
4
4
|
import { Metadata } from '../metadata.js';
|
|
5
|
+
export { Runtime } from '../runtimes/runtime.js';
|
|
5
6
|
export type RscPayload = {
|
|
6
7
|
returnValue?: {
|
|
7
8
|
ok: boolean;
|
package/dist/internal/env/rsc.js
CHANGED
|
@@ -17,6 +17,7 @@ import { processActionRequest } from '../server/actions.js';
|
|
|
17
17
|
import DefaultErr from '../ui/defaults/error.js';
|
|
18
18
|
import { RequestContext } from './request-context.js';
|
|
19
19
|
import { getKnownDigest, isKnownError } from './utils.js';
|
|
20
|
+
export { Runtime } from '../runtimes/runtime.js';
|
|
20
21
|
const logger = new Logger();
|
|
21
22
|
const BASE_PATH = BasePath.normalise(import.meta.env.BASE_URL);
|
|
22
23
|
function resolveFilePath(root, relativePath) {
|
|
@@ -2,6 +2,7 @@ import { match as createMatch } from 'path-to-regexp';
|
|
|
2
2
|
import { BasePath } from '../../utils/base-path.js';
|
|
3
3
|
import { Solas } from '../../solas.js';
|
|
4
4
|
import { HttpException } from '../navigation/http-exception.js';
|
|
5
|
+
import { Runtime } from '../runtimes/runtime.js';
|
|
5
6
|
import { maybeAction } from '../server/actions.js';
|
|
6
7
|
import { enforce } from '../server/csrf.js';
|
|
7
8
|
import { getAlternatePathname, normalisePathname, toPathPattern } from './utils.js';
|
|
@@ -253,24 +254,24 @@ export class HttpRouter {
|
|
|
253
254
|
*/
|
|
254
255
|
static async serveStatic(filePath, req, precompress = false, headers = {}) {
|
|
255
256
|
const accept = req.headers.get('accept-encoding') ?? '';
|
|
256
|
-
let
|
|
257
|
+
let resolvedPath = filePath;
|
|
257
258
|
let encoding = null;
|
|
258
259
|
if (precompress) {
|
|
259
260
|
// prefer a precompressed variant when the client accepts it and one was emitted
|
|
260
261
|
if (accept.includes('br')) {
|
|
261
|
-
const
|
|
262
|
-
if (await
|
|
263
|
-
|
|
262
|
+
const brotliPath = `${filePath}.br`;
|
|
263
|
+
if (await Runtime.exists(brotliPath)) {
|
|
264
|
+
resolvedPath = brotliPath;
|
|
264
265
|
encoding = 'br';
|
|
265
266
|
}
|
|
266
267
|
}
|
|
267
268
|
}
|
|
268
|
-
if (!(await
|
|
269
|
+
if (!(await Runtime.exists(resolvedPath))) {
|
|
269
270
|
return new Response('Not found', { status: 404 });
|
|
270
271
|
}
|
|
271
272
|
// get mime type from original path, not compressed variant
|
|
272
|
-
const mimeType =
|
|
273
|
-
const res = new Response(
|
|
273
|
+
const mimeType = Runtime.mimeType(filePath);
|
|
274
|
+
const res = new Response(await Runtime.readBuffer(resolvedPath), {
|
|
274
275
|
headers: {
|
|
275
276
|
'Content-Type': headers['Content-Type'] ?? mimeType,
|
|
276
277
|
},
|
|
@@ -4,6 +4,7 @@ import { Compress } from '../utils/compress.js';
|
|
|
4
4
|
import { Logger } from '../utils/logger.js';
|
|
5
5
|
import { Solas } from '../solas.js';
|
|
6
6
|
import { Prerender } from './prerender.js';
|
|
7
|
+
import { Runtime } from './runtimes/runtime.js';
|
|
7
8
|
const logger = new Logger();
|
|
8
9
|
export async function postbuild(cwd = process.cwd()) {
|
|
9
10
|
const manifestPath = path.join(cwd, Solas.Config.GENERATED_DIR, 'build.json');
|
|
@@ -59,8 +60,8 @@ export async function postbuild(cwd = process.cwd()) {
|
|
|
59
60
|
if (artifact.mode === 'ppr') {
|
|
60
61
|
await fs.mkdir(artifactDir, { recursive: true });
|
|
61
62
|
const writes = [
|
|
62
|
-
|
|
63
|
-
|
|
63
|
+
Runtime.write(path.join(artifactDir, 'prelude.html'), artifact.html),
|
|
64
|
+
Runtime.write(path.join(artifactDir, 'metadata.json'), JSON.stringify({
|
|
64
65
|
schema: artifact.schema,
|
|
65
66
|
route: artifact.route,
|
|
66
67
|
createdAt: artifact.createdAt,
|
|
@@ -68,7 +69,7 @@ export async function postbuild(cwd = process.cwd()) {
|
|
|
68
69
|
})),
|
|
69
70
|
];
|
|
70
71
|
if (artifact.postponed !== undefined) {
|
|
71
|
-
writes.push(
|
|
72
|
+
writes.push(Runtime.write(path.join(artifactDir, 'postponed.json'), JSON.stringify(artifact.postponed)));
|
|
72
73
|
}
|
|
73
74
|
await Promise.all(writes);
|
|
74
75
|
artifactManifest[route] = {
|
|
@@ -82,13 +83,13 @@ export async function postbuild(cwd = process.cwd()) {
|
|
|
82
83
|
}
|
|
83
84
|
await fs.mkdir(artifactDir, { recursive: true });
|
|
84
85
|
await Promise.all([
|
|
85
|
-
|
|
86
|
+
Runtime.write(path.join(artifactDir, 'metadata.json'), JSON.stringify({
|
|
86
87
|
schema: artifact.schema,
|
|
87
88
|
route: artifact.route,
|
|
88
89
|
createdAt: artifact.createdAt,
|
|
89
90
|
mode: artifact.mode,
|
|
90
91
|
})),
|
|
91
|
-
|
|
92
|
+
Runtime.write(Prerender.Artifact.getFilePath(outDir, route, Prerender.Artifact.FULL_PRERENDER_FILENAME), artifact.html),
|
|
92
93
|
]);
|
|
93
94
|
artifactManifest[route] = {
|
|
94
95
|
mode: artifact.mode,
|
|
@@ -108,7 +109,7 @@ export async function postbuild(cwd = process.cwd()) {
|
|
|
108
109
|
artifacts: artifactManifest,
|
|
109
110
|
publicFiles: manifest.publicFiles,
|
|
110
111
|
};
|
|
111
|
-
await
|
|
112
|
+
await Runtime.write(Solas.Runtime.getManifestPath(outDir), JSON.stringify(runtimeManifest));
|
|
112
113
|
if (manifest.sitemapRoutes.length > 0 && manifest.url) {
|
|
113
114
|
const origin = manifest.url.replace(/\/$/, '');
|
|
114
115
|
const urls = manifest.sitemapRoutes
|
|
@@ -120,7 +121,7 @@ export async function postbuild(cwd = process.cwd()) {
|
|
|
120
121
|
urls,
|
|
121
122
|
'</urlset>',
|
|
122
123
|
].join('\n');
|
|
123
|
-
await
|
|
124
|
+
await Runtime.write(path.join(outDir, 'sitemap.xml'), sitemap);
|
|
124
125
|
logger.info('[sitemap]', `generated ${manifest.sitemapRoutes.length} urls`);
|
|
125
126
|
}
|
|
126
127
|
if (manifest.precompress) {
|
|
@@ -144,7 +145,7 @@ export async function postbuild(cwd = process.cwd()) {
|
|
|
144
145
|
normalisedPath.endsWith(`/${Prerender.Artifact.FULL_PRERENDER_FILENAME}`));
|
|
145
146
|
},
|
|
146
147
|
})) {
|
|
147
|
-
await
|
|
148
|
+
await Runtime.write(`${input}.br`, compressed);
|
|
148
149
|
logger.info('[precompress]', `${path.basename(input)}.br`);
|
|
149
150
|
}
|
|
150
151
|
}
|
|
@@ -4,6 +4,7 @@ import { BasePath } from '../utils/base-path.js';
|
|
|
4
4
|
import { Logger } from '../utils/logger.js';
|
|
5
5
|
import { Solas } from '../solas.js';
|
|
6
6
|
import { toPathPattern } from './http-router/utils.js';
|
|
7
|
+
import { Runtime as AdapterRuntime } from './runtimes/runtime.js';
|
|
7
8
|
const logger = new Logger();
|
|
8
9
|
export { Prerender };
|
|
9
10
|
var Prerender;
|
|
@@ -60,18 +61,18 @@ var Prerender;
|
|
|
60
61
|
* Load the postponed state for a given route from the file system, if it exists
|
|
61
62
|
*/
|
|
62
63
|
async function loadPostponedState(outDir, pathname) {
|
|
63
|
-
let
|
|
64
|
+
let filePath;
|
|
64
65
|
try {
|
|
65
|
-
|
|
66
|
+
filePath = path.join(getPath(outDir, pathname), 'postponed.json');
|
|
66
67
|
}
|
|
67
68
|
catch (err) {
|
|
68
69
|
logger.warn(`[prerender:artifacts] rejected postponed state path for ${pathname}`, Logger.print(err));
|
|
69
70
|
return null;
|
|
70
71
|
}
|
|
71
|
-
if (!(await
|
|
72
|
+
if (!(await AdapterRuntime.exists(filePath)))
|
|
72
73
|
return null;
|
|
73
74
|
try {
|
|
74
|
-
return JSON.parse(await
|
|
75
|
+
return JSON.parse(await AdapterRuntime.readText(filePath));
|
|
75
76
|
}
|
|
76
77
|
catch {
|
|
77
78
|
return null;
|
|
@@ -82,18 +83,18 @@ var Prerender;
|
|
|
82
83
|
* Load the prelude HTML for a given route from the file system, if it exists
|
|
83
84
|
*/
|
|
84
85
|
async function loadPrelude(outDir, pathname) {
|
|
85
|
-
let
|
|
86
|
+
let filePath;
|
|
86
87
|
try {
|
|
87
|
-
|
|
88
|
+
filePath = path.join(getPath(outDir, pathname), 'prelude.html');
|
|
88
89
|
}
|
|
89
90
|
catch (err) {
|
|
90
91
|
logger.warn(`[prerender:artifacts] rejected prelude path for ${pathname}`, Logger.print(err));
|
|
91
92
|
return null;
|
|
92
93
|
}
|
|
93
|
-
if (!(await
|
|
94
|
+
if (!(await AdapterRuntime.exists(filePath)))
|
|
94
95
|
return null;
|
|
95
96
|
try {
|
|
96
|
-
return await
|
|
97
|
+
return await AdapterRuntime.readText(filePath);
|
|
97
98
|
}
|
|
98
99
|
catch {
|
|
99
100
|
return null;
|
|
@@ -104,18 +105,18 @@ var Prerender;
|
|
|
104
105
|
* Load the prerender artifact metadata for a given route from the file system, if it exists and is valid
|
|
105
106
|
*/
|
|
106
107
|
async function loadMetadata(outDir, pathname) {
|
|
107
|
-
let
|
|
108
|
+
let filePath;
|
|
108
109
|
try {
|
|
109
|
-
|
|
110
|
+
filePath = path.join(getPath(outDir, pathname), 'metadata.json');
|
|
110
111
|
}
|
|
111
112
|
catch (err) {
|
|
112
113
|
logger.warn(`[prerender:artifacts] rejected metadata path for ${pathname}`, Logger.print(err));
|
|
113
114
|
return null;
|
|
114
115
|
}
|
|
115
|
-
if (!(await
|
|
116
|
+
if (!(await AdapterRuntime.exists(filePath)))
|
|
116
117
|
return null;
|
|
117
118
|
try {
|
|
118
|
-
const value = JSON.parse(await
|
|
119
|
+
const value = JSON.parse(await AdapterRuntime.readText(filePath));
|
|
119
120
|
if (!value || typeof value !== 'object')
|
|
120
121
|
return null;
|
|
121
122
|
const schema = value.schema;
|
|
@@ -270,7 +271,7 @@ var Prerender;
|
|
|
270
271
|
const params = await buildContext.exportReader.value(filePath, 'params', (v) => typeof v === 'function');
|
|
271
272
|
if (!params)
|
|
272
273
|
return [];
|
|
273
|
-
const resolved = await Promise.
|
|
274
|
+
const resolved = await Promise.resolve().then(() => params());
|
|
274
275
|
if (!Array.isArray(resolved))
|
|
275
276
|
return [];
|
|
276
277
|
return resolved;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { RuntimeBase } from './runtime.js';
|
|
2
|
+
export declare class RuntimeBun extends RuntimeBase {
|
|
3
|
+
exists(filePath: string): Promise<boolean>;
|
|
4
|
+
readText(filePath: string): Promise<string>;
|
|
5
|
+
readBuffer(filePath: string): Promise<ArrayBuffer>;
|
|
6
|
+
mimeType(filePath: string): string;
|
|
7
|
+
write(filePath: string, content: string | NodeJS.ArrayBufferView): Promise<void>;
|
|
8
|
+
hash(value: string): string;
|
|
9
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { RuntimeBase } from './runtime.js';
|
|
2
|
+
export class RuntimeBun extends RuntimeBase {
|
|
3
|
+
async exists(filePath) {
|
|
4
|
+
return Bun.file(filePath).exists();
|
|
5
|
+
}
|
|
6
|
+
readText(filePath) {
|
|
7
|
+
return Bun.file(filePath).text();
|
|
8
|
+
}
|
|
9
|
+
readBuffer(filePath) {
|
|
10
|
+
return Bun.file(filePath).arrayBuffer();
|
|
11
|
+
}
|
|
12
|
+
mimeType(filePath) {
|
|
13
|
+
return Bun.file(filePath).type || 'application/octet-stream';
|
|
14
|
+
}
|
|
15
|
+
async write(filePath, content) {
|
|
16
|
+
// normalise wider arraybuffer views into a shape Bun.write accepts directly
|
|
17
|
+
await Bun.write(filePath, typeof content === 'string'
|
|
18
|
+
? content
|
|
19
|
+
: content instanceof Uint8Array
|
|
20
|
+
? content
|
|
21
|
+
: new Uint8Array(content.buffer, content.byteOffset, content.byteLength));
|
|
22
|
+
}
|
|
23
|
+
hash(value) {
|
|
24
|
+
const hash = Bun.hash(value);
|
|
25
|
+
// Bun.hash returns an integer-like value, so keep it in BigInt space and avoid
|
|
26
|
+
// precision loss. Clamp it to an unsigned 64-bit value before hex
|
|
27
|
+
// formatting. Pad to 16 chars to match the Node runtime
|
|
28
|
+
// output shape
|
|
29
|
+
return BigInt.asUintN(64, typeof hash === 'bigint' ? hash : BigInt(hash))
|
|
30
|
+
.toString(16)
|
|
31
|
+
.padStart(16, '0');
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { RuntimeBase } from './runtime.js';
|
|
2
|
+
export declare class RuntimeNode extends RuntimeBase {
|
|
3
|
+
exists(filePath: string): Promise<boolean>;
|
|
4
|
+
readText(filePath: string): Promise<string>;
|
|
5
|
+
readBuffer(filePath: string): Promise<ArrayBuffer>;
|
|
6
|
+
mimeType(filePath: string): string;
|
|
7
|
+
write(filePath: string, content: string | NodeJS.ArrayBufferView): Promise<void>;
|
|
8
|
+
hash(value: string): string;
|
|
9
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
import { lookup } from 'mime-types';
|
|
4
|
+
import { RuntimeBase } from './runtime.js';
|
|
5
|
+
export class RuntimeNode extends RuntimeBase {
|
|
6
|
+
async exists(filePath) {
|
|
7
|
+
try {
|
|
8
|
+
await fs.access(filePath);
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
readText(filePath) {
|
|
16
|
+
return fs.readFile(filePath, 'utf-8');
|
|
17
|
+
}
|
|
18
|
+
async readBuffer(filePath) {
|
|
19
|
+
const buffer = await fs.readFile(filePath);
|
|
20
|
+
return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
|
|
21
|
+
}
|
|
22
|
+
mimeType(filePath) {
|
|
23
|
+
return lookup(filePath) || 'application/octet-stream';
|
|
24
|
+
}
|
|
25
|
+
async write(filePath, content) {
|
|
26
|
+
await fs.writeFile(filePath, content);
|
|
27
|
+
}
|
|
28
|
+
hash(value) {
|
|
29
|
+
return createHash('sha256').update(value).digest('hex').slice(0, 16);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export declare namespace Runtime {
|
|
2
|
+
type Impl = {
|
|
3
|
+
exists: (filePath: string) => Promise<boolean>;
|
|
4
|
+
readText: (filePath: string) => Promise<string>;
|
|
5
|
+
readBuffer: (filePath: string) => Promise<ArrayBuffer>;
|
|
6
|
+
mimeType: (filePath: string) => string;
|
|
7
|
+
write: (filePath: string, content: string | NodeJS.ArrayBufferView) => Promise<void>;
|
|
8
|
+
hash: (value: string) => string;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export declare abstract class RuntimeBase implements Runtime.Impl {
|
|
12
|
+
abstract exists(filePath: string): Promise<boolean>;
|
|
13
|
+
abstract readText(filePath: string): Promise<string>;
|
|
14
|
+
abstract readBuffer(filePath: string): Promise<ArrayBuffer>;
|
|
15
|
+
abstract mimeType(filePath: string): string;
|
|
16
|
+
abstract write(filePath: string, content: string | NodeJS.ArrayBufferView): Promise<void>;
|
|
17
|
+
abstract hash(value: string): string;
|
|
18
|
+
}
|
|
19
|
+
export declare class Runtime {
|
|
20
|
+
#private;
|
|
21
|
+
static set runtime(runtime: Runtime.Impl);
|
|
22
|
+
static get runtime(): Runtime.Impl;
|
|
23
|
+
static exists(filePath: string): Promise<boolean>;
|
|
24
|
+
static readText(filePath: string): Promise<string>;
|
|
25
|
+
static readBuffer(filePath: string): Promise<ArrayBuffer>;
|
|
26
|
+
static mimeType(filePath: string): string;
|
|
27
|
+
static write(filePath: string, content: string | NodeJS.ArrayBufferView): Promise<void>;
|
|
28
|
+
static hash(value: string): string;
|
|
29
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export class RuntimeBase {
|
|
2
|
+
}
|
|
3
|
+
export class Runtime {
|
|
4
|
+
static #runtime;
|
|
5
|
+
static set runtime(runtime) {
|
|
6
|
+
this.#runtime = runtime;
|
|
7
|
+
}
|
|
8
|
+
static get runtime() {
|
|
9
|
+
if (!this.#runtime) {
|
|
10
|
+
throw new Error('No runtime configured. Please set the runtime implementation before using runtime methods.');
|
|
11
|
+
}
|
|
12
|
+
return this.#runtime;
|
|
13
|
+
}
|
|
14
|
+
static exists(filePath) {
|
|
15
|
+
return this.runtime.exists(filePath);
|
|
16
|
+
}
|
|
17
|
+
static readText(filePath) {
|
|
18
|
+
return this.runtime.readText(filePath);
|
|
19
|
+
}
|
|
20
|
+
static readBuffer(filePath) {
|
|
21
|
+
return this.runtime.readBuffer(filePath);
|
|
22
|
+
}
|
|
23
|
+
static mimeType(filePath) {
|
|
24
|
+
return this.runtime.mimeType(filePath);
|
|
25
|
+
}
|
|
26
|
+
static write(filePath, content) {
|
|
27
|
+
return this.runtime.write(filePath, content);
|
|
28
|
+
}
|
|
29
|
+
static hash(value) {
|
|
30
|
+
return this.runtime.hash(value);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { RuntimeBase } from '../internal/runtimes/runtime.js';
|
|
2
|
+
declare class BunAdapter extends RuntimeBase {
|
|
3
|
+
readonly name: "bun";
|
|
4
|
+
readonly module: string;
|
|
5
|
+
exists(filePath: string): Promise<boolean>;
|
|
6
|
+
readText(filePath: string): Promise<string>;
|
|
7
|
+
readBuffer(filePath: string): Promise<ArrayBuffer>;
|
|
8
|
+
mimeType(filePath: string): string;
|
|
9
|
+
write(filePath: string, content: string | NodeJS.ArrayBufferView): Promise<void>;
|
|
10
|
+
hash(value: string): string;
|
|
11
|
+
}
|
|
12
|
+
export default function bunAdapter(): BunAdapter;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { RuntimeBase } from '../internal/runtimes/runtime.js';
|
|
2
|
+
import { Solas } from '../solas.js';
|
|
3
|
+
class BunAdapter extends RuntimeBase {
|
|
4
|
+
name = 'bun';
|
|
5
|
+
module = `${Solas.Config.PKG_NAME}/runtimes/bun`;
|
|
6
|
+
async exists(filePath) {
|
|
7
|
+
return Bun.file(filePath).exists();
|
|
8
|
+
}
|
|
9
|
+
readText(filePath) {
|
|
10
|
+
return Bun.file(filePath).text();
|
|
11
|
+
}
|
|
12
|
+
readBuffer(filePath) {
|
|
13
|
+
return Bun.file(filePath).arrayBuffer();
|
|
14
|
+
}
|
|
15
|
+
mimeType(filePath) {
|
|
16
|
+
return Bun.file(filePath).type || 'application/octet-stream';
|
|
17
|
+
}
|
|
18
|
+
async write(filePath, content) {
|
|
19
|
+
// normalise wider arraybuffer views into a shape Bun.write accepts directly
|
|
20
|
+
await Bun.write(filePath, typeof content === 'string'
|
|
21
|
+
? content
|
|
22
|
+
: content instanceof Uint8Array
|
|
23
|
+
? content
|
|
24
|
+
: new Uint8Array(content.buffer, content.byteOffset, content.byteLength));
|
|
25
|
+
}
|
|
26
|
+
hash(value) {
|
|
27
|
+
const hash = Bun.hash(value);
|
|
28
|
+
// Bun.hash returns an integer-like value, so keep it in BigInt space and avoid
|
|
29
|
+
// precision loss. Clamp it to an unsigned 64-bit value before hex
|
|
30
|
+
// formatting. Pad to 16 chars to match the Node adapter
|
|
31
|
+
// output shape
|
|
32
|
+
return BigInt.asUintN(64, typeof hash === 'bigint' ? hash : BigInt(hash))
|
|
33
|
+
.toString(16)
|
|
34
|
+
.padStart(16, '0');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export default function bunAdapter() {
|
|
38
|
+
return new BunAdapter();
|
|
39
|
+
}
|