protobuf-fastdsl 0.1.3 → 0.1.5
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/README.md +64 -20
- package/dist/index.js +5 -11
- package/package.json +9 -9
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# protobuf-fastdsl
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
将 TypeScript 接口在构建时编译为**完全内联**、零依赖的 protobuf 编解码函数。
|
|
4
4
|
|
|
5
5
|
无 `.proto` 文件。无运行时库。无函数调用开销。只需 TypeScript 接口 → 确定化的二进制编解码代码。
|
|
6
6
|
|
|
@@ -8,9 +8,11 @@
|
|
|
8
8
|
|
|
9
9
|
- **零运行时** — 所有 wire-format 逻辑在编译时内联
|
|
10
10
|
- **TypeScript 原生** — 用 `pb<N, Type>` 标记接口字段即可定义 schema
|
|
11
|
+
- **跨文件类型** — 支持 `import type` / `import` 引用其他文件的接口定义
|
|
11
12
|
- **泛型单态化** — `Wrapper<Wrapper<string>>` → 自动生成具体编解码函数
|
|
12
13
|
- **重复字段** — `pb_repeated<N, Type>` 编译为 `Type[]`
|
|
13
14
|
- **编译期预计算 Tag** — 字段标签在编译时折叠为字面量字节
|
|
15
|
+
- **安全的 Fallback** — 未经插件转换时运行会直接抛错,不会静默产生错误结果
|
|
14
16
|
|
|
15
17
|
## 安装
|
|
16
18
|
|
|
@@ -20,30 +22,24 @@ npm install protobuf-fastdsl
|
|
|
20
22
|
|
|
21
23
|
## 快速开始
|
|
22
24
|
|
|
23
|
-
**1.
|
|
25
|
+
**1. 配置构建工具插件:**
|
|
24
26
|
|
|
25
27
|
```ts
|
|
26
|
-
|
|
28
|
+
// vite.config.ts
|
|
29
|
+
import protobufPlugin from 'protobuf-fastdsl/vite';
|
|
27
30
|
|
|
28
31
|
export default defineConfig({
|
|
29
|
-
plugins: [
|
|
32
|
+
plugins: [protobufPlugin()],
|
|
30
33
|
});
|
|
31
34
|
```
|
|
32
35
|
|
|
33
|
-
**2.
|
|
34
|
-
|
|
35
|
-
```json
|
|
36
|
-
{
|
|
37
|
-
"compilerOptions": {
|
|
38
|
-
"types": ["protobuf-fastdsl/types"]
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
**3. 用 TypeScript 接口定义 schema:**
|
|
36
|
+
**2. 定义 protobuf schema(TypeScript 接口):**
|
|
44
37
|
|
|
45
38
|
```ts
|
|
46
|
-
|
|
39
|
+
// schema/user.ts
|
|
40
|
+
import type { pb, pb_repeated, uint_32, bool } from 'protobuf-fastdsl';
|
|
41
|
+
|
|
42
|
+
export interface UserProfile {
|
|
47
43
|
id: pb<1, uint_32>;
|
|
48
44
|
username: pb<2, string>;
|
|
49
45
|
active: pb<3, bool>;
|
|
@@ -51,9 +47,12 @@ interface UserProfile {
|
|
|
51
47
|
}
|
|
52
48
|
```
|
|
53
49
|
|
|
54
|
-
**
|
|
50
|
+
**3. 编解码:**
|
|
55
51
|
|
|
56
52
|
```ts
|
|
53
|
+
import { protobuf_encode, protobuf_decode } from 'protobuf-fastdsl';
|
|
54
|
+
import type { UserProfile } from './schema/user';
|
|
55
|
+
|
|
57
56
|
const bytes = protobuf_encode<UserProfile>({
|
|
58
57
|
id: 42,
|
|
59
58
|
username: 'alice',
|
|
@@ -65,7 +64,7 @@ const user = protobuf_decode<UserProfile>(bytes);
|
|
|
65
64
|
// user.id === 42, user.tags === ['admin', 'dev']
|
|
66
65
|
```
|
|
67
66
|
|
|
68
|
-
|
|
67
|
+
构建时,插件将上述代码转换为:
|
|
69
68
|
|
|
70
69
|
```js
|
|
71
70
|
// 预计算的 tag 字面量,内联 varint 循环,零函数调用
|
|
@@ -73,6 +72,43 @@ const bytes = protobuf_encode_UserProfile({ id: 42, ... });
|
|
|
73
72
|
const user = protobuf_decode_UserProfile(bytes);
|
|
74
73
|
```
|
|
75
74
|
|
|
75
|
+
如果忘记配置插件,`protobuf_encode` / `protobuf_decode` 会在运行时抛出错误提示。
|
|
76
|
+
|
|
77
|
+
## 跨文件类型引用
|
|
78
|
+
|
|
79
|
+
接口定义和编解码调用可以在不同文件中,插件会自动跟踪 import 链:
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
// inner.ts
|
|
83
|
+
import type { pb, uint_32 } from 'protobuf-fastdsl';
|
|
84
|
+
|
|
85
|
+
export interface Inner {
|
|
86
|
+
value: pb<1, uint_32>;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// outer.ts
|
|
90
|
+
import { protobuf_encode } from 'protobuf-fastdsl';
|
|
91
|
+
import type { Inner } from './inner';
|
|
92
|
+
import type { pb } from 'protobuf-fastdsl';
|
|
93
|
+
|
|
94
|
+
interface Outer {
|
|
95
|
+
inner: pb<1, Inner>;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const bytes = protobuf_encode<Outer>({ inner: { value: 42 } });
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
支持传递性导入(A → B → C),插件会递归解析所有依赖。
|
|
102
|
+
|
|
103
|
+
## 别名导入
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
import { protobuf_encode as encode, protobuf_decode as decode } from 'protobuf-fastdsl';
|
|
107
|
+
|
|
108
|
+
const bytes = encode<UserProfile>(data); // → protobuf_encode_UserProfile(data)
|
|
109
|
+
const user = decode<UserProfile>(bytes); // → protobuf_decode_UserProfile(bytes)
|
|
110
|
+
```
|
|
111
|
+
|
|
76
112
|
## 泛型单态化
|
|
77
113
|
|
|
78
114
|
定义泛型 protobuf 模板,使用具体类型实例化:
|
|
@@ -95,8 +131,8 @@ const data = protobuf_encode<Wrapper<Wrapper<string>>>({
|
|
|
95
131
|
|---------------|-----------------|-----------|
|
|
96
132
|
| `uint_32`, `int_32` | `number` | Varint |
|
|
97
133
|
| `uint_64`, `int_64` | `bigint` | Varint |
|
|
98
|
-
| `sint_32` | `number` | Varint |
|
|
99
|
-
| `sint_64` | `bigint` | Varint |
|
|
134
|
+
| `sint_32` | `number` | Varint (ZigZag) |
|
|
135
|
+
| `sint_64` | `bigint` | Varint (ZigZag) |
|
|
100
136
|
| `bool` | `boolean` | Varint |
|
|
101
137
|
| `string` | `string` | LengthDelimited |
|
|
102
138
|
| `bytes` | `Uint8Array` | LengthDelimited |
|
|
@@ -111,6 +147,14 @@ const data = protobuf_encode<Wrapper<Wrapper<string>>>({
|
|
|
111
147
|
|
|
112
148
|
说明:
|
|
113
149
|
- 所有 64 位整数类型在 TypeScript 中统一映射为 `bigint`
|
|
150
|
+
- `sint_32` / `sint_64` 使用 ZigZag 编码,适用于频繁出现负数的场景
|
|
151
|
+
|
|
152
|
+
## 包入口
|
|
153
|
+
|
|
154
|
+
| 路径 | 用途 |
|
|
155
|
+
|------|------|
|
|
156
|
+
| `protobuf-fastdsl` | 用户代码 — `protobuf_encode`、`protobuf_decode`、所有类型 |
|
|
157
|
+
| `protobuf-fastdsl/vite` | Vite 插件 |
|
|
114
158
|
|
|
115
159
|
## ⚡ 性能测试
|
|
116
160
|
|
package/dist/index.js
CHANGED
|
@@ -1167,7 +1167,7 @@ function replaceCallSites(code, registry) {
|
|
|
1167
1167
|
import ts6 from "typescript";
|
|
1168
1168
|
import { readFileSync, existsSync } from "fs";
|
|
1169
1169
|
import { dirname, resolve } from "path";
|
|
1170
|
-
function
|
|
1170
|
+
function extractImports(sf) {
|
|
1171
1171
|
const result = [];
|
|
1172
1172
|
for (const stmt of sf.statements) {
|
|
1173
1173
|
if (!ts6.isImportDeclaration(stmt) || !stmt.importClause) continue;
|
|
@@ -1176,15 +1176,9 @@ function extractTypeImports(sf) {
|
|
|
1176
1176
|
const specifier = spec.text;
|
|
1177
1177
|
const clause = stmt.importClause;
|
|
1178
1178
|
const names = [];
|
|
1179
|
-
if (clause.
|
|
1180
|
-
if (clause.namedBindings && ts6.isNamedImports(clause.namedBindings)) {
|
|
1181
|
-
for (const el of clause.namedBindings.elements) {
|
|
1182
|
-
names.push(el.name.text);
|
|
1183
|
-
}
|
|
1184
|
-
}
|
|
1185
|
-
} else if (clause.namedBindings && ts6.isNamedImports(clause.namedBindings)) {
|
|
1179
|
+
if (clause.namedBindings && ts6.isNamedImports(clause.namedBindings)) {
|
|
1186
1180
|
for (const el of clause.namedBindings.elements) {
|
|
1187
|
-
|
|
1181
|
+
names.push(el.name.text);
|
|
1188
1182
|
}
|
|
1189
1183
|
}
|
|
1190
1184
|
if (names.length > 0) result.push({ names, specifier });
|
|
@@ -1229,7 +1223,7 @@ function resolveImports(code, importerPath, cache) {
|
|
|
1229
1223
|
visiting.add(abs);
|
|
1230
1224
|
const src = fileCode ?? readFileSync(abs, "utf-8");
|
|
1231
1225
|
const sf = ts6.createSourceFile(abs, src, ts6.ScriptTarget.Latest, true);
|
|
1232
|
-
const imports =
|
|
1226
|
+
const imports = extractImports(sf);
|
|
1233
1227
|
for (const imp of imports) {
|
|
1234
1228
|
const resolved = resolveModulePath(imp.specifier, abs);
|
|
1235
1229
|
if (!resolved) continue;
|
|
@@ -1263,7 +1257,7 @@ function protobufVitePlugin() {
|
|
|
1263
1257
|
if (!id.endsWith(".ts") || id.endsWith(".d.ts")) return null;
|
|
1264
1258
|
const imported = resolveImports(code, id, fileCache);
|
|
1265
1259
|
const { registry, callSites, sourceFile } = analyze(code, id, imported);
|
|
1266
|
-
if (registry.size === 0) return null;
|
|
1260
|
+
if (registry.size === 0 && callSites.length === 0) return null;
|
|
1267
1261
|
const generatedCode = generateCode(registry);
|
|
1268
1262
|
const { transformedCode, hasReplacements } = applyReplacements(code, sourceFile, callSites, registry);
|
|
1269
1263
|
if (!hasReplacements && generatedCode === "") return null;
|
package/package.json
CHANGED
|
@@ -1,24 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "protobuf-fastdsl",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"main": "dist/
|
|
6
|
-
"types": "
|
|
5
|
+
"main": "dist/runtime.js",
|
|
6
|
+
"types": "protobuf.d.ts",
|
|
7
7
|
"files": [
|
|
8
8
|
"dist",
|
|
9
9
|
"protobuf.d.ts"
|
|
10
10
|
],
|
|
11
11
|
"exports": {
|
|
12
12
|
".": {
|
|
13
|
-
"types": "./dist/index.d.ts",
|
|
14
|
-
"import": "./dist/index.js"
|
|
15
|
-
},
|
|
16
|
-
"./runtime": {
|
|
17
13
|
"types": "./protobuf.d.ts",
|
|
18
14
|
"import": "./dist/runtime.js"
|
|
19
15
|
},
|
|
20
|
-
"./
|
|
21
|
-
"types": "./
|
|
16
|
+
"./vite": {
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"import": "./dist/index.js"
|
|
22
19
|
}
|
|
23
20
|
},
|
|
24
21
|
"scripts": {
|
|
@@ -33,6 +30,9 @@
|
|
|
33
30
|
"peerDependencies": {
|
|
34
31
|
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0"
|
|
35
32
|
},
|
|
33
|
+
"peerDependenciesMeta": {
|
|
34
|
+
"vite": { "optional": true }
|
|
35
|
+
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@types/node": "^25.4.0",
|
|
38
38
|
"tsup": "^8.0.0",
|