@aigne/afs-provider-utils 1.11.0-beta.12

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/LICENSE.md ADDED
@@ -0,0 +1,26 @@
1
+ # Proprietary License
2
+
3
+ Copyright (c) 2024-2025 ArcBlock, Inc. All Rights Reserved.
4
+
5
+ This software and associated documentation files (the "Software") are proprietary
6
+ and confidential. Unauthorized copying, modification, distribution, or use of
7
+ this Software, via any medium, is strictly prohibited.
8
+
9
+ The Software is provided for internal use only within ArcBlock, Inc. and its
10
+ authorized affiliates.
11
+
12
+ ## No License Granted
13
+
14
+ No license, express or implied, is granted to any party for any purpose.
15
+ All rights are reserved by ArcBlock, Inc.
16
+
17
+ ## Public Artifact Distribution
18
+
19
+ Portions of this Software may be released publicly under separate open-source
20
+ licenses (such as MIT License) through designated public repositories. Such
21
+ public releases are governed by their respective licenses and do not affect
22
+ the proprietary nature of this repository.
23
+
24
+ ## Contact
25
+
26
+ For licensing inquiries, contact: legal@arcblock.io
package/dist/index.cjs ADDED
@@ -0,0 +1,8 @@
1
+ const require_local_path = require('./local-path.cjs');
2
+ const require_mime = require('./mime.cjs');
3
+ const require_path_guard = require('./path-guard.cjs');
4
+
5
+ exports.assertPathWithinRoot = require_path_guard.assertPathWithinRoot;
6
+ exports.getMimeType = require_mime.getMimeType;
7
+ exports.isBinaryFile = require_mime.isBinaryFile;
8
+ exports.resolveLocalPath = require_local_path.resolveLocalPath;
@@ -0,0 +1,4 @@
1
+ import { ResolveLocalPathOptions, resolveLocalPath } from "./local-path.cjs";
2
+ import { getMimeType, isBinaryFile } from "./mime.cjs";
3
+ import { assertPathWithinRoot } from "./path-guard.cjs";
4
+ export { type ResolveLocalPathOptions, assertPathWithinRoot, getMimeType, isBinaryFile, resolveLocalPath };
@@ -0,0 +1,4 @@
1
+ import { ResolveLocalPathOptions, resolveLocalPath } from "./local-path.mjs";
2
+ import { getMimeType, isBinaryFile } from "./mime.mjs";
3
+ import { assertPathWithinRoot } from "./path-guard.mjs";
4
+ export { type ResolveLocalPathOptions, assertPathWithinRoot, getMimeType, isBinaryFile, resolveLocalPath };
package/dist/index.mjs ADDED
@@ -0,0 +1,5 @@
1
+ import { resolveLocalPath } from "./local-path.mjs";
2
+ import { getMimeType, isBinaryFile } from "./mime.mjs";
3
+ import { assertPathWithinRoot } from "./path-guard.mjs";
4
+
5
+ export { assertPathWithinRoot, getMimeType, isBinaryFile, resolveLocalPath };
@@ -0,0 +1,17 @@
1
+ let node_path = require("node:path");
2
+
3
+ //#region src/local-path.ts
4
+ function resolveLocalPath(rawPath, options) {
5
+ if (rawPath === ".") return process.cwd();
6
+ let resolved = rawPath.replaceAll("${CWD}", process.cwd());
7
+ if (resolved.startsWith("~/")) {
8
+ const home = process.env.HOME;
9
+ if (!home) throw new Error("Cannot resolve '~/' path: HOME environment variable is not set");
10
+ resolved = (0, node_path.join)(home, resolved.slice(2));
11
+ }
12
+ if (!(0, node_path.isAbsolute)(resolved)) resolved = (0, node_path.join)(options?.cwd || process.cwd(), resolved);
13
+ return resolved;
14
+ }
15
+
16
+ //#endregion
17
+ exports.resolveLocalPath = resolveLocalPath;
@@ -0,0 +1,8 @@
1
+ //#region src/local-path.d.ts
2
+ interface ResolveLocalPathOptions {
3
+ cwd?: string;
4
+ }
5
+ declare function resolveLocalPath(rawPath: string, options?: ResolveLocalPathOptions): string;
6
+ //#endregion
7
+ export { ResolveLocalPathOptions, resolveLocalPath };
8
+ //# sourceMappingURL=local-path.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-path.d.cts","names":[],"sources":["../src/local-path.ts"],"mappings":";UAEiB,uBAAA;EACf,GAAA;AAAA;AAAA,iBAGc,gBAAA,CAAiB,OAAA,UAAiB,OAAA,GAAU,uBAAA"}
@@ -0,0 +1,8 @@
1
+ //#region src/local-path.d.ts
2
+ interface ResolveLocalPathOptions {
3
+ cwd?: string;
4
+ }
5
+ declare function resolveLocalPath(rawPath: string, options?: ResolveLocalPathOptions): string;
6
+ //#endregion
7
+ export { ResolveLocalPathOptions, resolveLocalPath };
8
+ //# sourceMappingURL=local-path.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-path.d.mts","names":[],"sources":["../src/local-path.ts"],"mappings":";UAEiB,uBAAA;EACf,GAAA;AAAA;AAAA,iBAGc,gBAAA,CAAiB,OAAA,UAAiB,OAAA,GAAU,uBAAA"}
@@ -0,0 +1,18 @@
1
+ import { isAbsolute, join } from "node:path";
2
+
3
+ //#region src/local-path.ts
4
+ function resolveLocalPath(rawPath, options) {
5
+ if (rawPath === ".") return process.cwd();
6
+ let resolved = rawPath.replaceAll("${CWD}", process.cwd());
7
+ if (resolved.startsWith("~/")) {
8
+ const home = process.env.HOME;
9
+ if (!home) throw new Error("Cannot resolve '~/' path: HOME environment variable is not set");
10
+ resolved = join(home, resolved.slice(2));
11
+ }
12
+ if (!isAbsolute(resolved)) resolved = join(options?.cwd || process.cwd(), resolved);
13
+ return resolved;
14
+ }
15
+
16
+ //#endregion
17
+ export { resolveLocalPath };
18
+ //# sourceMappingURL=local-path.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-path.mjs","names":[],"sources":["../src/local-path.ts"],"sourcesContent":["import { isAbsolute, join } from \"node:path\";\n\nexport interface ResolveLocalPathOptions {\n cwd?: string;\n}\n\nexport function resolveLocalPath(rawPath: string, options?: ResolveLocalPathOptions): string {\n if (rawPath === \".\") {\n return process.cwd();\n }\n\n let resolved = rawPath.replaceAll(\"${CWD}\", process.cwd());\n\n if (resolved.startsWith(\"~/\")) {\n const home = process.env.HOME;\n if (!home) {\n throw new Error(\"Cannot resolve '~/' path: HOME environment variable is not set\");\n }\n resolved = join(home, resolved.slice(2));\n }\n\n if (!isAbsolute(resolved)) {\n resolved = join(options?.cwd || process.cwd(), resolved);\n }\n\n return resolved;\n}\n"],"mappings":";;;AAMA,SAAgB,iBAAiB,SAAiB,SAA2C;AAC3F,KAAI,YAAY,IACd,QAAO,QAAQ,KAAK;CAGtB,IAAI,WAAW,QAAQ,WAAW,UAAU,QAAQ,KAAK,CAAC;AAE1D,KAAI,SAAS,WAAW,KAAK,EAAE;EAC7B,MAAM,OAAO,QAAQ,IAAI;AACzB,MAAI,CAAC,KACH,OAAM,IAAI,MAAM,iEAAiE;AAEnF,aAAW,KAAK,MAAM,SAAS,MAAM,EAAE,CAAC;;AAG1C,KAAI,CAAC,WAAW,SAAS,CACvB,YAAW,KAAK,SAAS,OAAO,QAAQ,KAAK,EAAE,SAAS;AAG1D,QAAO"}
package/dist/mime.cjs ADDED
@@ -0,0 +1,53 @@
1
+ let node_path = require("node:path");
2
+
3
+ //#region src/mime.ts
4
+ const MIME_TYPES = {
5
+ png: "image/png",
6
+ jpg: "image/jpeg",
7
+ jpeg: "image/jpeg",
8
+ gif: "image/gif",
9
+ bmp: "image/bmp",
10
+ webp: "image/webp",
11
+ svg: "image/svg+xml",
12
+ ico: "image/x-icon",
13
+ pdf: "application/pdf",
14
+ txt: "text/plain",
15
+ md: "text/markdown",
16
+ js: "text/javascript",
17
+ ts: "text/typescript",
18
+ json: "application/json",
19
+ html: "text/html",
20
+ css: "text/css",
21
+ xml: "text/xml"
22
+ };
23
+ const BINARY_EXTENSIONS = new Set([
24
+ "png",
25
+ "jpg",
26
+ "jpeg",
27
+ "gif",
28
+ "bmp",
29
+ "webp",
30
+ "ico",
31
+ "pdf",
32
+ "zip",
33
+ "tar",
34
+ "gz",
35
+ "exe",
36
+ "dll",
37
+ "so",
38
+ "dylib",
39
+ "wasm"
40
+ ]);
41
+ function getExtension(filePath) {
42
+ return (0, node_path.basename)(filePath).split(".").pop()?.toLowerCase() || "";
43
+ }
44
+ function getMimeType(filePath) {
45
+ return MIME_TYPES[getExtension(filePath)] || "application/octet-stream";
46
+ }
47
+ function isBinaryFile(filePath) {
48
+ return BINARY_EXTENSIONS.has(getExtension(filePath));
49
+ }
50
+
51
+ //#endregion
52
+ exports.getMimeType = getMimeType;
53
+ exports.isBinaryFile = isBinaryFile;
@@ -0,0 +1,6 @@
1
+ //#region src/mime.d.ts
2
+ declare function getMimeType(filePath: string): string;
3
+ declare function isBinaryFile(filePath: string): boolean;
4
+ //#endregion
5
+ export { getMimeType, isBinaryFile };
6
+ //# sourceMappingURL=mime.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mime.d.cts","names":[],"sources":["../src/mime.ts"],"mappings":";iBAgDgB,WAAA,CAAY,QAAA;AAAA,iBAIZ,YAAA,CAAa,QAAA"}
@@ -0,0 +1,6 @@
1
+ //#region src/mime.d.ts
2
+ declare function getMimeType(filePath: string): string;
3
+ declare function isBinaryFile(filePath: string): boolean;
4
+ //#endregion
5
+ export { getMimeType, isBinaryFile };
6
+ //# sourceMappingURL=mime.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mime.d.mts","names":[],"sources":["../src/mime.ts"],"mappings":";iBAgDgB,WAAA,CAAY,QAAA;AAAA,iBAIZ,YAAA,CAAa,QAAA"}
package/dist/mime.mjs ADDED
@@ -0,0 +1,53 @@
1
+ import { basename } from "node:path";
2
+
3
+ //#region src/mime.ts
4
+ const MIME_TYPES = {
5
+ png: "image/png",
6
+ jpg: "image/jpeg",
7
+ jpeg: "image/jpeg",
8
+ gif: "image/gif",
9
+ bmp: "image/bmp",
10
+ webp: "image/webp",
11
+ svg: "image/svg+xml",
12
+ ico: "image/x-icon",
13
+ pdf: "application/pdf",
14
+ txt: "text/plain",
15
+ md: "text/markdown",
16
+ js: "text/javascript",
17
+ ts: "text/typescript",
18
+ json: "application/json",
19
+ html: "text/html",
20
+ css: "text/css",
21
+ xml: "text/xml"
22
+ };
23
+ const BINARY_EXTENSIONS = new Set([
24
+ "png",
25
+ "jpg",
26
+ "jpeg",
27
+ "gif",
28
+ "bmp",
29
+ "webp",
30
+ "ico",
31
+ "pdf",
32
+ "zip",
33
+ "tar",
34
+ "gz",
35
+ "exe",
36
+ "dll",
37
+ "so",
38
+ "dylib",
39
+ "wasm"
40
+ ]);
41
+ function getExtension(filePath) {
42
+ return basename(filePath).split(".").pop()?.toLowerCase() || "";
43
+ }
44
+ function getMimeType(filePath) {
45
+ return MIME_TYPES[getExtension(filePath)] || "application/octet-stream";
46
+ }
47
+ function isBinaryFile(filePath) {
48
+ return BINARY_EXTENSIONS.has(getExtension(filePath));
49
+ }
50
+
51
+ //#endregion
52
+ export { getMimeType, isBinaryFile };
53
+ //# sourceMappingURL=mime.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mime.mjs","names":[],"sources":["../src/mime.ts"],"sourcesContent":["import { basename } from \"node:path\";\n\nconst MIME_TYPES: Record<string, string> = {\n // Images\n png: \"image/png\",\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n gif: \"image/gif\",\n bmp: \"image/bmp\",\n webp: \"image/webp\",\n svg: \"image/svg+xml\",\n ico: \"image/x-icon\",\n // Documents\n pdf: \"application/pdf\",\n txt: \"text/plain\",\n md: \"text/markdown\",\n // Code\n js: \"text/javascript\",\n ts: \"text/typescript\",\n json: \"application/json\",\n html: \"text/html\",\n css: \"text/css\",\n xml: \"text/xml\",\n};\n\nconst BINARY_EXTENSIONS = new Set([\n \"png\",\n \"jpg\",\n \"jpeg\",\n \"gif\",\n \"bmp\",\n \"webp\",\n \"ico\",\n \"pdf\",\n \"zip\",\n \"tar\",\n \"gz\",\n \"exe\",\n \"dll\",\n \"so\",\n \"dylib\",\n \"wasm\",\n]);\n\nfunction getExtension(filePath: string): string {\n return basename(filePath).split(\".\").pop()?.toLowerCase() || \"\";\n}\n\nexport function getMimeType(filePath: string): string {\n return MIME_TYPES[getExtension(filePath)] || \"application/octet-stream\";\n}\n\nexport function isBinaryFile(filePath: string): boolean {\n return BINARY_EXTENSIONS.has(getExtension(filePath));\n}\n"],"mappings":";;;AAEA,MAAM,aAAqC;CAEzC,KAAK;CACL,KAAK;CACL,MAAM;CACN,KAAK;CACL,KAAK;CACL,MAAM;CACN,KAAK;CACL,KAAK;CAEL,KAAK;CACL,KAAK;CACL,IAAI;CAEJ,IAAI;CACJ,IAAI;CACJ,MAAM;CACN,MAAM;CACN,KAAK;CACL,KAAK;CACN;AAED,MAAM,oBAAoB,IAAI,IAAI;CAChC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;AAEF,SAAS,aAAa,UAA0B;AAC9C,QAAO,SAAS,SAAS,CAAC,MAAM,IAAI,CAAC,KAAK,EAAE,aAAa,IAAI;;AAG/D,SAAgB,YAAY,UAA0B;AACpD,QAAO,WAAW,aAAa,SAAS,KAAK;;AAG/C,SAAgB,aAAa,UAA2B;AACtD,QAAO,kBAAkB,IAAI,aAAa,SAAS,CAAC"}
@@ -0,0 +1,34 @@
1
+ let node_path = require("node:path");
2
+ let node_fs_promises = require("node:fs/promises");
3
+ let _aigne_afs = require("@aigne/afs");
4
+
5
+ //#region src/path-guard.ts
6
+ /**
7
+ * Assert that a resolved path stays within the given root directory.
8
+ * Performs both logical path check and realpath-based symlink check.
9
+ */
10
+ async function assertPathWithinRoot(fullPath, rootDir) {
11
+ const mountRoot = (0, node_path.resolve)(rootDir);
12
+ const resolved = (0, node_path.resolve)(fullPath);
13
+ if (resolved !== mountRoot && !resolved.startsWith(`${mountRoot}/`)) throw new _aigne_afs.AFSError("Path traversal is not allowed", "AFS_PERMISSION_DENIED");
14
+ try {
15
+ let real;
16
+ try {
17
+ real = await (0, node_fs_promises.realpath)(resolved);
18
+ } catch {
19
+ const parent = (0, node_path.dirname)(resolved);
20
+ try {
21
+ real = await (0, node_fs_promises.realpath)(parent);
22
+ } catch {
23
+ return;
24
+ }
25
+ }
26
+ const realMountRoot = await (0, node_fs_promises.realpath)(mountRoot);
27
+ if (real !== realMountRoot && !real.startsWith(`${realMountRoot}/`)) throw new _aigne_afs.AFSError("Path traversal via symlink is not allowed", "AFS_PERMISSION_DENIED");
28
+ } catch (error) {
29
+ if (error instanceof _aigne_afs.AFSError) throw error;
30
+ }
31
+ }
32
+
33
+ //#endregion
34
+ exports.assertPathWithinRoot = assertPathWithinRoot;
@@ -0,0 +1,9 @@
1
+ //#region src/path-guard.d.ts
2
+ /**
3
+ * Assert that a resolved path stays within the given root directory.
4
+ * Performs both logical path check and realpath-based symlink check.
5
+ */
6
+ declare function assertPathWithinRoot(fullPath: string, rootDir: string): Promise<void>;
7
+ //#endregion
8
+ export { assertPathWithinRoot };
9
+ //# sourceMappingURL=path-guard.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path-guard.d.cts","names":[],"sources":["../src/path-guard.ts"],"mappings":";;AAQA;;;iBAAsB,oBAAA,CAAqB,QAAA,UAAkB,OAAA,WAAkB,OAAA"}
@@ -0,0 +1,9 @@
1
+ //#region src/path-guard.d.ts
2
+ /**
3
+ * Assert that a resolved path stays within the given root directory.
4
+ * Performs both logical path check and realpath-based symlink check.
5
+ */
6
+ declare function assertPathWithinRoot(fullPath: string, rootDir: string): Promise<void>;
7
+ //#endregion
8
+ export { assertPathWithinRoot };
9
+ //# sourceMappingURL=path-guard.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path-guard.d.mts","names":[],"sources":["../src/path-guard.ts"],"mappings":";;AAQA;;;iBAAsB,oBAAA,CAAqB,QAAA,UAAkB,OAAA,WAAkB,OAAA"}
@@ -0,0 +1,35 @@
1
+ import { dirname, resolve } from "node:path";
2
+ import { realpath } from "node:fs/promises";
3
+ import { AFSError } from "@aigne/afs";
4
+
5
+ //#region src/path-guard.ts
6
+ /**
7
+ * Assert that a resolved path stays within the given root directory.
8
+ * Performs both logical path check and realpath-based symlink check.
9
+ */
10
+ async function assertPathWithinRoot(fullPath, rootDir) {
11
+ const mountRoot = resolve(rootDir);
12
+ const resolved = resolve(fullPath);
13
+ if (resolved !== mountRoot && !resolved.startsWith(`${mountRoot}/`)) throw new AFSError("Path traversal is not allowed", "AFS_PERMISSION_DENIED");
14
+ try {
15
+ let real;
16
+ try {
17
+ real = await realpath(resolved);
18
+ } catch {
19
+ const parent = dirname(resolved);
20
+ try {
21
+ real = await realpath(parent);
22
+ } catch {
23
+ return;
24
+ }
25
+ }
26
+ const realMountRoot = await realpath(mountRoot);
27
+ if (real !== realMountRoot && !real.startsWith(`${realMountRoot}/`)) throw new AFSError("Path traversal via symlink is not allowed", "AFS_PERMISSION_DENIED");
28
+ } catch (error) {
29
+ if (error instanceof AFSError) throw error;
30
+ }
31
+ }
32
+
33
+ //#endregion
34
+ export { assertPathWithinRoot };
35
+ //# sourceMappingURL=path-guard.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path-guard.mjs","names":[],"sources":["../src/path-guard.ts"],"sourcesContent":["import { realpath } from \"node:fs/promises\";\nimport { dirname, resolve } from \"node:path\";\nimport { AFSError } from \"@aigne/afs\";\n\n/**\n * Assert that a resolved path stays within the given root directory.\n * Performs both logical path check and realpath-based symlink check.\n */\nexport async function assertPathWithinRoot(fullPath: string, rootDir: string): Promise<void> {\n const mountRoot = resolve(rootDir);\n const resolved = resolve(fullPath);\n\n // 1. Logical path check (catches ../ traversal)\n if (resolved !== mountRoot && !resolved.startsWith(`${mountRoot}/`)) {\n throw new AFSError(\"Path traversal is not allowed\", \"AFS_PERMISSION_DENIED\");\n }\n\n // 2. Symlink-aware check: resolve real path and verify it's still within root\n try {\n let real: string;\n try {\n real = await realpath(resolved);\n } catch {\n // Target doesn't exist yet (e.g. write to new file) — check parent\n const parent = dirname(resolved);\n try {\n real = await realpath(parent);\n } catch {\n // Parent also doesn't exist — logical check is sufficient\n return;\n }\n }\n const realMountRoot = await realpath(mountRoot);\n if (real !== realMountRoot && !real.startsWith(`${realMountRoot}/`)) {\n throw new AFSError(\"Path traversal via symlink is not allowed\", \"AFS_PERMISSION_DENIED\");\n }\n } catch (error) {\n if (error instanceof AFSError) throw error;\n // Other filesystem errors (e.g. permission denied) — let the actual operation handle them\n }\n}\n"],"mappings":";;;;;;;;;AAQA,eAAsB,qBAAqB,UAAkB,SAAgC;CAC3F,MAAM,YAAY,QAAQ,QAAQ;CAClC,MAAM,WAAW,QAAQ,SAAS;AAGlC,KAAI,aAAa,aAAa,CAAC,SAAS,WAAW,GAAG,UAAU,GAAG,CACjE,OAAM,IAAI,SAAS,iCAAiC,wBAAwB;AAI9E,KAAI;EACF,IAAI;AACJ,MAAI;AACF,UAAO,MAAM,SAAS,SAAS;UACzB;GAEN,MAAM,SAAS,QAAQ,SAAS;AAChC,OAAI;AACF,WAAO,MAAM,SAAS,OAAO;WACvB;AAEN;;;EAGJ,MAAM,gBAAgB,MAAM,SAAS,UAAU;AAC/C,MAAI,SAAS,iBAAiB,CAAC,KAAK,WAAW,GAAG,cAAc,GAAG,CACjE,OAAM,IAAI,SAAS,6CAA6C,wBAAwB;UAEnF,OAAO;AACd,MAAI,iBAAiB,SAAU,OAAM"}
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@aigne/afs-provider-utils",
3
+ "version": "1.11.0-beta.12",
4
+ "description": "Shared utilities for AFS providers: MIME detection, path resolution, path guard",
5
+ "license": "UNLICENSED",
6
+ "publishConfig": {
7
+ "access": "public"
8
+ },
9
+ "author": "Arcblock <blocklet@arcblock.io> https://github.com/arcblock",
10
+ "homepage": "https://github.com/arcblock/afs",
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "git+https://github.com/arcblock/afs"
14
+ },
15
+ "bugs": {
16
+ "url": "https://github.com/arcblock/afs/issues"
17
+ },
18
+ "type": "module",
19
+ "main": "./dist/index.cjs",
20
+ "module": "./dist/index.mjs",
21
+ "types": "./dist/index.d.cts",
22
+ "exports": {
23
+ ".": {
24
+ "require": "./dist/index.cjs",
25
+ "import": "./dist/index.mjs"
26
+ },
27
+ "./*": "./*"
28
+ },
29
+ "files": [
30
+ "dist",
31
+ "LICENSE",
32
+ "README.md",
33
+ "CHANGELOG.md"
34
+ ],
35
+ "dependencies": {
36
+ "@aigne/afs": "^1.11.0-beta.12"
37
+ },
38
+ "devDependencies": {
39
+ "@types/bun": "^1.3.6",
40
+ "npm-run-all": "^4.1.5",
41
+ "rimraf": "^6.1.2",
42
+ "tsdown": "0.20.0-beta.3",
43
+ "typescript": "5.9.2",
44
+ "@aigne/scripts": "0.0.0",
45
+ "@aigne/typescript-config": "0.0.0"
46
+ },
47
+ "scripts": {
48
+ "build": "tsdown",
49
+ "check-types": "tsc --noEmit",
50
+ "clean": "rimraf dist coverage",
51
+ "test": "bun test",
52
+ "test:coverage": "bun test --coverage --coverage-reporter=lcov --coverage-reporter=text"
53
+ }
54
+ }