@jk2908/solas 0.5.3 → 0.6.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 (69) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/README.md +81 -85
  3. package/dist/config.d.ts +25 -0
  4. package/dist/config.js +152 -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 -37
  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 +46 -0
  12. package/dist/internal/browser-router/history.js +131 -0
  13. package/dist/internal/browser-router/link.d.ts +1 -1
  14. package/dist/internal/browser-router/link.js +1 -1
  15. package/dist/internal/browser-router/router.d.ts +6 -14
  16. package/dist/internal/browser-router/router.js +39 -13
  17. package/dist/internal/browser-router/shared.d.ts +183 -164
  18. package/dist/internal/browser-router/shared.js +49 -56
  19. package/dist/internal/browser-router/use-router.d.ts +1 -10
  20. package/dist/internal/browser-router/use-search-params.js +3 -3
  21. package/dist/internal/build.d.ts +108 -110
  22. package/dist/internal/build.js +569 -581
  23. package/dist/internal/codegen/config.js +3 -3
  24. package/dist/internal/codegen/environments.js +5 -6
  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 +6 -4
  34. package/dist/internal/env/rsc.js +27 -25
  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/server/actions.js +2 -2
  49. package/dist/internal/ui/error-boundary.d.ts +1 -1
  50. package/dist/manifest.d.ts +7 -0
  51. package/dist/manifest.js +85 -0
  52. package/dist/prerender.d.ts +1 -1
  53. package/dist/prerender.js +1 -1
  54. package/dist/solas.d.ts +2 -46
  55. package/dist/solas.js +6 -266
  56. package/dist/types.d.ts +14 -21
  57. package/dist/types.js +1 -1
  58. package/dist/utils/base-path.d.ts +12 -14
  59. package/dist/utils/base-path.js +75 -82
  60. package/dist/utils/compress.d.ts +9 -11
  61. package/dist/utils/compress.js +59 -64
  62. package/dist/utils/context.d.ts +4 -6
  63. package/dist/utils/context.js +17 -22
  64. package/dist/utils/logger.js +2 -2
  65. package/dist/utils/time.d.ts +2 -4
  66. package/dist/utils/time.js +23 -29
  67. package/package.json +18 -8
  68. package/dist/runtimes/bun.d.ts +0 -13
  69. package/dist/runtimes/bun.js +0 -39
@@ -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.3",
3
+ "version": "0.6.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,13 +0,0 @@
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 {};
@@ -1,39 +0,0 @@
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
- }