@jk2908/solas 0.5.4 → 0.7.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 +30 -0
- package/README.md +34 -94
- package/dist/config.d.ts +24 -0
- package/dist/config.js +143 -0
- package/dist/events.d.ts +4 -0
- package/dist/events.js +5 -0
- package/dist/index.d.ts +1 -2
- package/dist/index.js +59 -41
- package/dist/internal/boundary-error.d.ts +4 -0
- package/dist/internal/boundary-error.js +8 -0
- package/dist/internal/browser-router/history.d.ts +1 -1
- package/dist/internal/browser-router/link.d.ts +1 -1
- package/dist/internal/browser-router/link.js +1 -1
- package/dist/internal/browser-router/router.d.ts +2 -2
- package/dist/internal/browser-router/router.js +5 -5
- package/dist/internal/browser-router/shared.d.ts +182 -184
- package/dist/internal/browser-router/shared.js +49 -56
- package/dist/internal/browser-router/use-router.d.ts +1 -1
- package/dist/internal/browser-router/use-search-params.js +3 -3
- package/dist/internal/build.d.ts +108 -110
- package/dist/internal/build.js +569 -581
- package/dist/internal/codegen/config.js +4 -5
- package/dist/internal/codegen/environments.d.ts +1 -1
- package/dist/internal/codegen/environments.js +6 -9
- package/dist/internal/codegen/format.d.ts +6 -0
- package/dist/internal/codegen/format.js +24 -0
- package/dist/internal/codegen/manifest.js +2 -2
- package/dist/internal/codegen/maps.d.ts +1 -1
- package/dist/internal/codegen/maps.js +2 -2
- package/dist/internal/codegen/types.js +4 -4
- package/dist/internal/codegen/utils.js +2 -2
- package/dist/internal/env/request-context.js +1 -1
- package/dist/internal/env/rsc.d.ts +3 -3
- package/dist/internal/env/rsc.js +26 -26
- package/dist/internal/env/ssr.js +4 -4
- package/dist/internal/http-router/router.js +8 -8
- package/dist/internal/metadata.d.ts +1 -1
- package/dist/internal/metadata.js +1 -1
- package/dist/internal/navigation/http-exception-boundary.d.ts +1 -1
- package/dist/internal/navigation/redirect-boundary.d.ts +1 -1
- package/dist/internal/navigation/redirect.js +6 -6
- package/dist/internal/postbuild.js +18 -17
- package/dist/internal/prerender.d.ts +130 -138
- package/dist/internal/prerender.js +326 -354
- package/dist/internal/public-files.js +2 -4
- package/dist/internal/render/head.js +2 -2
- package/dist/internal/resolver.js +1 -1
- package/dist/internal/runtimes/runtime.d.ts +7 -21
- package/dist/internal/runtimes/runtime.js +24 -19
- package/dist/internal/server/actions.js +2 -2
- package/dist/internal/ui/error-boundary.d.ts +1 -1
- package/dist/manifest.d.ts +7 -0
- package/dist/manifest.js +85 -0
- package/dist/prerender.d.ts +1 -1
- package/dist/prerender.js +1 -1
- package/dist/solas.d.ts +2 -46
- package/dist/solas.js +6 -266
- package/dist/types.d.ts +14 -25
- package/dist/types.js +1 -1
- package/dist/utils/base-path.d.ts +12 -14
- package/dist/utils/base-path.js +75 -82
- package/dist/utils/compress.d.ts +9 -11
- package/dist/utils/compress.js +59 -64
- package/dist/utils/context.d.ts +4 -6
- package/dist/utils/context.js +17 -22
- package/dist/utils/logger.js +2 -2
- package/dist/utils/time.d.ts +2 -4
- package/dist/utils/time.js +23 -29
- package/package.json +18 -8
- package/dist/internal/runtimes/bun.d.ts +0 -9
- package/dist/internal/runtimes/bun.js +0 -33
- package/dist/internal/runtimes/create.d.ts +0 -4
- package/dist/internal/runtimes/create.js +0 -9
- package/dist/internal/runtimes/node.d.ts +0 -9
- package/dist/internal/runtimes/node.js +0 -31
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { promises as fs } from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import
|
|
3
|
+
import * as Config from '../config.js';
|
|
4
4
|
/**
|
|
5
5
|
* Collect the root request paths for files that originate in Vite's public dir.
|
|
6
6
|
* Vite copies these files into the built client output unchanged. Solas stores
|
|
@@ -37,9 +37,7 @@ export async function collect(root) {
|
|
|
37
37
|
for (const entry of entries) {
|
|
38
38
|
// top-level /public/_solas would collide with framework assets served
|
|
39
39
|
// from /_solas, so keep that namespace reserved
|
|
40
|
-
if (parts.length === 0 &&
|
|
41
|
-
entry.isDirectory() &&
|
|
42
|
-
entry.name === Solas.Config.ASSETS_DIR) {
|
|
40
|
+
if (parts.length === 0 && entry.isDirectory() && entry.name === Config.ASSETS_DIR) {
|
|
43
41
|
continue;
|
|
44
42
|
}
|
|
45
43
|
const nextParts = [...parts, entry.name];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { use } from 'react';
|
|
3
3
|
import { Logger } from '../../utils/logger.js';
|
|
4
|
-
import
|
|
4
|
+
import * as Config from '../../config.js';
|
|
5
5
|
const logger = new Logger();
|
|
6
6
|
const cache = new WeakMap();
|
|
7
7
|
/**
|
|
@@ -37,7 +37,7 @@ export function Head({ metadata: m, }) {
|
|
|
37
37
|
return null;
|
|
38
38
|
const metadata = use(toSafeUsable(m));
|
|
39
39
|
return (_jsxs(_Fragment, { children: [
|
|
40
|
-
_jsx("meta", { name: "generator", content:
|
|
40
|
+
_jsx("meta", { name: "generator", content: Config.NAME }), toContent(metadata.title) !== undefined && (_jsx("title", { children: toContent(metadata.title) })), metadata.meta?.map(meta => {
|
|
41
41
|
if ('charSet' in meta) {
|
|
42
42
|
return _jsx("meta", { charSet: meta.charSet }, meta.charSet);
|
|
43
43
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { lazy } from 'react';
|
|
2
2
|
import { Logger } from '../utils/logger.js';
|
|
3
|
-
import
|
|
3
|
+
import * as Build from './build.js';
|
|
4
4
|
import { Metadata } from './metadata.js';
|
|
5
5
|
import { HttpException, isHttpException } from './navigation/http-exception.js';
|
|
6
6
|
const logger = new Logger();
|
|
@@ -1,25 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* A single filesystem/hash/mime implementation shared by every runtime.
|
|
3
|
+
*
|
|
4
|
+
* Bun runs the Node standard library natively, so there is no need for
|
|
5
|
+
* per-runtime adapters: `node:fs` and `node:crypto` behave identically under
|
|
6
|
+
* Bun and Node, and mime lookups go through the same table for both.
|
|
7
|
+
*/
|
|
19
8
|
export declare class Runtime {
|
|
20
|
-
#private;
|
|
21
|
-
static set runtime(runtime: Runtime.Impl);
|
|
22
|
-
static get runtime(): Runtime.Impl;
|
|
23
9
|
static exists(filePath: string): Promise<boolean>;
|
|
24
10
|
static readText(filePath: string): Promise<string>;
|
|
25
11
|
static readBuffer(filePath: string): Promise<ArrayBuffer>;
|
|
@@ -1,32 +1,37 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
import { getMimeTypeFromPath } from './mime.js';
|
|
4
|
+
/**
|
|
5
|
+
* A single filesystem/hash/mime implementation shared by every runtime.
|
|
6
|
+
*
|
|
7
|
+
* Bun runs the Node standard library natively, so there is no need for
|
|
8
|
+
* per-runtime adapters: `node:fs` and `node:crypto` behave identically under
|
|
9
|
+
* Bun and Node, and mime lookups go through the same table for both.
|
|
10
|
+
*/
|
|
3
11
|
export class Runtime {
|
|
4
|
-
static
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
12
|
+
static async exists(filePath) {
|
|
13
|
+
try {
|
|
14
|
+
await fs.access(filePath);
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return false;
|
|
11
19
|
}
|
|
12
|
-
return this.#runtime;
|
|
13
|
-
}
|
|
14
|
-
static exists(filePath) {
|
|
15
|
-
return this.runtime.exists(filePath);
|
|
16
20
|
}
|
|
17
21
|
static readText(filePath) {
|
|
18
|
-
return
|
|
22
|
+
return fs.readFile(filePath, 'utf-8');
|
|
19
23
|
}
|
|
20
|
-
static readBuffer(filePath) {
|
|
21
|
-
|
|
24
|
+
static async readBuffer(filePath) {
|
|
25
|
+
const buffer = await fs.readFile(filePath);
|
|
26
|
+
return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
|
|
22
27
|
}
|
|
23
28
|
static mimeType(filePath) {
|
|
24
|
-
return
|
|
29
|
+
return getMimeTypeFromPath(filePath);
|
|
25
30
|
}
|
|
26
31
|
static write(filePath, content) {
|
|
27
|
-
return
|
|
32
|
+
return fs.writeFile(filePath, content);
|
|
28
33
|
}
|
|
29
34
|
static hash(value) {
|
|
30
|
-
return
|
|
35
|
+
return createHash('sha256').update(value).digest('hex').slice(0, 16);
|
|
31
36
|
}
|
|
32
37
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createTemporaryReferenceSet, decodeAction, decodeFormState, decodeReply, loadServerAction, } from '@vitejs/plugin-rsc/rsc';
|
|
2
|
-
import
|
|
2
|
+
import * as Config from '../../config.js';
|
|
3
3
|
import { enforce } from './csrf.js';
|
|
4
4
|
/**
|
|
5
5
|
* Check if a request is an action request and reuse parsed FormData
|
|
@@ -66,7 +66,7 @@ export async function processActionRequest(req, csrf = {}) {
|
|
|
66
66
|
// we might have already parsed FormData in the router for multipart action
|
|
67
67
|
// detection should be attached to the SolasRequest, so we can reuse that
|
|
68
68
|
// to avoid parsing twice
|
|
69
|
-
const parsedFormData = req[
|
|
69
|
+
const parsedFormData = req[Config.REQUEST_META_KEY]?.parsedFormData;
|
|
70
70
|
const formData = parsedFormData ?? (await req.formData());
|
|
71
71
|
const decodedAction = await decodeAction(formData);
|
|
72
72
|
const result = await decodedAction();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Component } from 'react';
|
|
2
|
-
import type { BoundaryError } from '
|
|
2
|
+
import type { BoundaryError } from '../boundary-error.js';
|
|
3
3
|
export type Props = {
|
|
4
4
|
fallback: ((error: BoundaryError, reset: () => void) => React.ReactNode) | React.ReactNode;
|
|
5
5
|
onError?: (error: BoundaryError) => void;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import * as Prerender from './internal/prerender.js';
|
|
2
|
+
export type Manifest = {
|
|
3
|
+
artifacts: Prerender.ArtifactManifest;
|
|
4
|
+
publicFiles: ReadonlySet<string>;
|
|
5
|
+
};
|
|
6
|
+
export declare function getManifestPath(outDir: string): string;
|
|
7
|
+
export declare function loadManifest(outDir: string): Promise<Manifest | null>;
|
package/dist/manifest.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import * as Config from './config.js';
|
|
2
|
+
import { Runtime } from './internal/runtimes/runtime.js';
|
|
3
|
+
const manifestCache = new Map();
|
|
4
|
+
export function getManifestPath(outDir) {
|
|
5
|
+
return [outDir, Config.GENERATED_DIR, Config.RUNTIME_MANIFEST]
|
|
6
|
+
.map((part, index) => {
|
|
7
|
+
const normalised = part.replace(/\\/g, '/').replace(/\/+/g, '/');
|
|
8
|
+
if (index === 0)
|
|
9
|
+
return normalised.replace(/\/+$/, '');
|
|
10
|
+
return normalised.replace(/^\/+/, '').replace(/\/+$/, '');
|
|
11
|
+
})
|
|
12
|
+
.join('/');
|
|
13
|
+
}
|
|
14
|
+
export async function loadManifest(outDir) {
|
|
15
|
+
if (manifestCache.has(outDir)) {
|
|
16
|
+
return manifestCache.get(outDir) ?? null;
|
|
17
|
+
}
|
|
18
|
+
const manifestPath = getManifestPath(outDir);
|
|
19
|
+
if (!(await Runtime.exists(manifestPath))) {
|
|
20
|
+
manifestCache.set(outDir, null);
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
try {
|
|
24
|
+
const value = JSON.parse(await Runtime.readText(manifestPath));
|
|
25
|
+
if (!isRecord(value)) {
|
|
26
|
+
manifestCache.set(outDir, null);
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
const artifacts = value.artifacts ?? value.routes;
|
|
30
|
+
const publicFiles = value.publicFiles;
|
|
31
|
+
if (!isRecord(artifacts)) {
|
|
32
|
+
manifestCache.set(outDir, null);
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
if (publicFiles !== undefined && !Array.isArray(publicFiles)) {
|
|
36
|
+
manifestCache.set(outDir, null);
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
for (const entry of Object.values(artifacts)) {
|
|
40
|
+
if (!isRecord(entry)) {
|
|
41
|
+
manifestCache.set(outDir, null);
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
const { mode, files } = entry;
|
|
45
|
+
if (mode !== 'full' && mode !== 'ppr') {
|
|
46
|
+
manifestCache.set(outDir, null);
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
if (files !== undefined) {
|
|
50
|
+
if (!Array.isArray(files)) {
|
|
51
|
+
manifestCache.set(outDir, null);
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
for (const file of files) {
|
|
55
|
+
if (file !== 'html' &&
|
|
56
|
+
file !== 'prelude' &&
|
|
57
|
+
file !== 'postponed' &&
|
|
58
|
+
file !== 'metadata') {
|
|
59
|
+
manifestCache.set(outDir, null);
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
for (const entry of publicFiles ?? []) {
|
|
66
|
+
if (typeof entry !== 'string' || !entry.startsWith('/')) {
|
|
67
|
+
manifestCache.set(outDir, null);
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const runtimeManifest = {
|
|
72
|
+
artifacts: artifacts,
|
|
73
|
+
publicFiles: new Set(publicFiles ?? []),
|
|
74
|
+
};
|
|
75
|
+
manifestCache.set(outDir, runtimeManifest);
|
|
76
|
+
return runtimeManifest;
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
manifestCache.set(outDir, null);
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function isRecord(value) {
|
|
84
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
85
|
+
}
|
package/dist/prerender.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export * as Prerender from './internal/prerender.js';
|
package/dist/prerender.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export * as Prerender from './internal/prerender.js';
|
package/dist/solas.d.ts
CHANGED
|
@@ -1,47 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
import type { PluginConfig } from './types.js';
|
|
3
|
-
export declare namespace Solas {
|
|
4
|
-
interface Routes {
|
|
5
|
-
}
|
|
6
|
-
namespace Config {
|
|
7
|
-
const NAME = "Solas";
|
|
8
|
-
const SLUG: string;
|
|
9
|
-
const PKG_NAME: string;
|
|
10
|
-
const OUT_DIR = "dist";
|
|
11
|
-
const APP_DIR = "app";
|
|
12
|
-
const GENERATED_DIR: string;
|
|
13
|
-
const ENTRY_RSC = "entry.rsc.tsx";
|
|
14
|
-
const ENTRY_SSR = "entry.ssr.tsx";
|
|
15
|
-
const ENTRY_BROWSER = "entry.browser.tsx";
|
|
16
|
-
const ASSETS_DIR: string;
|
|
17
|
-
const PUBLIC_DIR = "public";
|
|
18
|
-
const $: unique symbol;
|
|
19
|
-
const REQUEST_META_KEY: string;
|
|
20
|
-
const LOG_LEVELS: readonly ["debug", "info", "warn", "error", "fatal"];
|
|
21
|
-
const RUNTIMES: readonly ["auto", "node", "bun"];
|
|
22
|
-
const PRERENDER_MODES: readonly ["full", "ppr", false];
|
|
23
|
-
const TRAILING_SLASH_MODES: readonly ["always", "never", "ignore"];
|
|
24
|
-
const RUNTIME_MANIFEST = "runtime-manifest.json";
|
|
25
|
-
/**
|
|
26
|
-
* Validate the plugin configuration object, throwing an error if invalid
|
|
27
|
-
* @param input - the unvalidated configuration object
|
|
28
|
-
* @return the typed and validated configuration object
|
|
29
|
-
*/
|
|
30
|
-
function validate(input: unknown): PluginConfig;
|
|
31
|
-
}
|
|
32
|
-
function getVersion(): string;
|
|
33
|
-
namespace Runtime {
|
|
34
|
-
type Manifest = {
|
|
35
|
-
artifacts: Prerender.Artifact.Manifest;
|
|
36
|
-
publicFiles: ReadonlySet<string>;
|
|
37
|
-
};
|
|
38
|
-
function getManifestPath(outDir: string): string;
|
|
39
|
-
function loadManifest(outDir: string): Promise<Manifest | null>;
|
|
40
|
-
}
|
|
41
|
-
namespace Events {
|
|
42
|
-
const names: {
|
|
43
|
-
readonly NAVIGATION: `${string}navigation`;
|
|
44
|
-
readonly NAVIGATION_ERROR: `${string}navigationerror`;
|
|
45
|
-
};
|
|
46
|
-
}
|
|
1
|
+
export interface Routes {
|
|
47
2
|
}
|
|
3
|
+
export declare function getVersion(): string;
|
package/dist/solas.js
CHANGED
|
@@ -1,268 +1,8 @@
|
|
|
1
|
-
import
|
|
2
|
-
export
|
|
3
|
-
|
|
4
|
-
(
|
|
5
|
-
|
|
6
|
-
(function (Config) {
|
|
7
|
-
Config.NAME = 'Solas';
|
|
8
|
-
Config.SLUG = Config.NAME.toLowerCase();
|
|
9
|
-
Config.PKG_NAME = `@jk2908/${Config.SLUG}`;
|
|
10
|
-
Config.OUT_DIR = 'dist';
|
|
11
|
-
Config.APP_DIR = 'app';
|
|
12
|
-
Config.GENERATED_DIR = `.${Config.SLUG}`;
|
|
13
|
-
Config.ENTRY_RSC = 'entry.rsc.tsx';
|
|
14
|
-
Config.ENTRY_SSR = 'entry.ssr.tsx';
|
|
15
|
-
Config.ENTRY_BROWSER = 'entry.browser.tsx';
|
|
16
|
-
Config.ASSETS_DIR = `_${Config.SLUG}`;
|
|
17
|
-
Config.PUBLIC_DIR = 'public';
|
|
18
|
-
Config.$ = Symbol(Config.SLUG);
|
|
19
|
-
Config.REQUEST_META_KEY = `__${Config.SLUG.toUpperCase()}__`;
|
|
20
|
-
Config.LOG_LEVELS = ['debug', 'info', 'warn', 'error', 'fatal'];
|
|
21
|
-
Config.RUNTIMES = ['auto', 'node', 'bun'];
|
|
22
|
-
Config.PRERENDER_MODES = ['full', 'ppr', false];
|
|
23
|
-
Config.TRAILING_SLASH_MODES = ['always', 'never', 'ignore'];
|
|
24
|
-
Config.RUNTIME_MANIFEST = 'runtime-manifest.json';
|
|
25
|
-
const CONFIG_KEYS = new Set([
|
|
26
|
-
'runtime',
|
|
27
|
-
'port',
|
|
28
|
-
'logger',
|
|
29
|
-
'metadata',
|
|
30
|
-
'precompress',
|
|
31
|
-
'prerender',
|
|
32
|
-
'sitemap',
|
|
33
|
-
'trustedOrigins',
|
|
34
|
-
'trailingSlash',
|
|
35
|
-
'url',
|
|
36
|
-
]);
|
|
37
|
-
const LOGGER_KEYS = new Set(['level']);
|
|
38
|
-
/**
|
|
39
|
-
* Validate the plugin configuration object, throwing an error if invalid
|
|
40
|
-
* @param input - the unvalidated configuration object
|
|
41
|
-
* @return the typed and validated configuration object
|
|
42
|
-
*/
|
|
43
|
-
function validate(input) {
|
|
44
|
-
if (input === undefined)
|
|
45
|
-
return {};
|
|
46
|
-
const errors = [];
|
|
47
|
-
if (!isRecord(input)) {
|
|
48
|
-
throw new Error(`[${Config.NAME}] Invalid config:\n- Expected plugin config to be an object`);
|
|
49
|
-
}
|
|
50
|
-
for (const key of Object.keys(input)) {
|
|
51
|
-
if (!CONFIG_KEYS.has(key)) {
|
|
52
|
-
errors.push(`Unknown config key: ${key}`);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
if ('runtime' in input &&
|
|
56
|
-
input.runtime !== undefined &&
|
|
57
|
-
!isRuntime(input.runtime)) {
|
|
58
|
-
errors.push("config.runtime must be 'auto', 'node', or 'bun'");
|
|
59
|
-
}
|
|
60
|
-
if ('url' in input && input.url !== undefined) {
|
|
61
|
-
if (typeof input.url !== 'string') {
|
|
62
|
-
errors.push('config.url must be a string');
|
|
63
|
-
}
|
|
64
|
-
else {
|
|
65
|
-
try {
|
|
66
|
-
const url = new URL(input.url);
|
|
67
|
-
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
|
|
68
|
-
errors.push('config.url must use http:// or https://');
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
catch {
|
|
72
|
-
errors.push('config.url must be a valid URL');
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
if ('precompress' in input && input.precompress !== undefined) {
|
|
77
|
-
if (typeof input.precompress !== 'boolean') {
|
|
78
|
-
errors.push('config.precompress must be a boolean');
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
if ('trustedOrigins' in input && input.trustedOrigins !== undefined) {
|
|
82
|
-
if (!Array.isArray(input.trustedOrigins)) {
|
|
83
|
-
errors.push('config.trustedOrigins must be an array of origins');
|
|
84
|
-
}
|
|
85
|
-
else {
|
|
86
|
-
for (const [index, value] of input.trustedOrigins.entries()) {
|
|
87
|
-
if (typeof value !== 'string') {
|
|
88
|
-
errors.push(`config.trustedOrigins[${index}] must be a string`);
|
|
89
|
-
continue;
|
|
90
|
-
}
|
|
91
|
-
try {
|
|
92
|
-
const url = new URL(value);
|
|
93
|
-
const canonical = value.replace(/\/$/, '');
|
|
94
|
-
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
|
|
95
|
-
errors.push(`config.trustedOrigins[${index}] must use http:// or https://`);
|
|
96
|
-
continue;
|
|
97
|
-
}
|
|
98
|
-
if (canonical !== url.origin) {
|
|
99
|
-
errors.push(`config.trustedOrigins[${index}] must be an origin without a path, query, or hash`);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
catch {
|
|
103
|
-
errors.push(`config.trustedOrigins[${index}] must be a valid URL origin`);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
if ('sitemap' in input && input.sitemap !== undefined && input.sitemap !== false) {
|
|
109
|
-
if (typeof input.sitemap !== 'boolean' && typeof input.sitemap !== 'object') {
|
|
110
|
-
errors.push('config.sitemap must be a boolean or an object with a routes function');
|
|
111
|
-
}
|
|
112
|
-
if (typeof input.sitemap === 'object' &&
|
|
113
|
-
input.sitemap !== null &&
|
|
114
|
-
typeof input.sitemap.routes !== 'function') {
|
|
115
|
-
errors.push('config.sitemap.routes must be a function');
|
|
116
|
-
}
|
|
117
|
-
if (!input.url) {
|
|
118
|
-
errors.push('config.url is required when sitemap is enabled');
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
if ('prerender' in input && input.prerender !== undefined) {
|
|
122
|
-
if (!new Set(Config.PRERENDER_MODES).has(input.prerender)) {
|
|
123
|
-
errors.push("config.prerender must be 'full', 'ppr', or false");
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
if ('trailingSlash' in input && input.trailingSlash !== undefined) {
|
|
127
|
-
if (typeof input.trailingSlash !== 'string' ||
|
|
128
|
-
!new Set(Config.TRAILING_SLASH_MODES).has(input.trailingSlash)) {
|
|
129
|
-
errors.push("config.trailingSlash must be 'always', 'never', or 'ignore'");
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
if ('metadata' in input &&
|
|
133
|
-
input.metadata !== undefined &&
|
|
134
|
-
!isRecord(input.metadata)) {
|
|
135
|
-
errors.push('config.metadata must be an object when provided');
|
|
136
|
-
}
|
|
137
|
-
if ('logger' in input && input.logger !== undefined) {
|
|
138
|
-
if (!isRecord(input.logger)) {
|
|
139
|
-
errors.push('config.logger must be an object when provided');
|
|
140
|
-
}
|
|
141
|
-
else {
|
|
142
|
-
for (const key of Object.keys(input.logger)) {
|
|
143
|
-
if (!LOGGER_KEYS.has(key)) {
|
|
144
|
-
errors.push(`Unknown config.logger key: ${key}`);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
if ('level' in input.logger && input.logger.level !== undefined) {
|
|
148
|
-
if (typeof input.logger.level !== 'string' ||
|
|
149
|
-
!new Set(Config.LOG_LEVELS).has(input.logger.level)) {
|
|
150
|
-
errors.push('config.logger.level must be one of: debug, info, warn, error, fatal');
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
if (errors.length > 0) {
|
|
156
|
-
throw new Error(`[${Config.NAME}] Invalid config:\n- ${errors.join('\n- ')}`);
|
|
157
|
-
}
|
|
158
|
-
return input;
|
|
159
|
-
}
|
|
160
|
-
Config.validate = validate;
|
|
161
|
-
})(Config = Solas.Config || (Solas.Config = {}));
|
|
162
|
-
function getVersion() {
|
|
163
|
-
const value = import.meta.env.SOLAS_VERSION;
|
|
164
|
-
if (typeof value !== 'string' || value.length === 0) {
|
|
165
|
-
throw new Error(`[${Config.NAME}] Missing ${Config.NAME} package version`);
|
|
166
|
-
}
|
|
167
|
-
return value;
|
|
1
|
+
import * as Config from './config.js';
|
|
2
|
+
export function getVersion() {
|
|
3
|
+
const value = import.meta.env.SOLAS_VERSION;
|
|
4
|
+
if (typeof value !== 'string' || value.length === 0) {
|
|
5
|
+
throw new Error(`[${Config.NAME}] Missing ${Config.NAME} package version`);
|
|
168
6
|
}
|
|
169
|
-
|
|
170
|
-
let Runtime;
|
|
171
|
-
(function (Runtime) {
|
|
172
|
-
const manifestCache = new Map();
|
|
173
|
-
function getManifestPath(outDir) {
|
|
174
|
-
return [outDir, Config.GENERATED_DIR, Config.RUNTIME_MANIFEST]
|
|
175
|
-
.map((part, index) => {
|
|
176
|
-
const normalised = part.replace(/\\/g, '/').replace(/\/+/g, '/');
|
|
177
|
-
if (index === 0)
|
|
178
|
-
return normalised.replace(/\/+$/, '');
|
|
179
|
-
return normalised.replace(/^\/+/, '').replace(/\/+$/, '');
|
|
180
|
-
})
|
|
181
|
-
.join('/');
|
|
182
|
-
}
|
|
183
|
-
Runtime.getManifestPath = getManifestPath;
|
|
184
|
-
async function loadManifest(outDir) {
|
|
185
|
-
if (manifestCache.has(outDir)) {
|
|
186
|
-
return manifestCache.get(outDir) ?? null;
|
|
187
|
-
}
|
|
188
|
-
const manifestPath = getManifestPath(outDir);
|
|
189
|
-
if (!(await InternalRuntime.exists(manifestPath))) {
|
|
190
|
-
manifestCache.set(outDir, null);
|
|
191
|
-
return null;
|
|
192
|
-
}
|
|
193
|
-
try {
|
|
194
|
-
const value = JSON.parse(await InternalRuntime.readText(manifestPath));
|
|
195
|
-
if (!isRecord(value)) {
|
|
196
|
-
manifestCache.set(outDir, null);
|
|
197
|
-
return null;
|
|
198
|
-
}
|
|
199
|
-
const artifacts = value.artifacts ?? value.routes;
|
|
200
|
-
const publicFiles = value.publicFiles;
|
|
201
|
-
if (!isRecord(artifacts)) {
|
|
202
|
-
manifestCache.set(outDir, null);
|
|
203
|
-
return null;
|
|
204
|
-
}
|
|
205
|
-
if (publicFiles !== undefined && !Array.isArray(publicFiles)) {
|
|
206
|
-
manifestCache.set(outDir, null);
|
|
207
|
-
return null;
|
|
208
|
-
}
|
|
209
|
-
for (const entry of Object.values(artifacts)) {
|
|
210
|
-
if (!isRecord(entry)) {
|
|
211
|
-
manifestCache.set(outDir, null);
|
|
212
|
-
return null;
|
|
213
|
-
}
|
|
214
|
-
const { mode, files } = entry;
|
|
215
|
-
if (mode !== 'full' && mode !== 'ppr') {
|
|
216
|
-
manifestCache.set(outDir, null);
|
|
217
|
-
return null;
|
|
218
|
-
}
|
|
219
|
-
if (files !== undefined) {
|
|
220
|
-
if (!Array.isArray(files)) {
|
|
221
|
-
manifestCache.set(outDir, null);
|
|
222
|
-
return null;
|
|
223
|
-
}
|
|
224
|
-
for (const file of files) {
|
|
225
|
-
if (file !== 'html' &&
|
|
226
|
-
file !== 'prelude' &&
|
|
227
|
-
file !== 'postponed' &&
|
|
228
|
-
file !== 'metadata') {
|
|
229
|
-
manifestCache.set(outDir, null);
|
|
230
|
-
return null;
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
for (const entry of publicFiles ?? []) {
|
|
236
|
-
if (typeof entry !== 'string' || !entry.startsWith('/')) {
|
|
237
|
-
manifestCache.set(outDir, null);
|
|
238
|
-
return null;
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
const runtimeManifest = {
|
|
242
|
-
artifacts: artifacts,
|
|
243
|
-
publicFiles: new Set(publicFiles ?? []),
|
|
244
|
-
};
|
|
245
|
-
manifestCache.set(outDir, runtimeManifest);
|
|
246
|
-
return runtimeManifest;
|
|
247
|
-
}
|
|
248
|
-
catch {
|
|
249
|
-
manifestCache.set(outDir, null);
|
|
250
|
-
return null;
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
Runtime.loadManifest = loadManifest;
|
|
254
|
-
})(Runtime = Solas.Runtime || (Solas.Runtime = {}));
|
|
255
|
-
let Events;
|
|
256
|
-
(function (Events) {
|
|
257
|
-
Events.names = {
|
|
258
|
-
NAVIGATION: `${Config.SLUG}navigation`,
|
|
259
|
-
NAVIGATION_ERROR: `${Config.SLUG}navigationerror`,
|
|
260
|
-
};
|
|
261
|
-
})(Events = Solas.Events || (Solas.Events = {}));
|
|
262
|
-
})(Solas || (Solas = {}));
|
|
263
|
-
function isRecord(value) {
|
|
264
|
-
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
265
|
-
}
|
|
266
|
-
function isRuntime(value) {
|
|
267
|
-
return typeof value === 'string' && new Set(Solas.Config.RUNTIMES).has(value);
|
|
7
|
+
return value;
|
|
268
8
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -2,40 +2,32 @@ type BunRequest = Request & {
|
|
|
2
2
|
params?: Record<string, string | string[]>;
|
|
3
3
|
};
|
|
4
4
|
import { ExportReader } from './utils/export-reader.js';
|
|
5
|
-
import type
|
|
6
|
-
import type { Build } from './internal/build.js';
|
|
5
|
+
import type * as BrowserRouter from './internal/browser-router/shared.js';
|
|
7
6
|
import type { HttpRouter } from './internal/http-router/router.js';
|
|
8
7
|
import type { Metadata } from './internal/metadata.js';
|
|
9
8
|
import type { HttpException } from './internal/navigation/http-exception.js';
|
|
10
|
-
import
|
|
11
|
-
|
|
12
|
-
export type
|
|
13
|
-
type Origin = `http://${string}` | `https://${string}`;
|
|
9
|
+
import * as Config from './config.js';
|
|
10
|
+
import * as Build from './internal/build.js';
|
|
11
|
+
export type LogLevel = (typeof Config.LOG_LEVELS)[number];
|
|
12
|
+
export type Origin = `http://${string}` | `https://${string}`;
|
|
14
13
|
type PluginConfigBase = {
|
|
15
|
-
runtime?: Runtime;
|
|
16
|
-
port?: number;
|
|
17
14
|
precompress?: boolean;
|
|
18
15
|
prerender?: Route.Prerender;
|
|
19
16
|
metadata?: Metadata.Item;
|
|
20
|
-
trailingSlash?: (typeof
|
|
17
|
+
trailingSlash?: (typeof Config.TRAILING_SLASH_MODES)[number];
|
|
21
18
|
trustedOrigins?: readonly Origin[];
|
|
22
19
|
readonly logger?: {
|
|
23
20
|
level?: LogLevel;
|
|
24
21
|
};
|
|
25
22
|
};
|
|
26
|
-
export type PluginConfig = PluginConfigBase &
|
|
27
|
-
url
|
|
28
|
-
sitemap
|
|
23
|
+
export type PluginConfig = PluginConfigBase & {
|
|
24
|
+
url?: Origin;
|
|
25
|
+
sitemap?: boolean | {
|
|
29
26
|
routes: (existing: string[]) => string[] | Promise<string[]>;
|
|
30
27
|
};
|
|
31
|
-
} | {
|
|
32
|
-
url?: Origin;
|
|
33
|
-
sitemap?: false;
|
|
34
|
-
});
|
|
35
|
-
export type ConfiguredPluginConfig = PluginConfig & {
|
|
36
|
-
runtime: Runtime;
|
|
37
28
|
};
|
|
38
|
-
export type
|
|
29
|
+
export type ConfiguredPluginConfig = PluginConfig;
|
|
30
|
+
export type RuntimeConfig = PluginConfig & {
|
|
39
31
|
precompress: NonNullable<PluginConfig['precompress']>;
|
|
40
32
|
trailingSlash: NonNullable<PluginConfig['trailingSlash']>;
|
|
41
33
|
trustedOrigins: NonNullable<PluginConfig['trustedOrigins']>;
|
|
@@ -112,8 +104,8 @@ export type BuildManifest = {
|
|
|
112
104
|
prerenderRoutes: string[];
|
|
113
105
|
sitemapRoutes: string[];
|
|
114
106
|
precompress: boolean;
|
|
115
|
-
trailingSlash: (typeof
|
|
116
|
-
url?:
|
|
107
|
+
trailingSlash: (typeof Config.TRAILING_SLASH_MODES)[number];
|
|
108
|
+
url?: Origin;
|
|
117
109
|
};
|
|
118
110
|
export declare namespace Route {
|
|
119
111
|
type ParamsOf<TRoute> = TRoute extends {
|
|
@@ -132,10 +124,7 @@ export declare namespace Route {
|
|
|
132
124
|
} : {
|
|
133
125
|
params: ParamsOf<TRoute>;
|
|
134
126
|
}) & ErrorPropsOf<TError>;
|
|
135
|
-
export type Prerender = (typeof
|
|
127
|
+
export type Prerender = (typeof Config.PRERENDER_MODES)[number];
|
|
136
128
|
export {};
|
|
137
129
|
}
|
|
138
|
-
export type BoundaryError = Error & {
|
|
139
|
-
digest?: string;
|
|
140
|
-
};
|
|
141
130
|
export {};
|