@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.
Files changed (75) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/README.md +34 -94
  3. package/dist/config.d.ts +24 -0
  4. package/dist/config.js +143 -0
  5. package/dist/events.d.ts +4 -0
  6. package/dist/events.js +5 -0
  7. package/dist/index.d.ts +1 -2
  8. package/dist/index.js +59 -41
  9. package/dist/internal/boundary-error.d.ts +4 -0
  10. package/dist/internal/boundary-error.js +8 -0
  11. package/dist/internal/browser-router/history.d.ts +1 -1
  12. package/dist/internal/browser-router/link.d.ts +1 -1
  13. package/dist/internal/browser-router/link.js +1 -1
  14. package/dist/internal/browser-router/router.d.ts +2 -2
  15. package/dist/internal/browser-router/router.js +5 -5
  16. package/dist/internal/browser-router/shared.d.ts +182 -184
  17. package/dist/internal/browser-router/shared.js +49 -56
  18. package/dist/internal/browser-router/use-router.d.ts +1 -1
  19. package/dist/internal/browser-router/use-search-params.js +3 -3
  20. package/dist/internal/build.d.ts +108 -110
  21. package/dist/internal/build.js +569 -581
  22. package/dist/internal/codegen/config.js +4 -5
  23. package/dist/internal/codegen/environments.d.ts +1 -1
  24. package/dist/internal/codegen/environments.js +6 -9
  25. package/dist/internal/codegen/format.d.ts +6 -0
  26. package/dist/internal/codegen/format.js +24 -0
  27. package/dist/internal/codegen/manifest.js +2 -2
  28. package/dist/internal/codegen/maps.d.ts +1 -1
  29. package/dist/internal/codegen/maps.js +2 -2
  30. package/dist/internal/codegen/types.js +4 -4
  31. package/dist/internal/codegen/utils.js +2 -2
  32. package/dist/internal/env/request-context.js +1 -1
  33. package/dist/internal/env/rsc.d.ts +3 -3
  34. package/dist/internal/env/rsc.js +26 -26
  35. package/dist/internal/env/ssr.js +4 -4
  36. package/dist/internal/http-router/router.js +8 -8
  37. package/dist/internal/metadata.d.ts +1 -1
  38. package/dist/internal/metadata.js +1 -1
  39. package/dist/internal/navigation/http-exception-boundary.d.ts +1 -1
  40. package/dist/internal/navigation/redirect-boundary.d.ts +1 -1
  41. package/dist/internal/navigation/redirect.js +6 -6
  42. package/dist/internal/postbuild.js +18 -17
  43. package/dist/internal/prerender.d.ts +130 -138
  44. package/dist/internal/prerender.js +326 -354
  45. package/dist/internal/public-files.js +2 -4
  46. package/dist/internal/render/head.js +2 -2
  47. package/dist/internal/resolver.js +1 -1
  48. package/dist/internal/runtimes/runtime.d.ts +7 -21
  49. package/dist/internal/runtimes/runtime.js +24 -19
  50. package/dist/internal/server/actions.js +2 -2
  51. package/dist/internal/ui/error-boundary.d.ts +1 -1
  52. package/dist/manifest.d.ts +7 -0
  53. package/dist/manifest.js +85 -0
  54. package/dist/prerender.d.ts +1 -1
  55. package/dist/prerender.js +1 -1
  56. package/dist/solas.d.ts +2 -46
  57. package/dist/solas.js +6 -266
  58. package/dist/types.d.ts +14 -25
  59. package/dist/types.js +1 -1
  60. package/dist/utils/base-path.d.ts +12 -14
  61. package/dist/utils/base-path.js +75 -82
  62. package/dist/utils/compress.d.ts +9 -11
  63. package/dist/utils/compress.js +59 -64
  64. package/dist/utils/context.d.ts +4 -6
  65. package/dist/utils/context.js +17 -22
  66. package/dist/utils/logger.js +2 -2
  67. package/dist/utils/time.d.ts +2 -4
  68. package/dist/utils/time.js +23 -29
  69. package/package.json +18 -8
  70. package/dist/internal/runtimes/bun.d.ts +0 -9
  71. package/dist/internal/runtimes/bun.js +0 -33
  72. package/dist/internal/runtimes/create.d.ts +0 -4
  73. package/dist/internal/runtimes/create.js +0 -9
  74. package/dist/internal/runtimes/node.d.ts +0 -9
  75. package/dist/internal/runtimes/node.js +0 -31
