@jk2908/solas 0.6.0 → 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 CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.7.0
4
+
5
+ ### Breaking
6
+
7
+ - Removed the `runtime` config option and the Bun/Node runtime adapters. Solas now uses the Node standard library directly, which Bun runs natively, so there is no runtime selection to configure. Generated entries no longer call `createRuntime`.
8
+
3
9
  ## 0.6.0
4
10
 
5
11
  ### Breaking
package/README.md CHANGED
@@ -201,16 +201,6 @@ That keeps your route params aligned across links, imperative navigation, metada
201
201
 
202
202
  All Solas options are passed to `solas()` inside `defineConfig`.
203
203
 
204
- ### `runtime`
205
-
206
- Use `runtime` to select the runtime used by Solas server code.
207
-
208
- Supported values are `'auto'`, `'node'`, and `'bun'`.
209
-
210
- `solas()` defaults to `runtime: 'auto'`. In a Bun process, that selects Bun. Otherwise it falls back to Node.
211
-
212
- If you already run Vite through Bun, `runtime: 'auto'` is usually enough and Solas will detect Bun automatically.
213
-
214
204
  ### `url`
215
205
 
216
206
  Use `url` to set the public origin for your app. It is optional.
@@ -519,26 +509,11 @@ export default defineConfig({
519
509
 
520
510
  ## Runtime
521
511
 
522
- Solas uses a runtime for filesystem access, mime lookup, and hashing.
523
-
524
512
  This is not a deployment or packaging adapter. Platform adapters are not available yet.
525
513
 
526
- ### Node runtime
527
-
528
- If you want to pin Node explicitly, set `runtime: 'node'`:
529
-
530
- ```ts
531
- import { defineConfig } from 'vite'
532
-
533
- import solas from '@jk2908/solas'
534
- import react from '@vitejs/plugin-react'
535
-
536
- export default defineConfig({
537
- plugins: [solas({ runtime: 'node' }), react()],
538
- })
539
- ```
514
+ ## Scripts
540
515
 
541
- Use the normal Vite commands with the Node runtime:
516
+ Add scripts to your app:
542
517
 
543
518
  ```json
544
519
  {
@@ -550,22 +525,7 @@ Use the normal Vite commands with the Node runtime:
550
525
  }
551
526
  ```
552
527
 
553
- ### Bun runtime
554
-
555
- If you want Solas runtime code to execute in Bun, set `runtime: 'bun'`:
556
-
557
- ```ts
558
- import { defineConfig } from 'vite'
559
-
560
- import solas from '@jk2908/solas'
561
- import react from '@vitejs/plugin-react'
562
-
563
- export default defineConfig({
564
- plugins: [solas({ runtime: 'bun' }), react()],
565
- })
566
- ```
567
-
568
- When you use the Bun runtime, run Vite through Bun so the server/runtime code executes in a Bun process:
528
+ If you run Vite through Bun, use the Bun-backed commands instead:
569
529
 
570
530
  ```json
571
531
  {
@@ -577,22 +537,6 @@ When you use the Bun runtime, run Vite through Bun so the server/runtime code ex
577
537
  }
578
538
  ```
579
539
 
580
- ## Scripts
581
-
582
- Add scripts to your app:
583
-
584
- ```json
585
- {
586
- "scripts": {
587
- "dev": "vite dev",
588
- "build": "vite build",
589
- "preview": "vite preview"
590
- }
591
- }
592
- ```
593
-
594
- These defaults assume either `runtime: 'node'` or `runtime: 'auto'` while running Vite under Node. If you set `runtime: 'bun'`, or let `runtime: 'auto'` resolve to Bun by running Vite through Bun, use the Bun-backed commands shown in the runtime section above.
595
-
596
540
  ## Commands
597
541
 
598
542
  - `vite dev` starts the development server.
package/dist/config.d.ts CHANGED
@@ -13,7 +13,6 @@ export declare const PUBLIC_DIR = "public";
13
13
  export declare const $: unique symbol;
14
14
  export declare const REQUEST_META_KEY: string;
15
15
  export declare const LOG_LEVELS: readonly ["debug", "info", "warn", "error", "fatal"];
16
- export declare const RUNTIMES: readonly ["auto", "node", "bun"];
17
16
  export declare const PRERENDER_MODES: readonly ["full", "ppr", false];
18
17
  export declare const TRAILING_SLASH_MODES: readonly ["always", "never", "ignore"];
19
18
  export declare const RUNTIME_MANIFEST = "runtime-manifest.json";
package/dist/config.js CHANGED
@@ -1,4 +1,3 @@
1
- import * as Config from './config.js';
2
1
  export const NAME = 'Solas';
3
2
  export const SLUG = NAME.toLowerCase();
4
3
  export const PKG_NAME = `@jk2908/${SLUG}`;
@@ -13,12 +12,10 @@ export const PUBLIC_DIR = 'public';
13
12
  export const $ = Symbol(SLUG);
14
13
  export const REQUEST_META_KEY = `__${SLUG.toUpperCase()}__`;
15
14
  export const LOG_LEVELS = ['debug', 'info', 'warn', 'error', 'fatal'];
16
- export const RUNTIMES = ['auto', 'node', 'bun'];
17
15
  export const PRERENDER_MODES = ['full', 'ppr', false];
18
16
  export const TRAILING_SLASH_MODES = ['always', 'never', 'ignore'];
19
17
  export const RUNTIME_MANIFEST = 'runtime-manifest.json';
20
18
  const CONFIG_KEYS = new Set([
21
- 'runtime',
22
19
  'logger',
23
20
  'metadata',
24
21
  'precompress',
@@ -46,9 +43,6 @@ export function validate(input) {
46
43
  errors.push(`Unknown config key: ${key}`);
47
44
  }
48
45
  }
49
- if ('runtime' in input && input.runtime !== undefined && !isRuntime(input.runtime)) {
50
- errors.push("runtime must be 'auto', 'node', or 'bun'");
51
- }
52
46
  if ('url' in input && input.url !== undefined) {
53
47
  if (typeof input.url !== 'string') {
54
48
  errors.push('url must be a string');
@@ -147,6 +141,3 @@ export function validate(input) {
147
141
  function isRecord(value) {
148
142
  return typeof value === 'object' && value !== null && !Array.isArray(value);
149
143
  }
150
- function isRuntime(value) {
151
- return typeof value === 'string' && new Set(Config.RUNTIMES).has(value);
152
- }
package/dist/index.js CHANGED
@@ -16,10 +16,8 @@ import { writeMaps } from './internal/codegen/maps.js';
16
16
  import { writeTypes } from './internal/codegen/types.js';
17
17
  import { postbuild } from './internal/postbuild.js';
18
18
  import { collect as collectPublicFiles } from './internal/public-files.js';
19
- import { createRuntime } from './internal/runtimes/create.js';
20
19
  import { Runtime } from './internal/runtimes/runtime.js';
21
20
  const DEFAULT_CONFIG = {
22
- runtime: 'auto',
23
21
  precompress: false,
24
22
  prerender: false,
25
23
  trustedOrigins: [],
@@ -30,11 +28,9 @@ function solas(c) {
30
28
  const config = {
31
29
  ...DEFAULT_CONFIG,
32
30
  ...validatedConfig,
33
- runtime: validatedConfig.runtime ?? DEFAULT_CONFIG.runtime,
34
31
  };
35
32
  const envUrl = process.env.VITE_APP_URL?.toString();
36
33
  let resolvedUrl = c?.url ?? (envUrl && envUrl.length > 0 ? envUrl : undefined);
37
- Runtime.runtime = createRuntime(config.runtime);
38
34
  if (config.logger?.level)
39
35
  Logger.defaultLevel = config.logger.level;
40
36
  const logger = new Logger();
@@ -4,7 +4,6 @@ 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;
8
7
  const loggerLevel = config.logger?.level;
9
8
  const importLines = [
10
9
  `import type { RuntimeConfig } from '${Config.PKG_NAME}'`,
@@ -12,7 +11,7 @@ export function writeConfig(config) {
12
11
  ]
13
12
  .filter(Boolean)
14
13
  .join('\n');
15
- const configStatement = `const config = ${toSourceLiteral(runtimeConfig)} as const satisfies RuntimeConfig`;
14
+ const configStatement = `const config = ${toSourceLiteral(config)} as const satisfies RuntimeConfig`;
16
15
  const loggerStatement = loggerLevel
17
16
  ? `Logger.defaultLevel = ${toSourceLiteral(loggerLevel)}`
18
17
  : '';
@@ -2,7 +2,7 @@ import type { ConfiguredPluginConfig } from '../../types.js';
2
2
  /**
3
3
  * Generates the RSC entry code
4
4
  */
5
- export declare function writeRSCEntry(config: ConfiguredPluginConfig): string;
5
+ export declare function writeRSCEntry(_config: ConfiguredPluginConfig): string;
6
6
  /**
7
7
  * Generates the SSR entry code
8
8
  */
@@ -3,18 +3,16 @@ import { AUTOGEN_MSG, source, toStringLiteral } from './utils.js';
3
3
  /**
4
4
  * Generates the RSC entry code
5
5
  */
6
- export function writeRSCEntry(config) {
7
- const runtime = toStringLiteral(config.runtime);
6
+ export function writeRSCEntry(_config) {
8
7
  return source `
9
8
  ${AUTOGEN_MSG}
10
9
 
11
- import { createHandler, createRuntime, loadManifest, Runtime } from '${Config.PKG_NAME}/env/rsc'
10
+ import { createHandler, loadManifest } from '${Config.PKG_NAME}/env/rsc'
12
11
 
13
12
  import { manifest } from './manifest.js'
14
13
  import { importMap } from './maps.js'
15
14
  import { config } from './config.js'
16
15
 
17
- Runtime.runtime = createRuntime(${runtime})
18
16
  const runtimeManifest = await loadManifest(${toStringLiteral(Config.OUT_DIR)})
19
17
 
20
18
  export default createHandler(config, manifest, importMap, runtimeManifest)
@@ -2,7 +2,6 @@ import type { ReactFormState } from 'react-dom/client';
2
2
  import type { ImportMap, Manifest, RuntimeConfig } from '../../types.js';
3
3
  import * as RuntimeManifest from '../../manifest.js';
4
4
  import { Metadata } from '../metadata.js';
5
- export { createRuntime } from '../runtimes/create.js';
6
5
  export { Runtime } from '../runtimes/runtime.js';
7
6
  export { loadManifest } from '../../manifest.js';
8
7
  export type RscPayload = {
@@ -17,7 +17,6 @@ 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 { createRuntime } from '../runtimes/create.js';
21
20
  export { Runtime } from '../runtimes/runtime.js';
22
21
  export { loadManifest } from '../../manifest.js';
23
22
  const logger = new Logger();
@@ -1,25 +1,11 @@
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
- }
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
- export class RuntimeBase {
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 #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.');
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 this.runtime.readText(filePath);
22
+ return fs.readFile(filePath, 'utf-8');
19
23
  }
20
- static readBuffer(filePath) {
21
- return this.runtime.readBuffer(filePath);
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 this.runtime.mimeType(filePath);
29
+ return getMimeTypeFromPath(filePath);
25
30
  }
26
31
  static write(filePath, content) {
27
- return this.runtime.write(filePath, content);
32
+ return fs.writeFile(filePath, content);
28
33
  }
29
34
  static hash(value) {
30
- return this.runtime.hash(value);
35
+ return createHash('sha256').update(value).digest('hex').slice(0, 16);
31
36
  }
32
37
  }
package/dist/types.d.ts CHANGED
@@ -9,10 +9,8 @@ import type { HttpException } from './internal/navigation/http-exception.js';
9
9
  import * as Config from './config.js';
10
10
  import * as Build from './internal/build.js';
11
11
  export type LogLevel = (typeof Config.LOG_LEVELS)[number];
12
- export type Runtime = (typeof Config.RUNTIMES)[number];
13
12
  export type Origin = `http://${string}` | `https://${string}`;
14
13
  type PluginConfigBase = {
15
- runtime?: Runtime;
16
14
  precompress?: boolean;
17
15
  prerender?: Route.Prerender;
18
16
  metadata?: Metadata.Item;
@@ -28,10 +26,8 @@ export type PluginConfig = PluginConfigBase & {
28
26
  routes: (existing: string[]) => string[] | Promise<string[]>;
29
27
  };
30
28
  };
31
- export type ConfiguredPluginConfig = PluginConfig & {
32
- runtime: Runtime;
33
- };
34
- export type RuntimeConfig = Omit<PluginConfig, 'runtime'> & {
29
+ export type ConfiguredPluginConfig = PluginConfig;
30
+ export type RuntimeConfig = PluginConfig & {
35
31
  precompress: NonNullable<PluginConfig['precompress']>;
36
32
  trailingSlash: NonNullable<PluginConfig['trailingSlash']>;
37
33
  trustedOrigins: NonNullable<PluginConfig['trustedOrigins']>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jk2908/solas",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "A Vite + React meta-framework exploring streaming, Server Components, and partial prerendering. Designed for simplicity and lightness",
5
5
  "keywords": [
6
6
  "framework",
@@ -1,9 +0,0 @@
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
- }
@@ -1,33 +0,0 @@
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
- }
@@ -1,4 +0,0 @@
1
- import type { Runtime } from '../../types.js';
2
- import { RuntimeBun } from './bun.js';
3
- import { RuntimeNode } from './node.js';
4
- export declare function createRuntime(runtime: Runtime): RuntimeBun | RuntimeNode;
@@ -1,9 +0,0 @@
1
- import { RuntimeBun } from './bun.js';
2
- import { RuntimeNode } from './node.js';
3
- export function createRuntime(runtime) {
4
- if (runtime === 'bun' ||
5
- (runtime === 'auto' && typeof globalThis.Bun !== 'undefined')) {
6
- return new RuntimeBun();
7
- }
8
- return new RuntimeNode();
9
- }
@@ -1,9 +0,0 @@
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
- }
@@ -1,31 +0,0 @@
1
- import { createHash } from 'node:crypto';
2
- import fs from 'node:fs/promises';
3
- import { getMimeTypeFromPath } from './mime.js';
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 getMimeTypeFromPath(filePath);
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
- }