@yss123/cage-connectors 0.2.0 → 0.3.1
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 +94 -9
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/primitives.d.ts +17 -0
- package/dist/primitives.js +20 -0
- package/dist/primitives.js.map +1 -1
- package/dist/runtime.d.ts +27 -4
- package/dist/runtime.js +181 -63
- package/dist/runtime.js.map +1 -1
- package/dist/stream.d.ts +12 -0
- package/dist/stream.js +84 -0
- package/dist/stream.js.map +1 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,19 +1,104 @@
|
|
|
1
1
|
# @yss123/cage-connectors
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Cage 的外部系统连接层。声明允许的主机、操作、请求/响应 schema、密钥名称和重试策略,把领域事件映射为出站请求,并把成功回执或验签回调映射回领域命令。适用于 ERP、外部接口和模型适配器的 IO 边界。
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## 安装
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
需要 Node.js 24+,与其他 Cage 包保持同版本。在 domain 包安装:
|
|
8
8
|
|
|
9
9
|
```powershell
|
|
10
|
-
pnpm add @yss123/cage-connectors
|
|
10
|
+
pnpm add @yss123/cage-connectors zod
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
## 声明外部协议
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
- [框架源码与范例](https://github.com/shunshunyue/cage)
|
|
17
|
-
- [问题反馈](https://github.com/shunshunyue/cage/issues)
|
|
15
|
+
以下文件可放入 `packages/domain/src/integrations/erp.ts`。声明时不访问网络或读取密钥;`example.com` 主机需要替换为实际服务,并验证远端幂等协议。
|
|
18
16
|
|
|
19
|
-
|
|
17
|
+
```ts
|
|
18
|
+
/** ERP 协议声明;只列出允许传出的字段,实际 IO 交给 worker。 */
|
|
19
|
+
import { connector, op } from '@yss123/cage-connectors';
|
|
20
|
+
import { z } from 'zod';
|
|
21
|
+
|
|
22
|
+
/** 外部单据写操作;retryableWrite 要求对方按请求幂等键去重。 */
|
|
23
|
+
export const erp = connector('erp', {
|
|
24
|
+
hosts: { test: 'erp-sandbox.example.com', prod: 'erp.example.com' },
|
|
25
|
+
secret: 'ERP_API_TOKEN',
|
|
26
|
+
timeoutMs: 5000,
|
|
27
|
+
operations: {
|
|
28
|
+
pushOrder: op({
|
|
29
|
+
effect: 'retryableWrite',
|
|
30
|
+
method: 'POST',
|
|
31
|
+
path: '/api/purchase-orders',
|
|
32
|
+
request: z.object({
|
|
33
|
+
externalId: z.string(),
|
|
34
|
+
lines: z.array(z.object({ sku: z.string(), qty: z.number() })),
|
|
35
|
+
}),
|
|
36
|
+
response: z.object({ erpNo: z.string() }),
|
|
37
|
+
}),
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
在采购模板中可以继续加入事件映射;这里的事件和命令来自现有采购模块:
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
import { outbound } from '@yss123/cage-connectors';
|
|
46
|
+
import { OrderApproved, recordErpNo } from '../modules/purchase/index.js';
|
|
47
|
+
|
|
48
|
+
/** 审批事件提交后出站,回执通过 external 命令回写,不直接改表。 */
|
|
49
|
+
export const pushToErp = outbound(OrderApproved, erp.operations.pushOrder, {
|
|
50
|
+
idempotencyKey: (event) => `po-${event.orderId}`,
|
|
51
|
+
map: (event) => ({
|
|
52
|
+
externalId: event.orderId,
|
|
53
|
+
lines: event.items.map((item) => ({ sku: item.sku, qty: item.qty })),
|
|
54
|
+
}),
|
|
55
|
+
onSuccess: {
|
|
56
|
+
command: recordErpNo,
|
|
57
|
+
map: (response, event) => ({ id: event.orderId, erpNo: response.erpNo }),
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
将声明从 domain 根入口导出,运行 `cage check/gen`,按需迁移后启动 API 与 worker。部署通过环境或 `SecretProvider` 提供 `ERP_API_TOKEN` 的真实值;源码只保留密钥名称。
|
|
63
|
+
|
|
64
|
+
## 本地 fake 与真实环境
|
|
65
|
+
|
|
66
|
+
worker 启动入口注册模拟实现,业务声明仍使用同一协议:
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
import { fakeConnector, stage } from '@yss123/cage-connectors';
|
|
70
|
+
import * as domain from '@app/domain';
|
|
71
|
+
|
|
72
|
+
// 仅 fake 阶段使用固定响应;test/prod 阶段按声明连接真实主机。
|
|
73
|
+
if (stage() === 'fake') {
|
|
74
|
+
fakeConnector(domain.erp, {
|
|
75
|
+
pushOrder: (request) => ({ erpNo: `DEMO-${request.externalId}` }),
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
通过 `CAGE_STAGE` 选择阶段。fake 不代表第三方联调通过,生产必须验证协议、验签、超时和幂等性。密钥与本机日志不提交到源码。
|
|
81
|
+
|
|
82
|
+
## 公开入口与保证范围
|
|
83
|
+
|
|
84
|
+
| 入口 | 用途 |
|
|
85
|
+
| --- | --- |
|
|
86
|
+
| `connector`、`op` | 主机白名单、操作 schema、超时、限流和重试声明 |
|
|
87
|
+
| `outbound` | 领域事件到事务外操作的映射,成功回写调用命令 |
|
|
88
|
+
| `inbound`、`hmacSha256` | 原始正文验签、载荷校验及外部命令调用 |
|
|
89
|
+
| `fakeConnector`、`fakeCalls` | 注册模拟处理器与检查模拟调用 |
|
|
90
|
+
| `SecretProvider`、`setSecretProvider` | 替换服务端密钥来源,不能在浏览器调用 |
|
|
91
|
+
| `call`、`streamJson` | 受控服务端适配器的外呼与流读取,不用于业务 handler 直接 IO |
|
|
92
|
+
| `ConnectorError` | 区分外部协议、网络和执行失败 |
|
|
93
|
+
|
|
94
|
+
`effect: 'read'`、`'retryableWrite'`、`'write'` 分别表示读取、远端支持幂等的可重试写、不可安全重试写。不要为方便重试而把任意写标为 `retryableWrite`。
|
|
95
|
+
|
|
96
|
+
出站 IO 与业务数据库不共享事务,框架保存执行租约和回执以支持恢复,但不能回滚远端。结果未知的不可安全重试写会隔离;当前没有通用确认远端回执或解除 unknown 的管理接口,普通死信重放不能替代业务核对。连接器限流是每进程预算,多实例共享限流需另行设计。
|
|
97
|
+
|
|
98
|
+
## 文档
|
|
99
|
+
|
|
100
|
+
- [事件、出站与事务边界](https://github.com/shunshunyue/cage/blob/main/docs/ai/architecture.md)
|
|
101
|
+
- [采购 ERP 完整声明](https://github.com/shunshunyue/cage/blob/main/examples/purchase-admin/packages/domain/src/integrations/erp.ts)
|
|
102
|
+
- [排错方法](https://github.com/shunshunyue/cage/blob/main/docs/ai/troubleshooting.md)
|
|
103
|
+
|
|
104
|
+
源码与反馈:[GitHub](https://github.com/shunshunyue/cage)。许可证:[MIT](https://github.com/shunshunyue/cage/blob/main/LICENSE)。
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 声明入口提供外部协议与事件映射,运行入口负责真实 IO/fake 和恢复协议。
|
|
3
|
+
* 业务 handler 不直接调用此包的网络接口;异步写入通过 outbound,入站结果通过领域命令落实。
|
|
4
|
+
*/
|
|
5
|
+
export { streamJson } from './stream.js';
|
|
1
6
|
export { connector, op, outbound, inbound, hmacSha256, type Connector, type ConnectorDef, type Op, type OpDef, type Effect, type Outbound, type Inbound, type Verifier, } from './primitives.js';
|
|
2
7
|
export { call, stage, secrets, setSecretProvider, fakeConnector, fakeCalls, sampleFromSchema, outboundConsumer, ConnectorError, type SecretProvider, } from './runtime.js';
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
// @yss123/cage-connectors:第三方连接器原语与运行时。
|
|
2
|
+
/**
|
|
3
|
+
* 声明入口提供外部协议与事件映射,运行入口负责真实 IO/fake 和恢复协议。
|
|
4
|
+
* 业务 handler 不直接调用此包的网络接口;异步写入通过 outbound,入站结果通过领域命令落实。
|
|
5
|
+
*/
|
|
6
|
+
export { streamJson } from './stream.js';
|
|
2
7
|
export { connector, op, outbound, inbound, hmacSha256, } from './primitives.js';
|
|
3
8
|
export { call, stage, secrets, setSecretProvider, fakeConnector, fakeCalls, sampleFromSchema, outboundConsumer, ConnectorError, } from './runtime.js';
|
|
4
9
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,wCAAwC;AACxC,OAAO,EACL,SAAS,EAAE,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,GAE7C,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,SAAS,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,cAAc,GAEtH,MAAM,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,wCAAwC;AACxC;;;GAGG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EACL,SAAS,EAAE,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,GAE7C,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,SAAS,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,cAAc,GAEtH,MAAM,cAAc,CAAC"}
|
package/dist/primitives.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import type { EventConsumer } from '@yss123/cage-kernel/worker';
|
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
/** 副作用等级:read 只读;retryableWrite 可重试写(对方按幂等键去重);write 不可重试写(失败直接进死信,人工处理) */
|
|
5
5
|
export type Effect = 'read' | 'retryableWrite' | 'write';
|
|
6
|
+
/** 声明单次外部协议;effect 必须反映远端真实幂等保证,不能仅为方便重试而标 retryableWrite。 */
|
|
6
7
|
export interface OpDef<Req extends z.ZodObject<any> = z.ZodObject<any>, Res extends z.ZodType = z.ZodType> {
|
|
7
8
|
effect: Effect;
|
|
8
9
|
method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
@@ -16,7 +17,9 @@ export interface Op<Req extends z.ZodObject<any> = z.ZodObject<any>, Res extends
|
|
|
16
17
|
readonly name: string;
|
|
17
18
|
readonly connector: Connector;
|
|
18
19
|
}
|
|
20
|
+
/** 保存操作规格并检查绝对路径形式;此时没有连接器环境/密钥,也不会发出请求。 */
|
|
19
21
|
export declare function op<Req extends z.ZodObject<any>, Res extends z.ZodType>(def: OpDef<Req, Res>): OpDef<Req, Res>;
|
|
22
|
+
/** 外部系统的固定边界;hosts 按运行阶段选择,密钥仅声明名字,超时毫秒、限流每秒请求数。 */
|
|
20
23
|
export interface ConnectorDef<Ops extends Record<string, OpDef<any, any>>> {
|
|
21
24
|
/** 地址白名单:按环境各一个,代码里写不了任意地址 */
|
|
22
25
|
hosts: {
|
|
@@ -53,6 +56,10 @@ export interface Connector<Ops extends Record<string, OpDef<any, any>> = Record<
|
|
|
53
56
|
readonly [K in keyof Ops]: Op<Ops[K]['request'], Ops[K]['response']>;
|
|
54
57
|
};
|
|
55
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* 校验名称、超时、限流与主机形式,冻结操作并回链到所属连接器。
|
|
61
|
+
* hosts 不含路径/协议,运行时按阶段选择;构造不读取密钥或访问网络,便于静态抽取与离线检查。
|
|
62
|
+
*/
|
|
56
63
|
export declare function connector<Ops extends Record<string, OpDef<any, any>>>(name: string, def: ConnectorDef<Ops>): Connector<Ops>;
|
|
57
64
|
export interface Outbound<E extends AnyEvent = AnyEvent, O extends Op = Op> {
|
|
58
65
|
readonly [OUTBOUND]: true;
|
|
@@ -70,6 +77,10 @@ export interface Outbound<E extends AnyEvent = AnyEvent, O extends Op = Op> {
|
|
|
70
77
|
readonly fields?: readonly string[];
|
|
71
78
|
readonly [CONSUMERS]: (k: Kernel) => EventConsumer[];
|
|
72
79
|
}
|
|
80
|
+
/**
|
|
81
|
+
* 把领域事件映射为事务外写操作;只读操作不允许挂出站,成功回写必须指向领域命令。
|
|
82
|
+
* map 返回 null 可跳过当前事件,幂等键需要跨重投稳定;消费器由 CONSUMERS 在 worker 装配。
|
|
83
|
+
*/
|
|
73
84
|
export declare function outbound<E extends AnyEvent, O extends Op>(ev: E, operation: O, def: {
|
|
74
85
|
idempotencyKey: (e: PayloadOf<E>) => string;
|
|
75
86
|
map: (e: PayloadOf<E>) => z.input<O['request']> | null;
|
|
@@ -79,12 +90,14 @@ export declare function outbound<E extends AnyEvent, O extends Op>(ev: E, operat
|
|
|
79
90
|
};
|
|
80
91
|
fields?: string[];
|
|
81
92
|
}): Outbound<E, O>;
|
|
93
|
+
/** 入站原始字节验签协议;secret 是注入名称,verify 不解析业务载荷或决定其领域权限。 */
|
|
82
94
|
export interface Verifier {
|
|
83
95
|
readonly kind: string;
|
|
84
96
|
readonly secret: string;
|
|
85
97
|
verify(rawBody: string, headers: Record<string, string | string[] | undefined>, secretValue: string): boolean;
|
|
86
98
|
}
|
|
87
99
|
/** HMAC-SHA256 验签:签名放在请求头(默认 x-signature),值为 hex 或 sha256=hex */
|
|
100
|
+
/** 签名基于原始正文计算,接受 hex 或 sha256=hex;长度校验后恒定时间比较,不能对解析后 JSON 重算。 */
|
|
88
101
|
export declare function hmacSha256(secret: string, opts?: {
|
|
89
102
|
header?: string;
|
|
90
103
|
}): Verifier;
|
|
@@ -99,6 +112,10 @@ export interface Inbound<P extends z.ZodType = z.ZodType> {
|
|
|
99
112
|
readonly command: AnyCommand;
|
|
100
113
|
readonly map: (p: z.output<P>) => unknown;
|
|
101
114
|
}
|
|
115
|
+
/**
|
|
116
|
+
* 声明验签后的回调映射,HTTP 适配器校验载荷后以 external 身份执行目标命令。
|
|
117
|
+
* 事件名决定自动路由;这里不注册服务器,也不因签名通过而绕过命令状态、版本或业务约束。
|
|
118
|
+
*/
|
|
102
119
|
export declare function inbound<P extends z.ZodType>(conn: Connector<any>, eventName: string, def: {
|
|
103
120
|
verify: Verifier;
|
|
104
121
|
payload: P;
|
package/dist/primitives.js
CHANGED
|
@@ -2,14 +2,25 @@
|
|
|
2
2
|
import { createHmac, timingSafeEqual } from 'node:crypto';
|
|
3
3
|
import { CONNECTOR, CONSUMERS, INBOUND, OUTBOUND, declError, } from '@yss123/cage-kernel';
|
|
4
4
|
import { outboundConsumer } from './runtime.js';
|
|
5
|
+
/** 保存操作规格并检查绝对路径形式;此时没有连接器环境/密钥,也不会发出请求。 */
|
|
5
6
|
export function op(def) {
|
|
6
7
|
if (!def.path.startsWith('/'))
|
|
7
8
|
throw declError('CAGE-CONN-002', `操作路径 ${def.path} 必须以 / 开头`);
|
|
8
9
|
return def;
|
|
9
10
|
}
|
|
11
|
+
/**
|
|
12
|
+
* 校验名称、超时、限流与主机形式,冻结操作并回链到所属连接器。
|
|
13
|
+
* hosts 不含路径/协议,运行时按阶段选择;构造不读取密钥或访问网络,便于静态抽取与离线检查。
|
|
14
|
+
*/
|
|
10
15
|
export function connector(name, def) {
|
|
11
16
|
if (!/^[a-z][a-z0-9-]*$/.test(name))
|
|
12
17
|
throw declError('CAGE-CONN-001', `连接器名 ${name} 必须是小写短横线`);
|
|
18
|
+
if (def.timeoutMs !== undefined && (!Number.isSafeInteger(def.timeoutMs) || def.timeoutMs <= 0 || def.timeoutMs > 300_000)) {
|
|
19
|
+
throw declError('CAGE-CONN-015', '连接器 timeoutMs 必须是 1~300000 毫秒的整数');
|
|
20
|
+
}
|
|
21
|
+
if (def.rateLimit && (!Number.isFinite(def.rateLimit.perSecond) || def.rateLimit.perSecond <= 0)) {
|
|
22
|
+
throw declError('CAGE-CONN-015', '连接器 rateLimit.perSecond 必须为有限正数');
|
|
23
|
+
}
|
|
13
24
|
for (const h of Object.values(def.hosts)) {
|
|
14
25
|
if (h && /[/:\s]/.test(h.replace(/:\d+$/, '')))
|
|
15
26
|
throw declError('CAGE-CONN-003', `地址白名单只能写主机名(可带端口):${h}`);
|
|
@@ -26,6 +37,10 @@ export function connector(name, def) {
|
|
|
26
37
|
c.operations = Object.freeze(Object.fromEntries(Object.entries(def.operations).map(([k, o]) => [k, Object.freeze({ ...o, name: k, connector: c })])));
|
|
27
38
|
return Object.freeze(c);
|
|
28
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* 把领域事件映射为事务外写操作;只读操作不允许挂出站,成功回写必须指向领域命令。
|
|
42
|
+
* map 返回 null 可跳过当前事件,幂等键需要跨重投稳定;消费器由 CONSUMERS 在 worker 装配。
|
|
43
|
+
*/
|
|
29
44
|
export function outbound(ev, operation, def) {
|
|
30
45
|
if (operation.effect === 'read')
|
|
31
46
|
throw declError('CAGE-CONN-004', '只读操作不能用于出站映射');
|
|
@@ -42,6 +57,7 @@ export function outbound(ev, operation, def) {
|
|
|
42
57
|
return Object.freeze(o);
|
|
43
58
|
}
|
|
44
59
|
/** HMAC-SHA256 验签:签名放在请求头(默认 x-signature),值为 hex 或 sha256=hex */
|
|
60
|
+
/** 签名基于原始正文计算,接受 hex 或 sha256=hex;长度校验后恒定时间比较,不能对解析后 JSON 重算。 */
|
|
45
61
|
export function hmacSha256(secret, opts = {}) {
|
|
46
62
|
const header = (opts.header ?? 'x-signature').toLowerCase();
|
|
47
63
|
return Object.freeze({
|
|
@@ -56,6 +72,10 @@ export function hmacSha256(secret, opts = {}) {
|
|
|
56
72
|
},
|
|
57
73
|
});
|
|
58
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* 声明验签后的回调映射,HTTP 适配器校验载荷后以 external 身份执行目标命令。
|
|
77
|
+
* 事件名决定自动路由;这里不注册服务器,也不因签名通过而绕过命令状态、版本或业务约束。
|
|
78
|
+
*/
|
|
59
79
|
export function inbound(conn, eventName, def) {
|
|
60
80
|
if (!/^[a-z][A-Za-z0-9]*$/.test(eventName))
|
|
61
81
|
throw declError('CAGE-CONN-005', `入站事件名 ${eventName} 必须是小驼峰`);
|
package/dist/primitives.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"primitives.js","sourceRoot":"","sources":["../src/primitives.ts"],"names":[],"mappings":"AAAA,iDAAiD;AACjD,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,EACL,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,GACnD,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"primitives.js","sourceRoot":"","sources":["../src/primitives.ts"],"names":[],"mappings":"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"}
|
package/dist/runtime.d.ts
CHANGED
|
@@ -4,15 +4,25 @@ import { z } from 'zod';
|
|
|
4
4
|
import type { Connector, Op, Outbound } from './primitives.js';
|
|
5
5
|
/** 运行阶段:fake(本地与 CI,不访问网络)、test(第三方沙箱)、prod */
|
|
6
6
|
export declare function stage(): string;
|
|
7
|
+
/** 运行时秘密读取接口;实现可接环境变量或密钥服务,调用者不得把返回值写入日志。 */
|
|
7
8
|
export interface SecretProvider {
|
|
8
9
|
get(name: string): Promise<string>;
|
|
9
10
|
}
|
|
11
|
+
/** 替换进程级秘密提供器;动态多租户模型应使用单次 credential 回调,避免改变全局配置串用凭据。 */
|
|
10
12
|
export declare function setSecretProvider(p: SecretProvider): void;
|
|
11
13
|
export declare const secrets: {
|
|
12
14
|
get: (name: string) => Promise<string>;
|
|
13
15
|
};
|
|
14
|
-
|
|
15
|
-
|
|
16
|
+
/**
|
|
17
|
+
* 按连接器名维护单进程令牌桶,等待时间算入上层超时并支持取消。
|
|
18
|
+
* perSecond 可小于 1,但桶至少容纳一次请求;此预算不协调多个 API/worker 进程。
|
|
19
|
+
*/
|
|
20
|
+
export declare function take(c: Connector<any>, signal?: AbortSignal): Promise<void>;
|
|
21
|
+
type FakeHandler = (req: any, context: {
|
|
22
|
+
key: string;
|
|
23
|
+
signal: AbortSignal;
|
|
24
|
+
}) => unknown | Promise<unknown>;
|
|
25
|
+
/** fake 阶段的内存调用轨迹,供测试断言;不是生产录制平台,测试需要自行管理累计记录。 */
|
|
16
26
|
export declare const fakeCalls: {
|
|
17
27
|
connector: string;
|
|
18
28
|
operation: string;
|
|
@@ -23,16 +33,29 @@ export declare const fakeCalls: {
|
|
|
23
33
|
export declare function fakeConnector(c: Connector<any>, handlers: Record<string, FakeHandler>): void;
|
|
24
34
|
/** 没有注册 fake 时按响应 schema 造一个示例值 */
|
|
25
35
|
export declare function sampleFromSchema(schema: z.ZodType, seed?: string): unknown;
|
|
36
|
+
/**
|
|
37
|
+
* retryable 表示协议允许自动再试;outcome 区分已知拒绝和远端结果未知,二者不可互相推断。
|
|
38
|
+
* status=null 表示未取得 HTTP 状态,providerCode 仅携带已筛选稳定错误码,不存原始响应正文。
|
|
39
|
+
*/
|
|
26
40
|
export declare class ConnectorError extends Error {
|
|
27
41
|
readonly status: number | null;
|
|
28
42
|
readonly retryable: boolean;
|
|
29
|
-
|
|
43
|
+
readonly outcome: 'rejected' | 'unknown';
|
|
44
|
+
readonly providerCode?: string | undefined;
|
|
45
|
+
constructor(message: string, status: number | null, retryable: boolean, outcome?: 'rejected' | 'unknown', providerCode?: string | undefined);
|
|
30
46
|
}
|
|
31
47
|
/** 调用一个连接器操作。白名单外的字段直接报错,不会被悄悄发出去。 */
|
|
48
|
+
/**
|
|
49
|
+
* 先严格校验请求白名单,再按阶段走 fake 或真实主机;真实请求携带同一个幂等键和按需密钥。
|
|
50
|
+
* 总超时包含令牌等待、请求和响应读取;响应必须符合 schema 才交付,失败归一为 ConnectorError。
|
|
51
|
+
* 本函数只做一次调用,不自行重试;外部副作用能否重试由 effect 与 outbox 消费策略共同决定。
|
|
52
|
+
*/
|
|
32
53
|
export declare function call(o: Op, payload: unknown, key: string, opts?: {
|
|
33
54
|
kernel?: Kernel;
|
|
34
55
|
tenantId?: string;
|
|
56
|
+
signal?: AbortSignal;
|
|
35
57
|
}): Promise<any>;
|
|
36
|
-
/**
|
|
58
|
+
/** 出站网络执行与短事务回写分离;回写命令与 processed_event 去重登记共同提交。 */
|
|
59
|
+
/** prepare 完成网络/回执,handle 仅做短事务命令回写;不可重试异常明确标永久错误进入死信。 */
|
|
37
60
|
export declare function outboundConsumer(k: Kernel, ob: Outbound): EventConsumer;
|
|
38
61
|
export {};
|
package/dist/runtime.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// 连接器运行时:每一道管控都在这里发生——字段白名单、地址白名单、密钥、限流、超时、调用记录、响应校验。
|
|
2
2
|
import { systemCtx } from '@yss123/cage-kernel';
|
|
3
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
4
|
+
import { setTimeout as delay } from 'node:timers/promises';
|
|
3
5
|
import { sql } from 'kysely';
|
|
4
6
|
import { z } from 'zod';
|
|
5
7
|
/** 运行阶段:fake(本地与 CI,不访问网络)、test(第三方沙箱)、prod */
|
|
@@ -14,20 +16,27 @@ let secretProvider = {
|
|
|
14
16
|
return v;
|
|
15
17
|
},
|
|
16
18
|
};
|
|
19
|
+
/** 替换进程级秘密提供器;动态多租户模型应使用单次 credential 回调,避免改变全局配置串用凭据。 */
|
|
17
20
|
export function setSecretProvider(p) {
|
|
18
21
|
secretProvider = p;
|
|
19
22
|
}
|
|
20
23
|
export const secrets = { get: (name) => secretProvider.get(name) };
|
|
21
24
|
// ---------------- 限流 ----------------
|
|
22
25
|
const buckets = new Map();
|
|
23
|
-
|
|
26
|
+
/**
|
|
27
|
+
* 按连接器名维护单进程令牌桶,等待时间算入上层超时并支持取消。
|
|
28
|
+
* perSecond 可小于 1,但桶至少容纳一次请求;此预算不协调多个 API/worker 进程。
|
|
29
|
+
*/
|
|
30
|
+
export async function take(c, signal) {
|
|
24
31
|
const rate = c.rateLimit?.perSecond;
|
|
25
32
|
if (!rate)
|
|
26
33
|
return;
|
|
34
|
+
const capacity = Math.max(1, rate); // 低于每秒一次时仍需容纳一个完整令牌。
|
|
27
35
|
for (;;) {
|
|
36
|
+
signal?.throwIfAborted();
|
|
28
37
|
const now = Date.now();
|
|
29
|
-
const b = buckets.get(c.name) ?? { tokens:
|
|
30
|
-
b.tokens = Math.min(
|
|
38
|
+
const b = buckets.get(c.name) ?? { tokens: capacity, at: now };
|
|
39
|
+
b.tokens = Math.min(capacity, b.tokens + ((now - b.at) / 1000) * rate);
|
|
31
40
|
b.at = now;
|
|
32
41
|
if (b.tokens >= 1) {
|
|
33
42
|
b.tokens -= 1;
|
|
@@ -35,10 +44,11 @@ export async function take(c) {
|
|
|
35
44
|
return;
|
|
36
45
|
}
|
|
37
46
|
buckets.set(c.name, b);
|
|
38
|
-
await
|
|
47
|
+
await delay(Math.ceil(((1 - b.tokens) / rate) * 1000), undefined, { signal });
|
|
39
48
|
}
|
|
40
49
|
}
|
|
41
50
|
const fakes = new Map();
|
|
51
|
+
/** fake 阶段的内存调用轨迹,供测试断言;不是生产录制平台,测试需要自行管理累计记录。 */
|
|
42
52
|
export const fakeCalls = [];
|
|
43
53
|
/** 注册 fake 实现:fakeConnector(erp, { pushOrder: (req) => ({ erpNo: 'FAKE-' + req.externalId }) }) */
|
|
44
54
|
export function fakeConnector(c, handlers) {
|
|
@@ -76,16 +86,25 @@ export function sampleFromSchema(schema, seed = 'fake') {
|
|
|
76
86
|
return gen(js);
|
|
77
87
|
}
|
|
78
88
|
// ---------------- 调用 ----------------
|
|
89
|
+
/**
|
|
90
|
+
* retryable 表示协议允许自动再试;outcome 区分已知拒绝和远端结果未知,二者不可互相推断。
|
|
91
|
+
* status=null 表示未取得 HTTP 状态,providerCode 仅携带已筛选稳定错误码,不存原始响应正文。
|
|
92
|
+
*/
|
|
79
93
|
export class ConnectorError extends Error {
|
|
80
94
|
status;
|
|
81
95
|
retryable;
|
|
82
|
-
|
|
96
|
+
outcome;
|
|
97
|
+
providerCode;
|
|
98
|
+
constructor(message, status, retryable, outcome = 'rejected', providerCode) {
|
|
83
99
|
super(message);
|
|
84
100
|
this.status = status;
|
|
85
101
|
this.retryable = retryable;
|
|
102
|
+
this.outcome = outcome;
|
|
103
|
+
this.providerCode = providerCode;
|
|
86
104
|
this.name = 'ConnectorError';
|
|
87
105
|
}
|
|
88
106
|
}
|
|
107
|
+
/** 同步记录供运维查看,失败仅告警;可靠成功回执另存 connector_execution,不能依赖本日志恢复去重。 */
|
|
89
108
|
async function recordCall(k, o, key, tenantId, status, httpStatus, request, response, error) {
|
|
90
109
|
if (!k)
|
|
91
110
|
return;
|
|
@@ -95,87 +114,179 @@ async function recordCall(k, o, key, tenantId, status, httpStatus, request, resp
|
|
|
95
114
|
tenant_id: tenantId, connector: o.connector.name, operation: o.name, idempotency_key: key, ref_id: key, status,
|
|
96
115
|
http_status: httpStatus, request: JSON.stringify(request), response: response === undefined ? null : JSON.stringify(response), error: error ?? null,
|
|
97
116
|
})
|
|
98
|
-
.onConflict((oc) => oc.columns(['connector', 'operation', 'idempotency_key']).doUpdateSet({
|
|
117
|
+
.onConflict((oc) => oc.columns(['tenant_id', 'connector', 'operation', 'idempotency_key']).doUpdateSet({
|
|
99
118
|
status, http_status: httpStatus, attempts: sql `sys.sync_record.attempts + 1`, response: response === undefined ? null : JSON.stringify(response),
|
|
100
119
|
error: error ?? null, updated_at: sql `now()`,
|
|
101
120
|
}))
|
|
102
121
|
.execute()
|
|
103
122
|
.catch((e) => k.log.warn({ err: String(e) }, '同步记录写入失败'));
|
|
104
123
|
}
|
|
124
|
+
/** 将取消与实际 Promise 竞争,确保 fake 也遵守调用超时;底层工作仍应主动监听 signal 释放资源。 */
|
|
125
|
+
async function abortable(signal, run) {
|
|
126
|
+
signal.throwIfAborted();
|
|
127
|
+
let onAbort = () => { };
|
|
128
|
+
const aborted = new Promise((_resolve, reject) => {
|
|
129
|
+
onAbort = () => reject(signal.reason);
|
|
130
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
131
|
+
});
|
|
132
|
+
try {
|
|
133
|
+
return await Promise.race([run(), aborted]);
|
|
134
|
+
}
|
|
135
|
+
finally {
|
|
136
|
+
signal.removeEventListener('abort', onAbort);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
105
139
|
/** 调用一个连接器操作。白名单外的字段直接报错,不会被悄悄发出去。 */
|
|
140
|
+
/**
|
|
141
|
+
* 先严格校验请求白名单,再按阶段走 fake 或真实主机;真实请求携带同一个幂等键和按需密钥。
|
|
142
|
+
* 总超时包含令牌等待、请求和响应读取;响应必须符合 schema 才交付,失败归一为 ConnectorError。
|
|
143
|
+
* 本函数只做一次调用,不自行重试;外部副作用能否重试由 effect 与 outbox 消费策略共同决定。
|
|
144
|
+
*/
|
|
106
145
|
export async function call(o, payload, key, opts = {}) {
|
|
107
146
|
const body = o.request.strict().parse(payload);
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
const res = h ? await h(body) : sampleFromSchema(o.response, o.connector.name);
|
|
112
|
-
fakeCalls.push({ connector: o.connector.name, operation: o.name, request: body, key });
|
|
113
|
-
const parsed = o.response.parse(res);
|
|
114
|
-
await recordCall(opts.kernel, o, key, opts.tenantId ?? null, 'ok', 200, body, parsed);
|
|
115
|
-
return parsed;
|
|
116
|
-
}
|
|
117
|
-
const host = o.connector.hosts[st];
|
|
118
|
-
if (!host)
|
|
119
|
-
throw new ConnectorError(`[CAGE-CONN-007] 连接器 ${o.connector.name} 没有 ${st} 环境的地址`, null, false);
|
|
120
|
-
const token = o.connector.secret ? await secrets.get(o.connector.secret) : undefined;
|
|
121
|
-
await take(o.connector);
|
|
122
|
-
const url = new URL(o.path, `https://${host}`);
|
|
123
|
-
if (o.method === 'GET')
|
|
124
|
-
for (const [k, v] of Object.entries(body))
|
|
125
|
-
url.searchParams.set(k, String(v));
|
|
126
|
-
let res;
|
|
147
|
+
const timeout = AbortSignal.timeout(o.connector.timeoutMs);
|
|
148
|
+
const signal = opts.signal ? AbortSignal.any([opts.signal, timeout]) : timeout;
|
|
149
|
+
let httpStatus = null;
|
|
127
150
|
try {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
151
|
+
const parsed = await abortable(signal, async () => {
|
|
152
|
+
const st = stage();
|
|
153
|
+
if (st === 'fake') {
|
|
154
|
+
const h = fakes.get(`${o.connector.name}.${o.name}`);
|
|
155
|
+
fakeCalls.push({ connector: o.connector.name, operation: o.name, request: body, key });
|
|
156
|
+
const response = h ? await h(body, { key, signal }) : sampleFromSchema(o.response, o.connector.name);
|
|
157
|
+
httpStatus = 200;
|
|
158
|
+
return o.response.parse(response);
|
|
159
|
+
}
|
|
160
|
+
const host = o.connector.hosts[st];
|
|
161
|
+
if (!host)
|
|
162
|
+
throw new ConnectorError(`[CAGE-CONN-007] 连接器 ${o.connector.name} 没有 ${st} 环境的地址`, null, false);
|
|
163
|
+
const token = o.connector.secret ? await secrets.get(o.connector.secret) : undefined;
|
|
164
|
+
await take(o.connector, signal);
|
|
165
|
+
const url = new URL(o.path, `https://${host}`);
|
|
166
|
+
if (o.method === 'GET')
|
|
167
|
+
for (const [field, value] of Object.entries(body))
|
|
168
|
+
url.searchParams.set(field, String(value));
|
|
169
|
+
// 超时覆盖限流等待、请求和响应体读取,关闭 worker 同样能取消在途网络 IO。
|
|
170
|
+
const res = await fetch(url, {
|
|
171
|
+
method: o.method,
|
|
172
|
+
headers: { 'content-type': 'application/json', ...(token ? { authorization: `Bearer ${token}` } : {}), 'idempotency-key': key },
|
|
173
|
+
body: o.method === 'GET' ? undefined : JSON.stringify(body), signal,
|
|
174
|
+
});
|
|
175
|
+
httpStatus = res.status;
|
|
176
|
+
const text = await res.text();
|
|
177
|
+
let json = null;
|
|
178
|
+
try {
|
|
179
|
+
json = text ? JSON.parse(text) : null;
|
|
180
|
+
}
|
|
181
|
+
catch { /* 无效响应由下方 schema 统一诊断,不记录原文。 */ }
|
|
182
|
+
if (!res.ok)
|
|
183
|
+
throw new ConnectorError(`[CAGE-CONN-009] ${o.connector.name}.${o.name} 返回 ${res.status}`, res.status, o.effect !== 'write' && (res.status >= 500 || res.status === 429), res.status >= 500 ? 'unknown' : 'rejected');
|
|
184
|
+
const result = o.response.safeParse(json);
|
|
185
|
+
if (!result.success)
|
|
186
|
+
throw new ConnectorError(`[CAGE-CONN-010] ${o.connector.name}.${o.name} 的响应格式与声明不符`, res.status, false, 'unknown');
|
|
187
|
+
return result.data;
|
|
138
188
|
});
|
|
189
|
+
await recordCall(opts.kernel, o, key, opts.tenantId ?? null, 'ok', httpStatus, body, parsed);
|
|
190
|
+
return parsed;
|
|
139
191
|
}
|
|
140
192
|
catch (e) {
|
|
141
|
-
|
|
142
|
-
|
|
193
|
+
const err = e instanceof ConnectorError ? e : new ConnectorError(`[CAGE-CONN-008] 调用 ${o.connector.name}.${o.name} 失败:${e?.name ?? 'Error'}`, httpStatus, o.effect !== 'write', 'unknown');
|
|
194
|
+
await recordCall(opts.kernel, o, key, opts.tenantId ?? null, 'failed', httpStatus, body, undefined, err.message);
|
|
195
|
+
throw err;
|
|
143
196
|
}
|
|
144
|
-
|
|
145
|
-
|
|
197
|
+
}
|
|
198
|
+
/** 对对象键排序、保留数组次序以生成请求摘要,拒绝同一业务幂等键被不同请求内容复用。 */
|
|
199
|
+
function canonical(value) {
|
|
200
|
+
if (value === null || typeof value !== 'object')
|
|
201
|
+
return JSON.stringify(value);
|
|
202
|
+
if (Array.isArray(value))
|
|
203
|
+
return `[${value.map(canonical).join(',')}]`;
|
|
204
|
+
return `{${Object.keys(value).sort().filter((key) => value[key] !== undefined).map((key) => `${JSON.stringify(key)}:${canonical(value[key])}`).join(',')}}`;
|
|
205
|
+
}
|
|
206
|
+
/** 先持久化意图,释放连接后才外呼;成功回执先提交,后续本地回写失败不再外呼。 */
|
|
207
|
+
/**
|
|
208
|
+
* 短事务内声明执行意图并领取租约:摘要不符拒绝,已有成功直接复用回执,unknown 隔离禁止重发。
|
|
209
|
+
* 不可重试写的过期 calling 租约也转 unknown,因为进程中断不能证明远端未执行。
|
|
210
|
+
* 释放数据库连接后外呼,成功用租约 token 条件保存回执;后续本地回写失败可重放回执而无需重复外呼。
|
|
211
|
+
* 远端成功但回执未落库的窗口仍依赖远端幂等协议,不能宣称任意外部系统恰好一次。
|
|
212
|
+
*/
|
|
213
|
+
async function prepareOutbound(k, ob, row, consumer, signal) {
|
|
214
|
+
const mapped = ob.map(row.payload);
|
|
215
|
+
if (mapped === null || mapped === undefined)
|
|
216
|
+
return { skipped: true };
|
|
217
|
+
const body = ob.op.request.strict().parse(mapped);
|
|
218
|
+
const key = ob.idempotencyKey(row.payload);
|
|
219
|
+
if (!key || typeof key !== 'string')
|
|
220
|
+
throw new ConnectorError('[CAGE-CONN-013] 出站幂等键必须为非空字符串', null, false);
|
|
221
|
+
const requestHash = createHash('sha256').update(canonical(body)).digest('hex');
|
|
222
|
+
const identity = { tenant_id: row.tenant_id, connector: ob.op.connector.name, operation: ob.op.name, idempotency_key: key };
|
|
223
|
+
const locate = (db) => db.selectFrom('sys.connector_execution').selectAll()
|
|
224
|
+
.where('tenant_id', '=', identity.tenant_id).where('connector', '=', identity.connector)
|
|
225
|
+
.where('operation', '=', identity.operation).where('idempotency_key', '=', identity.idempotency_key);
|
|
226
|
+
const token = randomUUID();
|
|
227
|
+
const claim = await k.app.transaction().execute(async (tx) => {
|
|
228
|
+
await tx.insertInto('sys.connector_execution').values({ ...identity, request_hash: requestHash, status: 'planned', outbox_id: row.id, consumer })
|
|
229
|
+
.onConflict((oc) => oc.columns(['tenant_id', 'connector', 'operation', 'idempotency_key']).doNothing()).execute();
|
|
230
|
+
const previous = await locate(tx).forUpdate().executeTakeFirstOrThrow();
|
|
231
|
+
if (previous.request_hash !== requestHash)
|
|
232
|
+
return { kind: 'conflict' };
|
|
233
|
+
if (previous.status === 'succeeded')
|
|
234
|
+
return { kind: 'receipt', response: previous.response };
|
|
235
|
+
if (previous.status === 'unknown')
|
|
236
|
+
return { kind: 'unknown' };
|
|
237
|
+
if (previous.status === 'calling' && previous.lease_until && new Date(previous.lease_until).getTime() > Date.now())
|
|
238
|
+
return { kind: 'busy' };
|
|
239
|
+
if (previous.status === 'calling' && ob.op.effect === 'write') {
|
|
240
|
+
await tx.updateTable('sys.connector_execution').set({ status: 'unknown', error: '执行租约中断,远端结果需人工核对', updated_at: sql `now()` })
|
|
241
|
+
.where('tenant_id', '=', identity.tenant_id).where('connector', '=', identity.connector).where('operation', '=', identity.operation).where('idempotency_key', '=', key).execute();
|
|
242
|
+
return { kind: 'unknown' };
|
|
243
|
+
}
|
|
244
|
+
await tx.updateTable('sys.connector_execution')
|
|
245
|
+
.set({ status: 'calling', lease_token: token, lease_until: new Date(Date.now() + ob.op.connector.timeoutMs + 30_000), attempts: sql `attempts + 1`, error: null, updated_at: sql `now()` })
|
|
246
|
+
.where('tenant_id', '=', identity.tenant_id).where('connector', '=', identity.connector).where('operation', '=', identity.operation).where('idempotency_key', '=', key).execute();
|
|
247
|
+
return { kind: 'claimed' };
|
|
248
|
+
});
|
|
249
|
+
if (claim.kind === 'conflict')
|
|
250
|
+
throw new ConnectorError(`[CAGE-CONN-013] ${consumer} 同一幂等键对应不同请求;禁止覆盖已有执行`, null, false);
|
|
251
|
+
if (claim.kind === 'unknown')
|
|
252
|
+
throw new ConnectorError(`[CAGE-CONN-014] ${consumer} 的不可重试调用结果未知;人工核对远端后再恢复`, null, false, 'unknown');
|
|
253
|
+
if (claim.kind === 'busy')
|
|
254
|
+
throw new ConnectorError(`${consumer} 的调用仍由其他执行者持有`, null, true);
|
|
255
|
+
if (claim.kind === 'receipt')
|
|
256
|
+
return { skipped: false, response: ob.op.response.parse(claim.response) };
|
|
257
|
+
let response;
|
|
146
258
|
try {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
catch {
|
|
150
|
-
json = { raw: text.slice(0, 500) };
|
|
151
|
-
}
|
|
152
|
-
if (!res.ok) {
|
|
153
|
-
await recordCall(opts.kernel, o, key, opts.tenantId ?? null, 'failed', res.status, body, json);
|
|
154
|
-
const retryable = o.effect === 'retryableWrite' || o.effect === 'read' ? res.status >= 500 || res.status === 429 : false;
|
|
155
|
-
throw new ConnectorError(`[CAGE-CONN-009] ${o.connector.name}.${o.name} 返回 ${res.status}`, res.status, retryable);
|
|
259
|
+
// 回调声明的业务键可能只在租户内唯一;外发键补租户命名空间,重试始终复用。
|
|
260
|
+
response = await call(ob.op, body, `${row.tenant_id}:${key}`, { signal });
|
|
156
261
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
262
|
+
catch (e) {
|
|
263
|
+
const err = e;
|
|
264
|
+
const unknown = ob.op.effect === 'write' && err.outcome === 'unknown';
|
|
265
|
+
await k.app.updateTable('sys.connector_execution')
|
|
266
|
+
.set({ status: unknown ? 'unknown' : 'failed', error: String(err.message), lease_token: null, lease_until: null, updated_at: sql `now()` })
|
|
267
|
+
.where('tenant_id', '=', identity.tenant_id).where('connector', '=', identity.connector).where('operation', '=', identity.operation).where('idempotency_key', '=', key).where('lease_token', '=', token).execute();
|
|
268
|
+
await recordCall(k, ob.op, key, row.tenant_id, 'failed', err.status, body, undefined, err.message);
|
|
269
|
+
throw err;
|
|
161
270
|
}
|
|
162
|
-
|
|
163
|
-
|
|
271
|
+
// 此处崩溃仍只能依靠远端幂等协议。不可重试写的租约超期会进入 unknown,不盲目重发。
|
|
272
|
+
const saved = await k.app.updateTable('sys.connector_execution')
|
|
273
|
+
.set({ status: 'succeeded', response: JSON.stringify(response), error: null, lease_token: null, lease_until: null, updated_at: sql `now()` })
|
|
274
|
+
.where('tenant_id', '=', identity.tenant_id).where('connector', '=', identity.connector).where('operation', '=', identity.operation).where('idempotency_key', '=', key).where('lease_token', '=', token).executeTakeFirst();
|
|
275
|
+
if (Number(saved.numUpdatedRows) !== 1)
|
|
276
|
+
throw new ConnectorError(`${consumer} 执行租约已失效,未提交本地回写`, null, true, 'unknown');
|
|
277
|
+
await recordCall(k, ob.op, key, row.tenant_id, 'ok', 200, body, response);
|
|
278
|
+
return { skipped: false, response };
|
|
164
279
|
}
|
|
165
|
-
/**
|
|
280
|
+
/** 出站网络执行与短事务回写分离;回写命令与 processed_event 去重登记共同提交。 */
|
|
281
|
+
/** prepare 完成网络/回执,handle 仅做短事务命令回写;不可重试异常明确标永久错误进入死信。 */
|
|
166
282
|
export function outboundConsumer(k, ob) {
|
|
167
283
|
const name = `outbound:${ob.op.connector.name}.${ob.op.name}:${ob.event.name}`;
|
|
168
284
|
return {
|
|
169
285
|
name,
|
|
170
286
|
event: ob.event.name,
|
|
171
|
-
async
|
|
172
|
-
const req = ob.map(row.payload);
|
|
173
|
-
if (req === null || req === undefined)
|
|
174
|
-
return;
|
|
175
|
-
const key = ob.idempotencyKey(row.payload);
|
|
176
|
-
let res;
|
|
287
|
+
async prepare(row, _kernel, { signal }) {
|
|
177
288
|
try {
|
|
178
|
-
|
|
289
|
+
return await prepareOutbound(k, ob, row, name, signal);
|
|
179
290
|
}
|
|
180
291
|
catch (e) {
|
|
181
292
|
if (e instanceof ConnectorError && !e.retryable) {
|
|
@@ -184,8 +295,15 @@ export function outboundConsumer(k, ob) {
|
|
|
184
295
|
}
|
|
185
296
|
throw e;
|
|
186
297
|
}
|
|
298
|
+
},
|
|
299
|
+
async handle(row, tx, _kernel, prepared) {
|
|
300
|
+
if (!prepared)
|
|
301
|
+
throw new Error('[CAGE-WORKER-002] 出站消费者必须经 worker prepare 阶段执行');
|
|
302
|
+
const result = prepared;
|
|
303
|
+
if (result.skipped)
|
|
304
|
+
return;
|
|
187
305
|
if (ob.onSuccess) {
|
|
188
|
-
const input = ob.onSuccess.map(
|
|
306
|
+
const input = ob.onSuccess.map(result.response, row.payload);
|
|
189
307
|
const ctx = systemCtx({ kind: 'external', name: ob.op.connector.name, tenantId: row.tenant_id, capabilities: [k.registry.capabilityOf(ob.onSuccess.command)], requestId: row.request_id ?? undefined });
|
|
190
308
|
await k.executeIn(tx, ob.onSuccess.command, input, ctx, 0);
|
|
191
309
|
}
|
package/dist/runtime.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA,sDAAsD;AACtD,OAAO,EAAE,SAAS,EAAe,MAAM,qBAAqB,CAAC;AAE7D,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,UAAU,IAAI,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,GAAG,EAAE,MAAM,QAAQ,CAAC;AAC7B,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,+CAA+C;AAC/C,MAAM,UAAU,KAAK;IACnB,OAAO,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;AAC7F,CAAC;AASD,IAAI,cAAc,GAAmB;IACnC,KAAK,CAAC,GAAG,CAAC,IAAI;QACZ,MAAM,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC5B,IAAI,CAAC,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,IAAI,kBAAkB,CAAC,CAAC;QAC1E,OAAO,CAAC,CAAC;IACX,CAAC;CACF,CAAC;AAEF,0DAA0D;AAC1D,MAAM,UAAU,iBAAiB,CAAC,CAAiB;IACjD,cAAc,GAAG,CAAC,CAAC;AACrB,CAAC;AACD,MAAM,CAAC,MAAM,OAAO,GAAG,EAAE,GAAG,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;AAE3E,uCAAuC;AAEvC,MAAM,OAAO,GAAG,IAAI,GAAG,EAA0C,CAAC;AAClE;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,IAAI,CAAC,CAAiB,EAAE,MAAoB;IAChE,MAAM,IAAI,GAAG,CAAC,CAAC,SAAS,EAAE,SAAS,CAAC;IACpC,IAAI,CAAC,IAAI;QAAE,OAAO;IAClB,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,qBAAqB;IACzD,SAAS,CAAC;QACR,MAAM,EAAE,cAAc,EAAE,CAAC;QACzB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC;QAC/D,CAAC,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;QACvE,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC;QACX,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;YAClB,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC;YACd,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;YACvB,OAAO;QACT,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QACvB,MAAM,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,EAAE,SAAS,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IAChF,CAAC;AACH,CAAC;AAKD,MAAM,KAAK,GAAG,IAAI,GAAG,EAAuB,CAAC;AAC7C,kDAAkD;AAClD,MAAM,CAAC,MAAM,SAAS,GAA8E,EAAE,CAAC;AAEvG,mGAAmG;AACnG,MAAM,UAAU,aAAa,CAAC,CAAiB,EAAE,QAAqC;IACpF,KAAK,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC;QAAE,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;AAClF,CAAC;AAED,mCAAmC;AACnC,MAAM,UAAU,gBAAgB,CAAC,MAAiB,EAAE,IAAI,GAAG,MAAM;IAC/D,MAAM,EAAE,GAAQ,CAAC,CAAC,YAAY,CAAC,MAAM,EAAE,EAAE,eAAe,EAAE,KAAK,EAAE,CAAC,CAAC;IACnE,MAAM,GAAG,GAAG,CAAC,CAAM,EAAW,EAAE;QAC9B,IAAI,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC;QACpB,IAAI,CAAC,CAAC,KAAK,KAAK,SAAS;YAAE,OAAO,CAAC,CAAC,KAAK,CAAC;QAC1C,IAAI,CAAC,CAAC,IAAI;YAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC7B,IAAI,CAAC,CAAC,KAAK;YAAE,OAAO,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QACpC,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC;YACf,KAAK,QAAQ;gBACX,OAAO,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC7F,KAAK,OAAO;gBACV,OAAO,EAAE,CAAC;YACZ,KAAK,QAAQ;gBACX,OAAO,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,sCAAsC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,WAAW,EAAE,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;YAC3H,KAAK,SAAS,CAAC;YACf,KAAK,QAAQ;gBACX,OAAO,CAAC,CAAC;YACX,KAAK,SAAS;gBACZ,OAAO,KAAK,CAAC;YACf;gBACE,OAAO,IAAI,CAAC;QAChB,CAAC;IACH,CAAC,CAAC;IACF,OAAO,GAAG,CAAC,EAAE,CAAC,CAAC;AACjB,CAAC;AAED,uCAAuC;AAEvC;;;GAGG;AACH,MAAM,OAAO,cAAe,SAAQ,KAAK;IACD;IAAgC;IAA6B;IAAuD;IAA1J,YAAY,OAAe,EAAW,MAAqB,EAAW,SAAkB,EAAW,UAAkC,UAAU,EAAW,YAAqB;QAC7K,KAAK,CAAC,OAAO,CAAC,CAAC;QADqB,WAAM,GAAN,MAAM,CAAe;QAAW,cAAS,GAAT,SAAS,CAAS;QAAW,YAAO,GAAP,OAAO,CAAqC;QAAW,iBAAY,GAAZ,YAAY,CAAS;QAE7K,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;IAC/B,CAAC;CACF;AAED,gEAAgE;AAChE,KAAK,UAAU,UAAU,CAAC,CAAqB,EAAE,CAAK,EAAE,GAAW,EAAE,QAAuB,EAAE,MAAuB,EAAE,UAAyB,EAAE,OAAgB,EAAE,QAAiB,EAAE,KAAc;IACnM,IAAI,CAAC,CAAC;QAAE,OAAO;IACf,MAAM,CAAC,CAAC,GAAG;SACR,UAAU,CAAC,iBAAiB,CAAC;SAC7B,MAAM,CAAC;QACN,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,CAAC,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC,IAAI,EAAE,eAAe,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM;QAC9G,WAAW,EAAE,UAAU,EAAE,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,QAAQ,EAAE,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,KAAK,IAAI,IAAI;KACpJ,CAAC;SACD,UAAU,CAAC,CAAC,EAAE,EAAE,EAAE,CACjB,EAAE,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,iBAAiB,CAAC,CAAC,CAAC,WAAW,CAAC;QACjF,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,CAAA,8BAA8B,EAAE,QAAQ,EAAE,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC;QAChJ,KAAK,EAAE,KAAK,IAAI,IAAI,EAAE,UAAU,EAAE,GAAG,CAAA,OAAO;KAC7C,CAAC,CACH;SACA,OAAO,EAAE;SACT,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC,CAAC;AAC9D,CAAC;AAED,gEAAgE;AAChE,KAAK,UAAU,SAAS,CAAI,MAAmB,EAAE,GAAqB;IACpE,MAAM,CAAC,cAAc,EAAE,CAAC;IACxB,IAAI,OAAO,GAAe,GAAG,EAAE,GAAE,CAAC,CAAC;IACnC,MAAM,OAAO,GAAG,IAAI,OAAO,CAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE;QACtD,OAAO,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACtC,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;IACH,IAAI,CAAC;QAAC,OAAO,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,OAAO,CAAC,CAAC,CAAC;IAAC,CAAC;YAC5C,CAAC;QAAC,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAAC,CAAC;AAC3D,CAAC;AAED,sCAAsC;AACtC;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,IAAI,CAAC,CAAK,EAAE,OAAgB,EAAE,GAAW,EAAE,OAAqE,EAAE;IACtI,MAAM,IAAI,GAAI,CAAC,CAAC,OAA4B,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACrE,MAAM,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;IAC3D,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;IAC/E,IAAI,UAAU,GAAkB,IAAI,CAAC;IACrC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,MAAM,EAAE,KAAK,IAAI,EAAE;YAChD,MAAM,EAAE,GAAG,KAAK,EAAE,CAAC;YACnB,IAAI,EAAE,KAAK,MAAM,EAAE,CAAC;gBAClB,MAAM,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;gBACrD,SAAS,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,CAAC,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;gBACvF,MAAM,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;gBACrG,UAAU,GAAG,GAAG,CAAC;gBACjB,OAAO,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YACpC,CAAC;YACD,MAAM,IAAI,GAAG,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YACnC,IAAI,CAAC,IAAI;gBAAE,MAAM,IAAI,cAAc,CAAC,uBAAuB,CAAC,CAAC,SAAS,CAAC,IAAI,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;YAC3G,MAAM,KAAK,GAAG,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YACrF,MAAM,IAAI,CAAC,CAAC,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;YAChC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,WAAW,IAAI,EAAE,CAAC,CAAC;YAC/C,IAAI,CAAC,CAAC,MAAM,KAAK,KAAK;gBAAE,KAAK,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC;oBAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;YACtH,4CAA4C;YAC5C,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;gBAC3B,MAAM,EAAE,CAAC,CAAC,MAAM;gBAChB,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,UAAU,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,iBAAiB,EAAE,GAAG,EAAE;gBAC/H,IAAI,EAAE,CAAC,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,MAAM;aACpE,CAAC,CAAC;YACH,UAAU,GAAG,GAAG,CAAC,MAAM,CAAC;YACxB,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;YAC9B,IAAI,IAAI,GAAY,IAAI,CAAC;YACzB,IAAI,CAAC;gBAAC,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,gCAAgC,CAAC,CAAC;YACzF,IAAI,CAAC,GAAG,CAAC,EAAE;gBAAE,MAAM,IAAI,cAAc,CAAC,mBAAmB,CAAC,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,OAAO,GAAG,CAAC,MAAM,EAAE,EAAE,GAAG,CAAC,MAAM,EAChH,CAAC,CAAC,MAAM,KAAK,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,CAAC,EAAE,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;YACjH,MAAM,MAAM,GAAG,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;YAC1C,IAAI,CAAC,MAAM,CAAC,OAAO;gBAAE,MAAM,IAAI,cAAc,CAAC,mBAAmB,CAAC,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;YACxI,OAAO,MAAM,CAAC,IAAI,CAAC;QACrB,CAAC,CAAC,CAAC;QACH,MAAM,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,QAAQ,IAAI,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;QAC7F,OAAO,MAAM,CAAC;IAChB,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,GAAG,GAAG,CAAC,YAAY,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,cAAc,CAAC,sBAAsB,CAAC,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,OAAQ,CAAW,EAAE,IAAI,IAAI,OAAO,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC,MAAM,KAAK,OAAO,EAAE,SAAS,CAAC,CAAC;QACtM,MAAM,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,QAAQ,IAAI,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;QACjH,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC;AAED,+CAA+C;AAC/C,SAAS,SAAS,CAAC,KAAc;IAC/B,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAC9E,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;IACvE,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAE,KAAa,CAAC,GAAG,CAAC,KAAK,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,SAAS,CAAE,KAAa,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;AAChL,CAAC;AAID,4CAA4C;AAC5C;;;;;GAKG;AACH,KAAK,UAAU,eAAe,CAAC,CAAS,EAAE,EAAY,EAAE,GAAc,EAAE,QAAgB,EAAE,MAAoB;IAC5G,MAAM,MAAM,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,OAAc,CAAC,CAAC;IAC1C,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IACtE,MAAM,IAAI,GAAI,EAAE,CAAC,EAAE,CAAC,OAA4B,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACxE,MAAM,GAAG,GAAG,EAAE,CAAC,cAAc,CAAC,GAAG,CAAC,OAAc,CAAC,CAAC;IAClD,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,MAAM,IAAI,cAAc,CAAC,+BAA+B,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;IAC5G,MAAM,WAAW,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC/E,MAAM,QAAQ,GAAG,EAAE,SAAS,EAAE,GAAG,CAAC,SAAS,EAAE,SAAS,EAAE,EAAE,CAAC,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,SAAS,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,eAAe,EAAE,GAAG,EAAE,CAAC;IAC5H,MAAM,MAAM,GAAG,CAAC,EAAgB,EAAE,EAAE,CAAC,EAAE,CAAC,UAAU,CAAC,yBAAyB,CAAC,CAAC,SAAS,EAAE;SACtF,KAAK,CAAC,WAAW,EAAE,GAAG,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,GAAG,EAAE,QAAQ,CAAC,SAAS,CAAC;SACvF,KAAK,CAAC,WAAW,EAAE,GAAG,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,iBAAiB,EAAE,GAAG,EAAE,QAAQ,CAAC,eAAe,CAAC,CAAC;IACvG,MAAM,KAAK,GAAG,UAAU,EAAE,CAAC;IAC3B,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE;QAC3D,MAAM,EAAE,CAAC,UAAU,CAAC,yBAAyB,CAAC,CAAC,MAAM,CAAC,EAAE,GAAG,QAAQ,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,GAAG,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAC;aAC9I,UAAU,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,iBAAiB,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;QACpH,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,EAAE,CAAC,uBAAuB,EAAE,CAAC;QACxE,IAAI,QAAQ,CAAC,YAAY,KAAK,WAAW;YAAE,OAAO,EAAE,IAAI,EAAE,UAAmB,EAAE,CAAC;QAChF,IAAI,QAAQ,CAAC,MAAM,KAAK,WAAW;YAAE,OAAO,EAAE,IAAI,EAAE,SAAkB,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,EAAE,CAAC;QACtG,IAAI,QAAQ,CAAC,MAAM,KAAK,SAAS;YAAE,OAAO,EAAE,IAAI,EAAE,SAAkB,EAAE,CAAC;QACvE,IAAI,QAAQ,CAAC,MAAM,KAAK,SAAS,IAAI,QAAQ,CAAC,WAAW,IAAI,IAAI,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE;YAAE,OAAO,EAAE,IAAI,EAAE,MAAe,EAAE,CAAC;QACrJ,IAAI,QAAQ,CAAC,MAAM,KAAK,SAAS,IAAI,EAAE,CAAC,EAAE,CAAC,MAAM,KAAK,OAAO,EAAE,CAAC;YAC9D,MAAM,EAAE,CAAC,WAAW,CAAC,yBAAyB,CAAC,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,kBAAkB,EAAE,UAAU,EAAE,GAAG,CAAA,OAAO,EAAE,CAAC;iBAC1H,KAAK,CAAC,WAAW,EAAE,GAAG,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,GAAG,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,GAAG,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,iBAAiB,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC;YACpL,OAAO,EAAE,IAAI,EAAE,SAAkB,EAAE,CAAC;QACtC,CAAC;QACD,MAAM,EAAE,CAAC,WAAW,CAAC,yBAAyB,CAAC;aAC5C,GAAG,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,SAAS,CAAC,SAAS,GAAG,MAAM,CAAC,EAAE,QAAQ,EAAE,GAAG,CAAA,cAAc,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,CAAA,OAAO,EAAE,CAAC;aACxL,KAAK,CAAC,WAAW,EAAE,GAAG,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,GAAG,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,GAAG,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,iBAAiB,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC;QACpL,OAAO,EAAE,IAAI,EAAE,SAAkB,EAAE,CAAC;IACtC,CAAC,CAAC,CAAC;IACH,IAAI,KAAK,CAAC,IAAI,KAAK,UAAU;QAAE,MAAM,IAAI,cAAc,CAAC,mBAAmB,QAAQ,uBAAuB,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;IACzH,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS;QAAE,MAAM,IAAI,cAAc,CAAC,mBAAmB,QAAQ,yBAAyB,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;IACrI,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM;QAAE,MAAM,IAAI,cAAc,CAAC,GAAG,QAAQ,eAAe,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAC5F,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS;QAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,CAAC,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;IACxG,IAAI,QAAiB,CAAC;IACtB,IAAI,CAAC;QACH,uCAAuC;QACvC,QAAQ,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,GAAG,GAAG,CAAC,SAAS,IAAI,GAAG,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IAC5E,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,GAAG,GAAG,CAAmB,CAAC;QAChC,MAAM,OAAO,GAAG,EAAE,CAAC,EAAE,CAAC,MAAM,KAAK,OAAO,IAAI,GAAG,CAAC,OAAO,KAAK,SAAS,CAAC;QACtE,MAAM,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,yBAAyB,CAAC;aAC/C,GAAG,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,CAAA,OAAO,EAAE,CAAC;aACzI,KAAK,CAAC,WAAW,EAAE,GAAG,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,GAAG,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,GAAG,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,iBAAiB,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,aAAa,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC;QACrN,MAAM,UAAU,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,GAAG,CAAC,SAAS,EAAE,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;QACnG,MAAM,GAAG,CAAC;IACZ,CAAC;IACD,+CAA+C;IAC/C,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,yBAAyB,CAAC;SAC7D,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,CAAA,OAAO,EAAE,CAAC;SAC3I,KAAK,CAAC,WAAW,EAAE,GAAG,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,GAAG,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,GAAG,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,iBAAiB,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,aAAa,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC,gBAAgB,EAAE,CAAC;IAC9N,IAAI,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC;QAAE,MAAM,IAAI,cAAc,CAAC,GAAG,QAAQ,kBAAkB,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;IACvH,MAAM,UAAU,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,GAAG,CAAC,SAAS,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;IAC1E,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;AACtC,CAAC;AAED,qDAAqD;AACrD,0DAA0D;AAC1D,MAAM,UAAU,gBAAgB,CAAC,CAAS,EAAE,EAAY;IACtD,MAAM,IAAI,GAAG,YAAY,EAAE,CAAC,EAAE,CAAC,SAAS,CAAC,IAAI,IAAI,EAAE,CAAC,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;IAC/E,OAAO;QACL,IAAI;QACJ,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI;QACpB,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE;YACpC,IAAI,CAAC;gBACH,OAAO,MAAM,eAAe,CAAC,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;YACzD,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,IAAI,CAAC,YAAY,cAAc,IAAI,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC;oBAChD,oBAAoB;oBACpB,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;gBACtF,CAAC;gBACD,MAAM,CAAC,CAAC;YACV,CAAC;QACH,CAAC;QACD,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,EAAE,OAAO,EAAE,QAAQ;YACrC,IAAI,CAAC,QAAQ;gBAAE,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;YACjF,MAAM,MAAM,GAAG,QAA4B,CAAC;YAC5C,IAAI,MAAM,CAAC,OAAO;gBAAE,OAAO;YAC3B,IAAI,EAAE,CAAC,SAAS,EAAE,CAAC;gBACjB,MAAM,KAAK,GAAG,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,EAAE,GAAG,CAAC,OAAc,CAAC,CAAC;gBACpE,MAAM,GAAG,GAAG,SAAS,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,CAAC,SAAS,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,EAAE,SAAS,EAAE,GAAG,CAAC,UAAU,IAAI,SAAS,EAAE,CAAC,CAAC;gBACxM,MAAM,CAAC,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;YAC7D,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC"}
|
package/dist/stream.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Connector } from './primitives.js';
|
|
2
|
+
/** 模型等流式协议共用连接器的地址、密钥、限流和取消边界。响应正文不写通用日志。 */
|
|
3
|
+
/**
|
|
4
|
+
* 只向所选白名单主机 POST,禁止重定向和 fake 外呼;凭据可按本次调用独立读取。
|
|
5
|
+
* 逐块 UTF-8 解码并按 SSE 空行合并多个 data 行,await onData 提供消费背压;总响应上限 4 MiB。
|
|
6
|
+
* 取消、超限或回调失败都向上传播,finally 取消 reader 并释放锁;HTTP 错误只保留状态和稳定供应商码。
|
|
7
|
+
*/
|
|
8
|
+
export declare function streamJson(connector: Connector, path: string, body: unknown, signal: AbortSignal, onData: (data: string) => Promise<void>, options?: {
|
|
9
|
+
environment?: string;
|
|
10
|
+
protocol?: 'http:' | 'https:';
|
|
11
|
+
credential?: () => Promise<string>;
|
|
12
|
+
}): Promise<void>;
|
package/dist/stream.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 模型等 SSE 请求的传输适配:复用连接器主机、密钥、限流和超时边界,只传递 data 帧文本。
|
|
3
|
+
* JSON 业务解析与模型语义由上层负责,本层不记录提示词、响应正文或多租户凭据。
|
|
4
|
+
*/
|
|
5
|
+
import { ConnectorError, secrets, stage, take } from './runtime.js';
|
|
6
|
+
/** 模型等流式协议共用连接器的地址、密钥、限流和取消边界。响应正文不写通用日志。 */
|
|
7
|
+
/**
|
|
8
|
+
* 只向所选白名单主机 POST,禁止重定向和 fake 外呼;凭据可按本次调用独立读取。
|
|
9
|
+
* 逐块 UTF-8 解码并按 SSE 空行合并多个 data 行,await onData 提供消费背压;总响应上限 4 MiB。
|
|
10
|
+
* 取消、超限或回调失败都向上传播,finally 取消 reader 并释放锁;HTTP 错误只保留状态和稳定供应商码。
|
|
11
|
+
*/
|
|
12
|
+
export async function streamJson(connector, path, body, signal, onData, options = {}) {
|
|
13
|
+
const environment = options.environment ?? stage(), protocol = options.protocol ?? 'https:';
|
|
14
|
+
if (!path.startsWith('/') || path.startsWith('//'))
|
|
15
|
+
throw new ConnectorError('流式操作需要同主机绝对路径', null, false);
|
|
16
|
+
const host = connector.hosts[environment];
|
|
17
|
+
if (environment === 'fake')
|
|
18
|
+
throw new ConnectorError('fake 环境必须显式配置模型 fake,禁止外呼', null, false);
|
|
19
|
+
if (!host)
|
|
20
|
+
throw new ConnectorError('当前阶段未配置连接器地址', null, false);
|
|
21
|
+
const abort = AbortSignal.any([signal, AbortSignal.timeout(connector.timeoutMs)]);
|
|
22
|
+
await take(connector, abort);
|
|
23
|
+
// 每次调用独立提供凭据,动态模型不得改动全局 SecretProvider 而串用其他租户密钥。
|
|
24
|
+
const token = options.credential ? await options.credential() : connector.secret ? await secrets.get(connector.secret) : undefined;
|
|
25
|
+
const url = new URL(path, `${protocol}//${host}`);
|
|
26
|
+
if (url.host !== host || url.protocol !== protocol || !['http:', 'https:'].includes(protocol))
|
|
27
|
+
throw new ConnectorError('流式地址超出连接器白名单', null, false);
|
|
28
|
+
const response = await fetch(url, { method: 'POST', redirect: 'error', signal: abort,
|
|
29
|
+
headers: { 'content-type': 'application/json', accept: 'text/event-stream', ...(token ? { authorization: `Bearer ${token}` } : {}) },
|
|
30
|
+
body: JSON.stringify(body) });
|
|
31
|
+
if (!response.ok || !response.body) {
|
|
32
|
+
// 只提取供应商的稳定错误码/特定兼容性标记,不把响应正文写入日志或 Cage 运行记录。
|
|
33
|
+
let providerCode;
|
|
34
|
+
try {
|
|
35
|
+
const payload = await response.clone().json(), message = String(payload?.message ?? payload?.error?.message ?? '');
|
|
36
|
+
const code = payload?.code ?? payload?.error?.code;
|
|
37
|
+
if (typeof code === 'string' && /^[A-Za-z0-9_.-]{1,100}$/.test(code))
|
|
38
|
+
providerCode = code;
|
|
39
|
+
if (/previous_response_id\s+requires/i.test(message))
|
|
40
|
+
providerCode = 'PREVIOUS_RESPONSE_ID_UNSUPPORTED';
|
|
41
|
+
}
|
|
42
|
+
catch { /* 非 JSON 错误只保留 HTTP 状态 */ }
|
|
43
|
+
await response.body?.cancel();
|
|
44
|
+
throw new ConnectorError(`模型连接器返回 ${response.status}`, response.status, response.status === 429 || response.status >= 500, 'rejected', providerCode);
|
|
45
|
+
}
|
|
46
|
+
const reader = response.body.getReader(), decoder = new TextDecoder();
|
|
47
|
+
let buffer = '', bytes = 0, data = [];
|
|
48
|
+
const line = async (value) => {
|
|
49
|
+
if (!value) {
|
|
50
|
+
if (data.length)
|
|
51
|
+
await onData(data.join('\n'));
|
|
52
|
+
data = [];
|
|
53
|
+
}
|
|
54
|
+
else if (value.startsWith('data:'))
|
|
55
|
+
data.push(value.slice(5).replace(/^ /, ''));
|
|
56
|
+
};
|
|
57
|
+
try {
|
|
58
|
+
for (;;) {
|
|
59
|
+
abort.throwIfAborted();
|
|
60
|
+
const chunk = await reader.read();
|
|
61
|
+
if (chunk.done)
|
|
62
|
+
break;
|
|
63
|
+
bytes += chunk.value.byteLength;
|
|
64
|
+
if (bytes > 4 * 1024 * 1024)
|
|
65
|
+
throw new ConnectorError('模型响应超过 4 MiB', response.status, false);
|
|
66
|
+
buffer += decoder.decode(chunk.value, { stream: true });
|
|
67
|
+
let end;
|
|
68
|
+
while ((end = buffer.indexOf('\n')) >= 0) {
|
|
69
|
+
const value = buffer.slice(0, end).replace(/\r$/, '');
|
|
70
|
+
buffer = buffer.slice(end + 1);
|
|
71
|
+
await line(value);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
buffer += decoder.decode();
|
|
75
|
+
if (buffer)
|
|
76
|
+
await line(buffer.replace(/\r$/, ''));
|
|
77
|
+
await line('');
|
|
78
|
+
}
|
|
79
|
+
finally {
|
|
80
|
+
await reader.cancel().catch(() => { });
|
|
81
|
+
reader.releaseLock();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=stream.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream.js","sourceRoot":"","sources":["../src/stream.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yss123/cage-connectors",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "第三方连接器原语与运行时",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"zod": "^4.1.5",
|
|
19
19
|
"kysely": "^0.28.5",
|
|
20
|
-
"@yss123/cage-kernel": "0.
|
|
20
|
+
"@yss123/cage-kernel": "0.3.1"
|
|
21
21
|
},
|
|
22
22
|
"license": "MIT",
|
|
23
23
|
"repository": {
|