package/dist/types.js CHANGED
@@ -1 +1 @@
1
- import { Solas } from './solas.js';
1
+ import * as Config from './config.js';
@@ -1,14 +1,12 @@
1
- export declare namespace BasePath {
2
- /**
3
- * Normalise a base path so every check uses the same shape
4
- */
5
- function normalise(value: string | null | undefined): string;
6
- /**
7
- * Strip the base path from a request path
8
- */
9
- function strip(pathname: string, base: string | null | undefined): string | null;
10
- /**
11
- * Add the base path to a path when needed
12
- */
13
- function apply(pathname: string, base: string | null | undefined): string;
14
- }
1
+ /**
2
+ * Normalise a base path so every check uses the same shape
3
+ */
4
+ export declare function normaliseBasePath(value: string | null | undefined): string;
5
+ /**
6
+ * Strip the base path from a request path
7
+ */
8
+ export declare function stripBasePath(pathname: string, base: string | null | undefined): string | null;
9
+ /**
10
+ * Add the base path to a path when needed
11
+ */
12
+ export declare function applyBasePath(pathname: string, base: string | null | undefined): string;
@@ -1,85 +1,78 @@
1
- export { BasePath };
2
- var BasePath;
3
- (function (BasePath) {
4
- /**
5
- * Normalise a base path so every check uses the same shape
6
- */
7
- function normalise(value) {
8
- // no base means the app lives at the site root
9
- if (!value)
10
- return '/';
11
- if (value === '/' || value === '.' || value === './')
12
- return '/';
13
- let pathname = value.trim();
14
- if (!pathname)
15
- return '/';
16
- // plain path bases are the common case, so keep them cheap
17
- if (!pathname.startsWith('http://') && !pathname.startsWith('https://')) {
18
- const hashIndex = pathname.indexOf('#');
19
- const searchIndex = pathname.indexOf('?');
20
- const end = hashIndex === -1
21
- ? searchIndex
22
- : searchIndex === -1
23
- ? hashIndex
24
- : Math.min(hashIndex, searchIndex);
25
- if (end >= 0)
26
- pathname = pathname.slice(0, end);
1
+ /**
2
+ * Normalise a base path so every check uses the same shape
3
+ */
4
+ export function normaliseBasePath(value) {
5
+ // no base means the app lives at the site root
6
+ if (!value)
7
+ return '/';
8
+ if (value === '/' || value === '.' || value === './')
9
+ return '/';
10
+ let pathname = value.trim();
11
+ if (!pathname)
12
+ return '/';
13
+ // plain path bases are the common case, so keep them cheap
14
+ if (!pathname.startsWith('http://') && !pathname.startsWith('https://')) {
15
+ const hashIndex = pathname.indexOf('#');
16
+ const searchIndex = pathname.indexOf('?');
17
+ const end = hashIndex === -1
18
+ ? searchIndex
19
+ : searchIndex === -1
20
+ ? hashIndex
21
+ : Math.min(hashIndex, searchIndex);
22
+ if (end >= 0)
23
+ pathname = pathname.slice(0, end);
24
+ }
25
+ else {
26
+ try {
27
+ // full urls can still show up here, but we only need the path part
28
+ pathname = new URL(pathname).pathname;
27
29
  }
28
- else {
29
- try {
30
- // full urls can still show up here, but we only need the path part
31
- pathname = new URL(pathname).pathname;
32
- }
33
- catch {
34
- // if parsing fails, fall back to the raw value below
35
- }
30
+ catch {
31
+ // if parsing fails, fall back to the raw value below
36
32
  }
37
- if (!pathname || pathname === '.' || pathname === './')
38
- return '/';
39
- // keep one stable shape: leading slash, trailing slash
40
- if (!pathname.startsWith('/'))
41
- pathname = `/${pathname}`;
42
- return pathname.endsWith('/') ? pathname : `${pathname}/`;
43
- }
44
- BasePath.normalise = normalise;
45
- /**
46
- * Strip the base path from a request path
47
- */
48
- function strip(pathname, base) {
49
- const normalisedBase = normalise(base);
50
- // root base means there is nothing to strip
51
- if (normalisedBase === '/')
52
- return pathname || '/';
53
- const basePath = normalisedBase.slice(0, -1);
54
- // treat both '/docs' and '/docs/' as the app root
55
- if (pathname === basePath || pathname === normalisedBase)
56
- return '/';
57
- // paths outside the base do not belong to this app
58
- if (!pathname.startsWith(`${basePath}/`))
59
- return null;
60
- // return the path as the app should see it
61
- return pathname.slice(basePath.length) || '/';
62
- }
63
- BasePath.strip = strip;
64
- /**
65
- * Add the base path to a path when needed
66
- */
67
- function apply(pathname, base) {
68
- const normalisedBase = normalise(base);
69
- // always work with a path-like value
70
- const target = pathname.startsWith('/') ? pathname : `/${pathname}`;
71
- // root base means the path can pass through unchanged
72
- if (normalisedBase === '/')
73
- return target;
74
- const basePath = normalisedBase.slice(0, -1);
75
- // leave it alone if the base is already there
76
- if (target === basePath || target.startsWith(`${basePath}/`))
77
- return target;
78
- // the app root maps to the base path itself
79
- if (target === '/')
80
- return normalisedBase;
81
- // everything else sits underneath the base path
82
- return `${basePath}${target}`;
83
33
  }
84
- BasePath.apply = apply;
85
- })(BasePath || (BasePath = {}));
34
+ if (!pathname || pathname === '.' || pathname === './')
35
+ return '/';
36
+ // keep one stable shape: leading slash, trailing slash
37
+ if (!pathname.startsWith('/'))
38
+ pathname = `/${pathname}`;
39
+ return pathname.endsWith('/') ? pathname : `${pathname}/`;
40
+ }
41
+ /**
42
+ * Strip the base path from a request path
43
+ */
44
+ export function stripBasePath(pathname, base) {
45
+ const normalisedBase = normaliseBasePath(base);
46
+ // root base means there is nothing to strip
47
+ if (normalisedBase === '/')
48
+ return pathname || '/';
49
+ const basePath = normalisedBase.slice(0, -1);
50
+ // treat both '/docs' and '/docs/' as the app root
51
+ if (pathname === basePath || pathname === normalisedBase)
52
+ return '/';
53
+ // paths outside the base do not belong to this app
54
+ if (!pathname.startsWith(`${basePath}/`))
55
+ return null;
56
+ // return the path as the app should see it
57
+ return pathname.slice(basePath.length) || '/';
58
+ }
59
+ /**
60
+ * Add the base path to a path when needed
61
+ */
62
+ export function applyBasePath(pathname, base) {
63
+ const normalisedBase = normaliseBasePath(base);
64
+ // always work with a path-like value
65
+ const target = pathname.startsWith('/') ? pathname : `/${pathname}`;
66
+ // root base means the path can pass through unchanged
67
+ if (normalisedBase === '/')
68
+ return target;
69
+ const basePath = normalisedBase.slice(0, -1);
70
+ // leave it alone if the base is already there
71
+ if (target === basePath || target.startsWith(`${basePath}/`))
72
+ return target;
73
+ // the app root maps to the base path itself
74
+ if (target === '/')
75
+ return normalisedBase;
76
+ // everything else sits underneath the base path
77
+ return `${basePath}${target}`;
78
+ }
@@ -1,11 +1,9 @@
1
- export declare namespace Compress {
2
- /**
3
- * Compress a file or directory
4
- */
5
- function run(input: string, config?: {
6
- filter?: (f: string) => boolean;
7
- }): AsyncGenerator<{
8
- input: string;
9
- compressed: Uint8Array;
10
- }>;
11
- }
1
+ /**
2
+ * Compress a file or directory
3
+ */
4
+ export declare function run(input: string, config?: {
5
+ filter?: (f: string) => boolean;
6
+ }): AsyncGenerator<{
7
+ input: string;
8
+ compressed: Uint8Array;
9
+ }>;
@@ -3,74 +3,69 @@ import os from 'node:os';
3
3
  import path from 'node:path';
4
4
  import { brotliCompress } from 'node:zlib';
5
5
  import { Runtime } from '../internal/runtimes/runtime.js';
6
- export { Compress };
7
- var Compress;
8
- (function (Compress) {
9
- const DEFAULT_CONCURRENCY = Math.max(1, Math.min(os.cpus().length, 8));
10
- async function collect(input, filter, output = []) {
11
- const stat = await fs.stat(input);
12
- if (!stat.isDirectory()) {
13
- if (filter(input))
14
- output.push(input);
15
- return output;
16
- }
17
- for (const entry of await fs.readdir(input, { withFileTypes: true })) {
18
- const next = path.join(input, entry.name);
19
- if (entry.isDirectory()) {
20
- await collect(next, filter, output);
21
- continue;
22
- }
23
- if (entry.isFile() && filter(next))
24
- output.push(next);
25
- }
6
+ const DEFAULT_CONCURRENCY = Math.max(1, Math.min(os.cpus().length, 8));
7
+ async function collect(input, filter, output = []) {
8
+ const stat = await fs.stat(input);
9
+ if (!stat.isDirectory()) {
10
+ if (filter(input))
11
+ output.push(input);
26
12
  return output;
27
13
  }
28
- async function compress(input) {
29
- const buffer = Buffer.from(await Runtime.readBuffer(input));
30
- const compressed = await new Promise((fulfill, reject) => {
31
- brotliCompress(buffer, (err, res) => {
32
- if (err) {
33
- reject(err);
34
- }
35
- else {
36
- fulfill(res);
37
- }
38
- });
39
- });
40
- // return the input path plus a zero-copy Uint8Array view over the
41
- // compressed Buffer using its exact offset and length
42
- return {
43
- input,
44
- compressed: new Uint8Array(compressed.buffer, compressed.byteOffset, compressed.byteLength),
45
- };
14
+ for (const entry of await fs.readdir(input, { withFileTypes: true })) {
15
+ const next = path.join(input, entry.name);
16
+ if (entry.isDirectory()) {
17
+ await collect(next, filter, output);
18
+ continue;
19
+ }
20
+ if (entry.isFile() && filter(next))
21
+ output.push(next);
46
22
  }
47
- /**
48
- * Compress a file or directory
49
- */
50
- async function* run(input, config = {}) {
51
- const { filter = f => /\.(js|css|html|svg|json|txt)$/.test(f) } = config;
52
- const targets = await collect(input, filter);
53
- if (!targets.length)
54
- return;
55
- let index = 0;
56
- const pending = new Map();
57
- function enqueue() {
58
- while (index < targets.length && pending.size < DEFAULT_CONCURRENCY) {
59
- const i = index++;
60
- const value = targets[i];
61
- pending.set(i, compress(value).then(compressed => ({
62
- index: i,
63
- value: compressed,
64
- })));
23
+ return output;
24
+ }
25
+ async function compress(input) {
26
+ const buffer = Buffer.from(await Runtime.readBuffer(input));
27
+ const compressed = await new Promise((fulfill, reject) => {
28
+ brotliCompress(buffer, (err, res) => {
29
+ if (err) {
30
+ reject(err);
65
31
  }
32
+ else {
33
+ fulfill(res);
34
+ }
35
+ });
36
+ });
37
+ // return the input path plus a zero-copy Uint8Array view over the
38
+ // compressed Buffer using its exact offset and length
39
+ return {
40
+ input,
41
+ compressed: new Uint8Array(compressed.buffer, compressed.byteOffset, compressed.byteLength),
42
+ };
43
+ }
44
+ /**
45
+ * Compress a file or directory
46
+ */
47
+ export async function* run(input, config = {}) {
48
+ const { filter = f => /\.(js|css|html|svg|json|txt)$/.test(f) } = config;
49
+ const targets = await collect(input, filter);
50
+ if (!targets.length)
51
+ return;
52
+ let index = 0;
53
+ const pending = new Map();
54
+ function enqueue() {
55
+ while (index < targets.length && pending.size < DEFAULT_CONCURRENCY) {
56
+ const i = index++;
57
+ const value = targets[i];
58
+ pending.set(i, compress(value).then(compressed => ({
59
+ index: i,
60
+ value: compressed,
61
+ })));
66
62
  }
63
+ }
64
+ enqueue();
65
+ while (pending.size > 0) {
66
+ const settled = await Promise.race(pending.values());
67
+ pending.delete(settled.index);
68
+ yield settled.value;
67
69
  enqueue();
68
- while (pending.size > 0) {
69
- const settled = await Promise.race(pending.values());
70
- pending.delete(settled.index);
71
- yield settled.value;
72
- enqueue();
73
- }
74
70
  }
75
- Compress.run = run;
76
- })(Compress || (Compress = {}));
71
+ }
@@ -1,6 +1,4 @@
1
- export declare namespace Context {
2
- function create<T>(name: string): {
3
- use(): NonNullable<T>;
4
- write<R>(value: T, fn: () => R | Promise<R>): R | Promise<R>;
5
- };
6
- }
1
+ export declare function create<T>(name: string): {
2
+ use(): NonNullable<T>;
3
+ write<R>(value: T, fn: () => R | Promise<R>): R | Promise<R>;
4
+ };
@@ -1,25 +1,20 @@
1
1
  import { AsyncLocalStorage } from 'node:async_hooks';
2
2
  import { Logger } from './logger.js';
3
3
  const logger = new Logger();
4
- export { Context };
5
- var Context;
6
- (function (Context) {
7
- function create(name) {
8
- const storage = new AsyncLocalStorage();
9
- return {
10
- use() {
11
- const r = storage.getStore();
12
- if (!r) {
13
- const error = new Error(`No ${name} context available`);
14
- logger.error(`[Context:create] ${error.message}`, error);
15
- throw error;
16
- }
17
- return r;
18
- },
19
- write(value, fn) {
20
- return storage.run(value, fn);
21
- },
22
- };
23
- }
24
- Context.create = create;
25
- })(Context || (Context = {}));
4
+ export function create(name) {
5
+ const storage = new AsyncLocalStorage();
6
+ return {
7
+ use() {
8
+ const r = storage.getStore();
9
+ if (!r) {
10
+ const error = new Error(`No ${name} context available`);
11
+ logger.error(`[Context:create] ${error.message}`, error);
12
+ throw error;
13
+ }
14
+ return r;
15
+ },
16
+ write(value, fn) {
17
+ return storage.run(value, fn);
18
+ },
19
+ };
20
+ }
@@ -1,5 +1,5 @@
1
+ import * as Config from '../config.js';
1
2
  import { HttpException } from '../internal/navigation/http-exception.js';
2
- import { Solas } from '../solas.js';
3
3
  const LEVELS = {
4
4
  debug: 0,
5
5
  info: 1,
@@ -69,7 +69,7 @@ export class Logger {
69
69
  if (level === 'error' || level === 'fatal') {
70
70
  entry.error = error ? Logger.toError(error) : new Error(message);
71
71
  }
72
- const line = `[${Solas.Config.NAME}] [${entry.ts}] [${level.toUpperCase()}] ${message}`;
72
+ const line = `[${Config.NAME}] [${entry.ts}] [${level.toUpperCase()}] ${message}`;
73
73
  const extra = entry.error ? `\n${Logger.print(entry.error)}` : '';
74
74
  if (level === 'warn') {
75
75
  console.warn(line, extra);
@@ -1,4 +1,2 @@
1
- export declare namespace Time {
2
- function debounce<T extends unknown[]>(fn: (...args: T) => void, wait: number): (...args: T) => void;
3
- function timeout<T>(task: Promise<T>, timeoutMs: number, label: string): Promise<Awaited<T>>;
4
- }
1
+ export declare function debounce<T extends unknown[]>(fn: (...args: T) => void, wait: number): (...args: T) => void;
2
+ export declare function timeout<T>(task: Promise<T>, timeoutMs: number, label: string): Promise<Awaited<T>>;
@@ -1,29 +1,23 @@
1
- export { Time };
2
- var Time;
3
- (function (Time) {
4
- function debounce(fn, wait) {
5
- let timeoutId = null;
6
- return (...args) => {
7
- if (timeoutId) {
8
- clearTimeout(timeoutId);
9
- }
10
- timeoutId = setTimeout(() => {
11
- fn.apply(null, args);
12
- }, wait);
13
- };
14
- }
15
- Time.debounce = debounce;
16
- function timeout(task, timeoutMs, label) {
17
- let timer;
18
- const timeout = new Promise((_, reject) => {
19
- timer = setTimeout(() => {
20
- reject(new Error(`Timed out after ${timeoutMs}ms (${label})`));
21
- }, timeoutMs);
22
- });
23
- return Promise.race([task, timeout]).finally(() => {
24
- if (timer)
25
- clearTimeout(timer);
26
- });
27
- }
28
- Time.timeout = timeout;
29
- })(Time || (Time = {}));
1
+ export function debounce(fn, wait) {
2
+ let timeoutId = null;
3
+ return (...args) => {
4
+ if (timeoutId) {
5
+ clearTimeout(timeoutId);
6
+ }
7
+ timeoutId = setTimeout(() => {
8
+ fn.apply(null, args);
9
+ }, wait);
10
+ };
11
+ }
12
+ export function timeout(task, timeoutMs, label) {
13
+ let timer;
14
+ const timeout = new Promise((_, reject) => {
15
+ timer = setTimeout(() => {
16
+ reject(new Error(`Timed out after ${timeoutMs}ms (${label})`));
17
+ }, timeoutMs);
18
+ });
19
+ return Promise.race([task, timeout]).finally(() => {
20
+ if (timer)
21
+ clearTimeout(timer);
22
+ });
23
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jk2908/solas",
3
- "version": "0.5.4",
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",
@@ -33,10 +33,6 @@
33
33
  "types": "./dist/index.d.ts",
34
34
  "import": "./dist/index.js"
35
35
  },
36
- "./$": {
37
- "types": "./dist/solas.d.ts",
38
- "import": "./dist/solas.js"
39
- },
40
36
  "./env/rsc": {
41
37
  "types": "./dist/internal/env/rsc.d.ts",
42
38
  "import": "./dist/internal/env/rsc.js"
@@ -76,6 +72,10 @@
76
72
  },
77
73
  "scripts": {
78
74
  "build": "rm -rf dist && tsgo",
75
+ "postbuild": "node scripts/measure.mjs",
76
+ "test": "vitest run",
77
+ "test:browser": "vitest run --config vitest.browser.config.ts",
78
+ "test:watch": "vitest",
79
79
  "lint": "bunx oxlint .",
80
80
  "lint:fix": "bunx oxlint --fix .",
81
81
  "format": "bunx oxfmt --write .",
@@ -85,16 +85,24 @@
85
85
  "prepublishOnly": "npm run build && npm run pack:dry"
86
86
  },
87
87
  "dependencies": {
88
- "@vitejs/plugin-rsc": "^0.5.20",
88
+ "@vitejs/plugin-rsc": "0.5.34",
89
89
  "oxc-parser": "^0.134.0",
90
+ "oxfmt": "^0.35.0",
90
91
  "path-to-regexp": "^8.3.0"
91
92
  },
92
93
  "devDependencies": {
93
94
  "@prettier/plugin-oxc": "^0.1.3",
94
95
  "@typescript/native-preview": "^7.0.0-dev.20260224.1",
96
+ "@vitejs/plugin-react": "^5.1.4",
97
+ "@vitest/browser": "^4.1.8",
98
+ "@vitest/browser-playwright": "^4.1.8",
99
+ "happy-dom": "^20.10.2",
95
100
  "network-information-types": "^0.1.1",
96
101
  "oxfmt": "^0.35.0",
97
- "typescript": "^5.9.3"
102
+ "playwright": "^1.58.2",
103
+ "typescript": "^5.9.3",
104
+ "vitest": "^4.1.8",
105
+ "vitest-browser-react": "^2.2.0"
98
106
  },
99
107
  "peerDependencies": {
100
108
  "@types/react": "^19.2.14",
@@ -109,5 +117,7 @@
109
117
  "optional": true
110
118
  }
111
119
  },
112
- "engines": {}
120
+ "engines": {
121
+ "node": "^20.19.0 || >=22.12.0"
122
+ }
113
123
  }
@@ -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
- }