init
This commit is contained in:
34
.gitignore
vendored
Normal file
34
.gitignore
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
# dependencies (bun install)
|
||||
node_modules
|
||||
|
||||
# output
|
||||
out
|
||||
dist
|
||||
*.tgz
|
||||
|
||||
# code coverage
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# logs
|
||||
logs
|
||||
_.log
|
||||
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# caches
|
||||
.eslintcache
|
||||
.cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# IntelliJ based IDEs
|
||||
.idea
|
||||
|
||||
# Finder (MacOS) folder config
|
||||
.DS_Store
|
||||
15
README.md
Normal file
15
README.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# infrastructure-runtime-adapter
|
||||
|
||||
To install dependencies:
|
||||
|
||||
```bash
|
||||
bun install
|
||||
```
|
||||
|
||||
To run:
|
||||
|
||||
```bash
|
||||
bun run index.ts
|
||||
```
|
||||
|
||||
This project was created using `bun init` in bun v1.3.13. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime.
|
||||
49
bun.lock
Normal file
49
bun.lock
Normal file
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"lockfileVersion": 1,
|
||||
"configVersion": 1,
|
||||
"workspaces": {
|
||||
"": {
|
||||
"name": "infrastructure-runtime-adapter",
|
||||
"dependencies": {
|
||||
"c12": "^4.0.0-beta.5",
|
||||
"pathe": "^2.0.3",
|
||||
"std-env": "^4.1.0",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5",
|
||||
},
|
||||
},
|
||||
},
|
||||
"packages": {
|
||||
"@types/bun": ["@types/bun@1.3.13", "", { "dependencies": { "bun-types": "1.3.13" } }, "sha512-9fqXWk5YIHGGnUau9TEi+qdlTYDAnOj+xLCmSTwXfAIqXr2x4tytJb43E9uCvt09zJURKXwAtkoH4nLQfzeTXw=="],
|
||||
|
||||
"@types/node": ["@types/node@25.7.0", "", { "dependencies": { "undici-types": "~7.21.0" } }, "sha512-z+pdZyxE+RTQE9AcboAZCb4otwcrvgHD+GlBpPgn0emDVt0ohrTMhAwlr2Wd9nZ+nihhYFxO2pThz3C5qSu2Eg=="],
|
||||
|
||||
"bun-types": ["bun-types@1.3.13", "", { "dependencies": { "@types/node": "*" } }, "sha512-QXKeHLlOLqQX9LgYaHJfzdBaV21T63HhFJnvuRCcjZiaUDpbs5ED1MgxbMra71CsryN/1dAoXuJJJwIv/2drVA=="],
|
||||
|
||||
"c12": ["c12@4.0.0-beta.5", "", { "dependencies": { "confbox": "^0.2.4", "defu": "^6.1.7", "exsolve": "^1.0.8", "pathe": "^2.0.3", "pkg-types": "^2.3.1", "rc9": "^3.0.1" }, "peerDependencies": { "chokidar": "^5", "dotenv": "*", "giget": "*", "jiti": "*", "magicast": "*" }, "optionalPeers": ["chokidar", "dotenv", "giget", "jiti", "magicast"] }, "sha512-yWGCPCQGJeFq4R0mFg5HOhC3Rg+B0PCdM+ldXWUhughoGgeeq8/tjRmXh4/lmhKWyhf+KOFxB/JMXf0Yv1Fd5A=="],
|
||||
|
||||
"confbox": ["confbox@0.2.4", "", {}, "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ=="],
|
||||
|
||||
"defu": ["defu@6.1.7", "", {}, "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ=="],
|
||||
|
||||
"destr": ["destr@2.0.5", "", {}, "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA=="],
|
||||
|
||||
"exsolve": ["exsolve@1.0.8", "", {}, "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA=="],
|
||||
|
||||
"pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
||||
|
||||
"pkg-types": ["pkg-types@2.3.1", "", { "dependencies": { "confbox": "^0.2.4", "exsolve": "^1.0.8", "pathe": "^2.0.3" } }, "sha512-y+ichcgc2LrADuhLNAx8DFjVfgz91pRxfZdI3UDhxHvcVEZsenLO+7XaU5vOp0u/7V/wZ+plyuQxtrDlZJ+yeg=="],
|
||||
|
||||
"rc9": ["rc9@3.0.1", "", { "dependencies": { "defu": "^6.1.6", "destr": "^2.0.5" } }, "sha512-gMDyleLWVE+i6Sgtc0QbbY6pEKqYs97NGi6isHQPqYlLemPoO8dxQ3uGi0f4NiP98c+jMW6cG1Kx9dDwfvqARQ=="],
|
||||
|
||||
"std-env": ["std-env@4.1.0", "", {}, "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ=="],
|
||||
|
||||
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
||||
|
||||
"undici-types": ["undici-types@7.21.0", "", {}, "sha512-w9IMgQrz4O0YN1LtB7K5P63vhlIOvC7opSmouCJ+ZywlPAlO9gIkJ+otk6LvGpAs2wg4econaCz3TvQ9xPoyuQ=="],
|
||||
}
|
||||
}
|
||||
17
package.json
Normal file
17
package.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "infrastructure-runtime-adapter",
|
||||
"module": "index.ts",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5"
|
||||
},
|
||||
"dependencies": {
|
||||
"c12": "^4.0.0-beta.5",
|
||||
"pathe": "^2.0.3",
|
||||
"std-env": "^4.1.0"
|
||||
}
|
||||
}
|
||||
80
runtime/adapters/bun.ts
Normal file
80
runtime/adapters/bun.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import type {
|
||||
RuntimeContext,
|
||||
ProcHandle,
|
||||
CompatibleBodyInit,
|
||||
SpawnOptions,
|
||||
} from "../types";
|
||||
import { resolve } from "pathe"; // Unjs 标准:跨平台路径处理
|
||||
import { isCI } from "std-env";
|
||||
|
||||
export const createBunRuntime = (): RuntimeContext => ({
|
||||
fs: {
|
||||
exists: (path) => Bun.file(path).exists(),
|
||||
// 返回标准 Response,极大简化了后续的文件解析逻辑
|
||||
read: async (path) => new Response(Bun.file(path)),
|
||||
write: async (path, data: CompatibleBodyInit) => {
|
||||
await Bun.write(path, data as any);
|
||||
},
|
||||
openWritable: (path) => {
|
||||
const file = Bun.file(path);
|
||||
// 利用 Bun.file().writer() 转换为符合 Web 标准的 WritableStream
|
||||
return new WritableStream({
|
||||
async write(chunk) {
|
||||
const writer = file.writer();
|
||||
writer.write(chunk);
|
||||
writer.flush();
|
||||
},
|
||||
});
|
||||
},
|
||||
mkdir: async (path, options) => {
|
||||
const fs = await import("node:fs/promises");
|
||||
await fs.mkdir(path, options);
|
||||
},
|
||||
remove: async (path) => {
|
||||
const fs = await import("node:fs/promises");
|
||||
await fs.rm(path, { recursive: true, force: true });
|
||||
},
|
||||
symlink: async (target, path) => {
|
||||
const fs = await import("node:fs/promises");
|
||||
await fs.symlink(target, path);
|
||||
},
|
||||
readdir: async (path) => {
|
||||
const fs = await import("node:fs/promises");
|
||||
return await fs.readdir(path);
|
||||
},
|
||||
stat: async (path) => {
|
||||
const { stat } = await import("node:fs/promises");
|
||||
const s = await stat(path);
|
||||
return {
|
||||
size: s.size,
|
||||
mtime: s.mtime,
|
||||
isDirectory: s.isDirectory(),
|
||||
};
|
||||
},
|
||||
},
|
||||
proc: {
|
||||
spawn: (args: string[], options: SpawnOptions = {}): ProcHandle => {
|
||||
// 核心改进:自动处理交互逻辑
|
||||
// 如果是在 CI 环境,强制使用 pipe 避免挂起;否则根据用户需求或自动选择
|
||||
const mode = options.stdio || (isCI ? "pipe" : "inherit");
|
||||
|
||||
const process = Bun.spawn(args, {
|
||||
cwd: options.cwd,
|
||||
env: options.env,
|
||||
stdout: mode,
|
||||
stderr: mode,
|
||||
stdin: mode,
|
||||
});
|
||||
|
||||
return {
|
||||
pid: process.pid,
|
||||
exited: process.exited,
|
||||
kill: (sig) => process.kill(sig as any),
|
||||
// 仅在非继承模式下暴露流,避免类型与行为冲突
|
||||
stdout: mode === "pipe" ? (process.stdout as any) : undefined,
|
||||
stderr: mode === "pipe" ? (process.stderr as any) : undefined,
|
||||
stdin: mode === "pipe" ? (process.stdin as any) : undefined,
|
||||
};
|
||||
},
|
||||
},
|
||||
});
|
||||
85
runtime/types.ts
Normal file
85
runtime/types.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import type { ReadableStream, WritableStream } from "node:stream/web";
|
||||
|
||||
/**
|
||||
* 显式定义 BodyInit 兼容类型,确保在没有 DOM 类型的环境下也能编译
|
||||
* 涵盖了 string, Blob, ArrayBuffer, 以及 ReadableStream
|
||||
*/
|
||||
export type CompatibleBodyInit =
|
||||
| string
|
||||
| Blob
|
||||
| ArrayBuffer
|
||||
| Uint8Array
|
||||
| ReadableStream<Uint8Array>;
|
||||
|
||||
export interface SpawnOptions {
|
||||
cwd?: string;
|
||||
env?: Record<string, string>;
|
||||
/**
|
||||
* 参考 Unjs/Nitro 的设计:
|
||||
* 'pipe': 走 Web Stream,适合自动化处理
|
||||
* 'inherit': 走物理 TTY,适合交互式 CLI (如你的 drizzle-kit 报错场景)
|
||||
*/
|
||||
stdio?: "pipe" | "inherit";
|
||||
}
|
||||
|
||||
export interface FileStat {
|
||||
size: number;
|
||||
mtime: Date;
|
||||
isDirectory: boolean;
|
||||
}
|
||||
|
||||
export interface ProcHandle {
|
||||
pid?: number;
|
||||
exited: Promise<number | null>;
|
||||
kill: (signal?: string) => void;
|
||||
// 符合 WinterCG 的流定义
|
||||
stdout?: ReadableStream<Uint8Array>;
|
||||
stderr?: ReadableStream<Uint8Array>;
|
||||
stdin?: WritableStream<Uint8Array>;
|
||||
}
|
||||
|
||||
export interface RuntimeContext {
|
||||
fs: {
|
||||
// --- 基础接口
|
||||
exists: (path: string) => Promise<boolean>;
|
||||
read: (path: string) => Promise<Response>;
|
||||
/** 使用我们定义的兼容类型 */
|
||||
write: (path: string, data: CompatibleBodyInit) => Promise<void>;
|
||||
|
||||
/**
|
||||
* 流式写入:适合导出大型 SQL 备份或持续写入日志
|
||||
* 遵循 Web 文件系统规范命名习惯
|
||||
* 返回 WritableStream,用于流式写入
|
||||
* 流式写入句柄 (对齐 Web WritableStream 概念)
|
||||
* 适合大文件写入或持续日志
|
||||
*/
|
||||
openWritable: (path: string) => WritableStream<Uint8Array>;
|
||||
/** 创建目录:recursive 选项是实现“一键初始化数据库项目”的关键 */
|
||||
mkdir: (
|
||||
path: string,
|
||||
options?: { recursive?: boolean },
|
||||
) => Promise<void>;
|
||||
/** 删除:支持递归删除整个数据库文件夹 */
|
||||
remove: (path: string) => Promise<void>;
|
||||
/** 软链接:这就是你实现 dbs/ 链接的核心能力 */
|
||||
symlink: (target: string, path: string) => Promise<void>;
|
||||
/** 读取目录下的文件列表 */
|
||||
readdir: (
|
||||
path: string,
|
||||
options?: { recursive?: boolean },
|
||||
) => Promise<string[]>;
|
||||
|
||||
// --- 建议增加的增强接口 ---
|
||||
/** 获取文件状态:用于判断配置文件是否被篡改或检查数据大小 */
|
||||
stat: (path: string) => Promise<FileStat>;
|
||||
/** 监听文件:如果 .env 或 schema 变了,管理程序可以自动重载或提醒 */
|
||||
watch?: (
|
||||
path: string,
|
||||
callback: (event: "change" | "rename") => void,
|
||||
) => () => void;
|
||||
};
|
||||
proc: {
|
||||
//test
|
||||
spawn: (args: string[], options?: SpawnOptions) => ProcHandle;
|
||||
};
|
||||
}
|
||||
12
runtime/utils/path.ts
Normal file
12
runtime/utils/path.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { isWindows } from "std-env";
|
||||
|
||||
/**
|
||||
* 获取跨平台兼容的二进制名称 (逻辑层)
|
||||
*/
|
||||
export function getBinName(name: string) {
|
||||
// 仅在 Windows 且没有后缀时尝试添加 .exe
|
||||
if (isWindows && !name.endsWith(".exe") && !name.includes(".")) {
|
||||
return `${name}.exe`;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
30
tsconfig.json
Normal file
30
tsconfig.json
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
// Environment setup & latest features
|
||||
"lib": ["ESNext"],
|
||||
"target": "ESNext",
|
||||
"module": "Preserve",
|
||||
"moduleDetection": "force",
|
||||
"jsx": "react-jsx",
|
||||
"allowJs": true,
|
||||
"types": ["bun"],
|
||||
|
||||
// Bundler mode
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"noEmit": true,
|
||||
|
||||
// Best practices
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"noImplicitOverride": true,
|
||||
|
||||
// Some stricter flags (disabled by default)
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"noPropertyAccessFromIndexSignature": false
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user