@tier0/sdk-v2 0.2.1 → 0.2.2
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 +7 -5
- package/dist/cjs/data/collection.d.ts +42 -2
- package/dist/cjs/data/collection.js +5 -2
- package/dist/cjs/data/collection.js.map +1 -1
- package/dist/cjs/data/index.d.ts +1 -1
- package/dist/cjs/data/index.js.map +1 -1
- package/dist/cjs/uns-v2/generated.d.ts +109 -40
- package/dist/cjs/uns-v2/generated.js +8 -8
- package/dist/cjs/uns-v2/generated.js.map +1 -1
- package/dist/esm/data/collection.d.ts +42 -2
- package/dist/esm/data/collection.js +5 -2
- package/dist/esm/data/collection.js.map +1 -1
- package/dist/esm/data/index.d.ts +1 -1
- package/dist/esm/data/index.js.map +1 -1
- package/dist/esm/uns-v2/generated.d.ts +109 -40
- package/dist/esm/uns-v2/generated.js +8 -8
- package/dist/esm/uns-v2/generated.js.map +1 -1
- package/package.json +17 -5
- package/skills/tier0-sdk-v2/tier0-sdk-v2-data/references/read-write.md +5 -1
- package/skills/tier0-sdk-v2/tier0-sdk-v2-source/SKILL.md +3 -3
- package/skills/tier0-sdk-v2/tier0-sdk-v2-source/references/create.md +3 -6
- package/skills/tier0-sdk-v2/tier0-sdk-v2-source/references/delete.md +10 -18
- package/skills/tier0-sdk-v2/tier0-sdk-v2-source/references/state.md +9 -10
- package/skills/tier0-sdk-v2/tier0-sdk-v2-source/references/update.md +39 -21
- package/skills/tier0-sdk-v2/tier0-sdk-v2-stream/SKILL.md +6 -6
- package/skills/tier0-sdk-v2/tier0-sdk-v2-stream/references/create.md +13 -13
- package/skills/tier0-sdk-v2/tier0-sdk-v2-stream/references/deploy.md +11 -10
- package/skills/tier0-sdk-v2/tier0-sdk-v2-stream/references/update.md +3 -5
- package/skills/tier0-sdk-v2/tier0-sdk-v2-uns/references/create.md +6 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tier0/sdk-v2",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Tier0 Cloud new-architecture SDK for TypeScript and JavaScript",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -58,12 +58,24 @@
|
|
|
58
58
|
}
|
|
59
59
|
},
|
|
60
60
|
"./source": {
|
|
61
|
-
"import": {
|
|
62
|
-
|
|
61
|
+
"import": {
|
|
62
|
+
"types": "./dist/esm/source.d.ts",
|
|
63
|
+
"default": "./dist/esm/source.js"
|
|
64
|
+
},
|
|
65
|
+
"require": {
|
|
66
|
+
"types": "./dist/cjs/source.d.ts",
|
|
67
|
+
"default": "./dist/cjs/source.js"
|
|
68
|
+
}
|
|
63
69
|
},
|
|
64
70
|
"./stream": {
|
|
65
|
-
"import": {
|
|
66
|
-
|
|
71
|
+
"import": {
|
|
72
|
+
"types": "./dist/esm/stream.d.ts",
|
|
73
|
+
"default": "./dist/esm/stream.js"
|
|
74
|
+
},
|
|
75
|
+
"require": {
|
|
76
|
+
"types": "./dist/cjs/stream.d.ts",
|
|
77
|
+
"default": "./dist/cjs/stream.js"
|
|
78
|
+
}
|
|
67
79
|
},
|
|
68
80
|
"./files": {
|
|
69
81
|
"import": {
|
|
@@ -88,13 +88,17 @@ export async function ordersScreen(page: number, selectedId: string) {
|
|
|
88
88
|
|
|
89
89
|
| 写入 | 说明 |
|
|
90
90
|
| --- | --- |
|
|
91
|
-
| `insertOne(doc)`、`insertMany(docs)` | 可带 `_id`;`insertMany` 最多 200 条、原子 |
|
|
91
|
+
| `insertOne(doc, options)`、`insertMany(docs)` | 可带 `_id`;单条默认返回 `{ insertedId }`,`returnDocument:'after'` 同次写入返回 `{ insertedId, value }`;`insertMany` 最多 200 条、原子 |
|
|
92
92
|
| `updateOne` / `updateMany` / `replaceOne` | 更新用操作符(`$set` `$inc` `$push` `$unset`…);`replaceOne` 会丢掉其他代码版本写入的字段 |
|
|
93
93
|
| `deleteOne` / `deleteMany` | 软删除,App Key 需要 `uns.delete` |
|
|
94
94
|
| `findOneAndUpdate` / `findOneAndReplace` / `findOneAndDelete` | 原子读改写,默认返回更新后的文档 |
|
|
95
95
|
| `bulkWrite(ops)` | `insertOne` / `updateOne` / `replaceOne` / `deleteOne` 混合,最多 200 项、原子 |
|
|
96
96
|
| `data.transact(steps)` | 跨集合原子写,见 [一致性](consistency.md) |
|
|
97
97
|
|
|
98
|
+
新建后需要显示保存结果或继续按版本编辑时,使用 `insertOne(doc, { returnDocument: 'after', idempotencyKey })`。`value` 与查询结果一样是 `{ _id, ...业务字段, _meta }`,包含服务端生成的数据和 `_meta.revision`,无需额外查询。返回快照需要当前读取权限;`projection` 只能与 `after` 一起提供。重放返回当次创建的快照,不能当成记录当前最新版本;之后的版本冲突仍需显式读取并处理。
|
|
99
|
+
|
|
100
|
+
带 `projection` 时,返回类型将业务字段递归标为可选,包括嵌套对象和数组元素的字段;读取前需判断是否存在。`_id` 和 `_meta` 仍然必有。不带 `projection` 的 `after` 保持完整文档类型。
|
|
101
|
+
|
|
98
102
|
## 批量与原子性
|
|
99
103
|
|
|
100
104
|
| 调用 | 上限 | 原子 |
|
|
@@ -24,14 +24,14 @@ Source 维护连接配置、认证引用与源状态。脚本处理用 [Stream](
|
|
|
24
24
|
## 核心约束
|
|
25
25
|
|
|
26
26
|
1. 原生文档 `{name,protocol,config}` 使用 documents.*;通用投影使用 source.list/get/create/update。http 原生协议与 http-api adapter 不可互换。
|
|
27
|
-
2.
|
|
28
|
-
3. 现有 Source 修改、启停、删除都要 expectedRevision
|
|
27
|
+
2. 已有 ID 和 revision 时优先用 documents.update 的 patch,避免额外 get;完整替换 document 前读取完整文档,安全列表摘要不能无损回写。原生 ID 是十进制字符串,revision 原样传递,不转 Number。
|
|
28
|
+
3. 现有 Source 修改、启停、删除都要 expectedRevision。没有任何 Stream 引用时可直接提交;有草稿、活动、历史或不可见引用时仍需 confirmedImpactRevision。先形成精确意图,再读取全部影响分页;回执不可合成、跨意图复用或静默刷新。提供的回执始终严格校验,不自动获取并同意影响。
|
|
29
29
|
4. 配置中保留认证/TLS 引用,不写明文秘密;HTTP 新凭据走创建专用 credentials。
|
|
30
30
|
5. desiredState 不代表实际健康,保存配置不代表 Stream 已切换至新 configRevision。运行状态另行读取。
|
|
31
31
|
|
|
32
32
|
## 返回值与异常
|
|
33
33
|
|
|
34
|
-
所有调用返回完整 code/msg/requestId/data。documents.create/update 的预检返回 validated/sideEffects,提交返回完整 Source view;批量状态/删除返回 Outcome。只有 committed 且无项错误才可报告提交成功;accepted/jobId
|
|
34
|
+
所有调用返回完整 code/msg/requestId/data。documents.create/update 的预检返回 validated/sideEffects,提交返回完整 Source view,可直接使用其中的文档和 revision,无需额外 get;批量状态/删除返回 Outcome。只有 committed 且无项错误才可报告提交成功;accepted/jobId 继续查询作业。预检按需使用,不是每次提交的必经步骤。
|
|
35
35
|
|
|
36
36
|
需要查看历史用 source.history({id,beforeRevision,size}),按 nextBeforeRevision 继续;探测用 source.probe,其外部系统访问应属于当前任务。source.connection.update 属于现有源变更,同样遵守影响确认契约,不能作为绕过文档校验的入口。
|
|
37
37
|
|
|
@@ -26,18 +26,15 @@ export async function createHttpSource(
|
|
|
26
26
|
name, protocol: 'http', description: '订单系统 HTTP 接入',
|
|
27
27
|
config: { configVersion: 2, endpoint, options: {} },
|
|
28
28
|
};
|
|
29
|
-
const preview = await client.source.documents.create({ dryRun: true, document });
|
|
30
|
-
if (!('validated' in preview.data) || !preview.data.validated) throw new Error('Source 预检未通过');
|
|
31
29
|
const saved = await client.source.documents.create({
|
|
32
30
|
dryRun: false, document, idempotencyKey: intentKey,
|
|
33
31
|
});
|
|
34
32
|
if (!('id' in saved.data)) throw new Error('未返回已保存的 Source');
|
|
35
|
-
|
|
36
|
-
return { receipt: saved, current: current.data };
|
|
33
|
+
return { receipt: saved, current: saved.data };
|
|
37
34
|
}
|
|
38
35
|
```
|
|
39
36
|
|
|
40
|
-
|
|
37
|
+
一次提交即可返回完整 data:id、revision、document、desiredState、runtimeState、health、createdAt、updatedAt,无需另行 get。需要单独预检时可先传 `dryRun: true`,返回 `{ validated: true, sideEffects: false }`,没有 ID。预检不保留提交条件;实际保存仍会校验。ID 原样保留十进制字符串。
|
|
41
38
|
|
|
42
39
|
## HTTP Bearer 凭据
|
|
43
40
|
|
|
@@ -56,7 +53,7 @@ export async function createAuthenticatedHttpSource(
|
|
|
56
53
|
credentials: { method: 'GET', kind: 'bearer', material: { token } },
|
|
57
54
|
});
|
|
58
55
|
if (!('id' in saved.data)) throw new Error('未返回 Source ID');
|
|
59
|
-
return
|
|
56
|
+
return { receipt: saved, current: saved.data };
|
|
60
57
|
}
|
|
61
58
|
```
|
|
62
59
|
|
|
@@ -7,41 +7,33 @@
|
|
|
7
7
|
| dryRun | boolean | 是 | true 预检;false 提交 |
|
|
8
8
|
| items | array | 是 | 1–200 项 |
|
|
9
9
|
| items[].id | string | 是 | 正十进制 int64 字符串 |
|
|
10
|
-
| items[].expectedRevision | integer | 是 |
|
|
11
|
-
| items[].confirmedImpactRevision | string |
|
|
10
|
+
| items[].expectedRevision | integer | 是 | 已知的当前修订 |
|
|
11
|
+
| items[].confirmedImpactRevision | string | 有引用时 | action=delete 的影响回执 |
|
|
12
12
|
| mode | auto/sync | 否 | 不支持 async |
|
|
13
13
|
| allowPartial | false | 否 | 不支持部分提交 |
|
|
14
14
|
| idempotencyKey | string | 否 | 最多 128 字符 |
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
Source 必须已停用。没有任何 Stream 引用时,直接用已有 ID 和 revision 提交;有引用时先 impact(action=delete) 并[遍历和评估影响](changes.md),再显式传入回执。服务端在同一事务中检查依赖和修订,不自动删除或停用依赖流。
|
|
17
17
|
|
|
18
18
|
```ts
|
|
19
19
|
import { createClient, type Tier0Client, type UnsV2Request } from '@tier0/sdk-v2';
|
|
20
20
|
|
|
21
21
|
export async function deleteSource(
|
|
22
22
|
id: string, expectedRevision: UnsV2Request<'sourceImpact'>['expectedRevision'],
|
|
23
|
-
|
|
23
|
+
intentKey: string, client: Tier0Client = createClient(), confirmedImpactRevision?: string,
|
|
24
24
|
) {
|
|
25
25
|
const receipt = await client.source.delete({
|
|
26
26
|
dryRun: false, mode: 'sync', allowPartial: false, idempotencyKey: intentKey,
|
|
27
|
-
items: [{ id, expectedRevision,
|
|
27
|
+
items: [{ id, expectedRevision,
|
|
28
|
+
...(confirmedImpactRevision === undefined ? {} : { confirmedImpactRevision }) }],
|
|
28
29
|
});
|
|
29
30
|
if (receipt.data.status !== 'committed' || receipt.data.results?.some(item => item.error)) {
|
|
30
31
|
return { state: 'unconfirmed' as const, receipt };
|
|
31
32
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
archived: true, page, size: 200, includeDocument: false,
|
|
36
|
-
});
|
|
37
|
-
const archived = data.list.find(item => item.id === id);
|
|
38
|
-
if (archived) return { state: 'archived' as const, receipt, archived };
|
|
39
|
-
if (!data.hasMore) break;
|
|
40
|
-
if (typeof data.nextPage !== 'number' || data.nextPage <= page) throw new Error('无有效下一页');
|
|
41
|
-
page = data.nextPage;
|
|
42
|
-
}
|
|
43
|
-
return { state: 'committed-readback-unconfirmed' as const, receipt };
|
|
33
|
+
const archived = receipt.data.results?.find(item => item.id === id && item.status === 'deleted');
|
|
34
|
+
if (!archived) return { state: 'unconfirmed' as const, receipt };
|
|
35
|
+
return { state: 'archived' as const, receipt, archived };
|
|
44
36
|
}
|
|
45
37
|
```
|
|
46
38
|
|
|
47
|
-
返回 Outcome,HTTP 200/validated/accepted
|
|
39
|
+
返回 Outcome,HTTP 200/validated/accepted 不等于完成。已提交回执中对应 ID 的 deleted 结果足以确认归档,无需遍历归档列表;不能把任意 get 异常当成成功。传输结果未知时保留原请求、requestId 和意图键,不自动重试。补充确认回执是新请求,应使用新幂等键。
|
|
@@ -5,33 +5,32 @@
|
|
|
5
5
|
| 字段 | 类型 | 必填 | 说明 |
|
|
6
6
|
| --- | --- | --- | --- |
|
|
7
7
|
| dryRun | boolean | 是 | 提交 false |
|
|
8
|
-
| items[].id / expectedRevision | string / integer | 是 |
|
|
8
|
+
| items[].id / expectedRevision | string / integer | 是 | 当前对象和已知修订 |
|
|
9
9
|
| items[].desiredState | enabled/disabled | 是 | 目标状态 |
|
|
10
|
-
| items[].confirmedImpactRevision | string |
|
|
10
|
+
| items[].confirmedImpactRevision | string | 有引用时 | enable/disable 对应的影响回执 |
|
|
11
11
|
| mode / allowPartial | string / boolean | 否 | 单项示例 sync / false |
|
|
12
12
|
| idempotencyKey | string | 否 | 本次状态变更的独立意图键 |
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
没有任何 Stream 引用时,一次请求即可提交期望状态。有引用时先以相同修订执行 impact(action=enable 或 disable),按[影响分析](changes.md)遍历并评估,再显式传入回执。服务端在同一事务中检查引用和修订,不会因未传回执而自动同意影响。
|
|
15
15
|
|
|
16
16
|
```ts
|
|
17
17
|
import { createClient, type Tier0Client, type UnsV2Request } from '@tier0/sdk-v2';
|
|
18
18
|
|
|
19
19
|
export async function setSourceState(
|
|
20
20
|
id: string, expectedRevision: UnsV2Request<'sourceImpact'>['expectedRevision'],
|
|
21
|
-
desiredState: 'enabled' | 'disabled',
|
|
22
|
-
|
|
21
|
+
desiredState: 'enabled' | 'disabled', intentKey: string,
|
|
22
|
+
client: Tier0Client = createClient(), confirmedImpactRevision?: string,
|
|
23
23
|
) {
|
|
24
24
|
const receipt = await client.source.state.update({
|
|
25
25
|
dryRun: false, mode: 'sync', allowPartial: false, idempotencyKey: intentKey,
|
|
26
|
-
items: [{ id, expectedRevision, desiredState,
|
|
26
|
+
items: [{ id, expectedRevision, desiredState,
|
|
27
|
+
...(confirmedImpactRevision === undefined ? {} : { confirmedImpactRevision }) }],
|
|
27
28
|
});
|
|
28
29
|
if (receipt.data.status !== 'committed' || receipt.data.results?.some(item => item.error)) {
|
|
29
30
|
return { state: 'unconfirmed' as const, receipt };
|
|
30
31
|
}
|
|
31
|
-
|
|
32
|
-
if (current.data.desiredState !== desiredState) throw new Error('读回状态与目标不一致');
|
|
33
|
-
return { state: 'committed' as const, receipt, current: current.data };
|
|
32
|
+
return { state: 'committed' as const, receipt };
|
|
34
33
|
}
|
|
35
34
|
```
|
|
36
35
|
|
|
37
|
-
响应为 Outcome;accepted/jobId 仅表示受理,继续查询作业。enabled 是期望状态;健康与连通性另用 observations/runtime
|
|
36
|
+
响应为 Outcome;committed 且无项错误即可确认期望状态已提交,无需额外 get。accepted/jobId 仅表示受理,继续查询作业。enabled 是期望状态;健康与连通性另用 observations/runtime 检查。提供的回执始终严格校验,确认过期或依赖变化不能自动刷新同意。
|
|
@@ -4,26 +4,47 @@
|
|
|
4
4
|
|
|
5
5
|
| 字段 | 类型 | 必填 | 说明 |
|
|
6
6
|
| --- | --- | --- | --- |
|
|
7
|
-
| id | string | 是 |
|
|
8
|
-
| document | CloudSourceDocument |
|
|
9
|
-
| expectedRevision | integer | 是 |
|
|
10
|
-
| confirmedImpactRevision | string |
|
|
7
|
+
| id | string | 是 | 已知的 Source ID |
|
|
8
|
+
| patch / document | object / CloudSourceDocument | 二选一 | patch 保留未提供的字段;document 完整替换 |
|
|
9
|
+
| expectedRevision | integer | 是 | 已读取或上次提交返回的 revision,原样传回 |
|
|
10
|
+
| confirmedImpactRevision | string | 有引用时 | 相同修改意图和修订的 impact 回执 |
|
|
11
11
|
| dryRun | boolean | 是 | true 预检;false 提交 |
|
|
12
12
|
| idempotencyKey | string | 否 | 最多 128 字符 |
|
|
13
13
|
|
|
14
|
-
##
|
|
14
|
+
## 示例:一次请求修改名称
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
已有 ID 和 revision 时直接提交 patch。服务端在同一事务和依赖锁下检查引用并修改:没有任何草稿、活动或历史 Stream 引用时无需 impact。patch 中未提供的字段保留原值,config 如果提供则整体替换;完整 document 更新需先取得完整文档并保留未改配置和认证引用。
|
|
17
17
|
|
|
18
18
|
```ts
|
|
19
|
-
import { createClient, type Tier0Client, type
|
|
19
|
+
import { createClient, type Tier0Client, type UnsV2Request } from '@tier0/sdk-v2';
|
|
20
20
|
|
|
21
|
-
export async function
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
export async function renameSource(
|
|
22
|
+
id: string, expectedRevision: UnsV2Request<'sourceImpact'>['expectedRevision'],
|
|
23
|
+
name: string, intentKey: string, client: Tier0Client = createClient(),
|
|
24
|
+
) {
|
|
25
|
+
const saved = await client.source.documents.update({
|
|
26
|
+
id, expectedRevision, patch: { name }, dryRun: false, idempotencyKey: intentKey,
|
|
27
|
+
});
|
|
28
|
+
if (!('id' in saved.data)) throw new Error('未收到更新提交结果');
|
|
29
|
+
return { receipt: saved, current: saved.data };
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
提交直接返回完整 Source view 和新的 revision,无需额外 get。版本冲突时不会覆盖其他人的修改。返回 `SOURCE_IMPACT_CONFIRMATION_REQUIRED` 表示存在引用且未提交,进入下面的显式评估流程;不要自动获取并同意影响。
|
|
34
|
+
|
|
35
|
+
## 有引用时:评估后确认相同意图
|
|
36
|
+
|
|
37
|
+
prepare 收集全部影响分页。在已有授权范围内评估 counts、restrictedCount 和引用后,再调用 commit。不可见引用也要求确认。影响字段说明见[影响分析](changes.md)。
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
import { createClient, type Tier0Client, type UnsV2Request } from '@tier0/sdk-v2';
|
|
41
|
+
|
|
42
|
+
export async function prepareSourceRename(
|
|
43
|
+
id: string, expectedRevision: UnsV2Request<'sourceImpact'>['expectedRevision'],
|
|
44
|
+
name: string, client: Tier0Client = createClient(),
|
|
45
|
+
) {
|
|
25
46
|
const intent: UnsV2Request<'sourceImpact'> = {
|
|
26
|
-
id, expectedRevision
|
|
47
|
+
id, expectedRevision, action: 'update', patch: { name },
|
|
27
48
|
};
|
|
28
49
|
const impact = await client.source.impact({ ...intent, offset: 0, size: 200 });
|
|
29
50
|
const references = [...impact.data.references];
|
|
@@ -45,18 +66,15 @@ export async function commitSourceRename(
|
|
|
45
66
|
plan: { intent: UnsV2Request<'sourceImpact'>; impact: { impactRevision: string } },
|
|
46
67
|
intentKey: string, client: Tier0Client = createClient(),
|
|
47
68
|
) {
|
|
48
|
-
const { id, expectedRevision,
|
|
49
|
-
if (action !== 'update' || !
|
|
50
|
-
const request = { id, expectedRevision, document,
|
|
51
|
-
confirmedImpactRevision: plan.impact.impactRevision };
|
|
52
|
-
const preview = await client.source.documents.update({ ...request, dryRun: true });
|
|
53
|
-
if (!('validated' in preview.data) || !preview.data.validated) throw new Error('预检未通过');
|
|
69
|
+
const { id, expectedRevision, patch, action } = plan.intent;
|
|
70
|
+
if (action !== 'update' || !patch) throw new Error('需要修改意图');
|
|
54
71
|
const saved = await client.source.documents.update({
|
|
55
|
-
|
|
72
|
+
id, expectedRevision, patch, confirmedImpactRevision: plan.impact.impactRevision,
|
|
73
|
+
dryRun: false, idempotencyKey: intentKey,
|
|
56
74
|
});
|
|
57
75
|
if (!('id' in saved.data)) throw new Error('未收到更新提交结果');
|
|
58
|
-
return { receipt: saved, current:
|
|
76
|
+
return { receipt: saved, current: saved.data };
|
|
59
77
|
}
|
|
60
78
|
```
|
|
61
79
|
|
|
62
|
-
|
|
80
|
+
影响评估后的 plan 保持不变;已提供的确认回执始终严格校验,即使之后已无引用。修订冲突、确认过期或不匹配时重新读取并评估,不静默刷新回执重放。修改请求内容或补充确认回执属于新请求,应使用新幂等键;传输结果未知时保留原路由、请求和键,SDK 不自动重试。
|
|
@@ -11,7 +11,7 @@ description: 使用 @tier0/sdk-v2 的 client.stream 创建、查询、修改和
|
|
|
11
11
|
|
|
12
12
|
| 用户任务 | 参考 | 方法 |
|
|
13
13
|
| --- | --- | --- |
|
|
14
|
-
| 新建并绑定 HTTP Source | [创建](references/create.md) |
|
|
14
|
+
| 新建并绑定 HTTP Source | [创建](references/create.md) | documents.create({program}) |
|
|
15
15
|
| 列表、完整文档、运行记录 | [查询](references/read.md) | programs.list/get、documents.get、observations.get、runs.list |
|
|
16
16
|
| 修改草稿、脚本和绑定 | [修改](references/update.md) | documents.validate/update |
|
|
17
17
|
| 发布、停用、版本回滚 | [部署](references/deploy.md) | programs.deploy/rollback、documents.disable |
|
|
@@ -22,22 +22,22 @@ description: 使用 @tier0/sdk-v2 的 client.stream 创建、查询、修改和
|
|
|
22
22
|
|
|
23
23
|
| 形态 | 完整输入 | 适用入口 |
|
|
24
24
|
| --- | --- | --- |
|
|
25
|
-
| Runtime program | name、sdkVersion、script 字符串、sourceBindings、timeoutMs | programs.create/update/validate |
|
|
25
|
+
| Runtime program | name、sdkVersion、script 字符串、sourceBindings、timeoutMs | documents.create/update/validate({program});兼容 programs.create/update/validate({document}) |
|
|
26
26
|
| 云端原生文档 | name、model、script 对象、sources、entries | documents.create/get/update/validate |
|
|
27
27
|
|
|
28
28
|
程序必须是完整 `module.exports = api => { ... }`,执行器 API 是 api.sources/api.uns/api.stream,与 npm 包的 client.source 不同。Runtime sourceBindings 为 sourceId/configRevision/acquisition;原生 sources 还使用 alias/resources。连接凭据不进脚本或 acquisition。
|
|
29
29
|
|
|
30
30
|
## 核心约束
|
|
31
31
|
|
|
32
|
-
1. programs.get
|
|
32
|
+
1. programs.get 返回脱敏摘要,不含可回写脚本;完整编辑使用 documents.get 或上次 documents.create/update 返回的完整文档。保留未修改的 sources/entries/script 字段。
|
|
33
33
|
2. create/update 保存草稿,不自动发布;documents 没有 deploy,云端 program 发布使用 programs.deploy。不能将已保存报告为已运行。
|
|
34
34
|
3. 所有更新/部署/删除携带当前 expectedRevision。原生停用另需 expectedActiveRelease,避免停掉他人刚发布的版本。
|
|
35
35
|
4. versionId 从 versions.list 取得,不把 release 序号当 ID。expectedSourceRevision 仅用于恰好一个依赖源,不代表多个源的修订。
|
|
36
|
-
5. resources=[]
|
|
36
|
+
5. resources=[] 是空冻结资源集,不是通配符。能力选择可查询 runtime.capabilities,写入本身由服务端校验;需要预览时单独 validate。当前云端程序 timeoutMs 上限 30000。
|
|
37
37
|
6. 仅执行用户要求的生命周期步骤;修改脚本不自动授予发布、停用或删除权限。
|
|
38
38
|
|
|
39
39
|
## 成功与异常
|
|
40
40
|
|
|
41
|
-
响应保留 code/msg/requestId/data
|
|
41
|
+
响应保留 code/msg/requestId/data。创建/更新回执的 revision 可直接用于发布;programs 发布回执的 activeRelease 是同次事务保存的正整数发布序号,无需前后 get。programs 回执和查询结果无活跃发布时省略 activeRelease,activeVersion 保留字符串 0 的兼容语义。校验要检查 valid,样例要检查 completed/failed,删除要检查 deleted;发布后按回执的 activeRelease 筛选 runs.list 才能确认该版本运行。样例 writePreview 不代表线上数据写入。
|
|
42
42
|
|
|
43
|
-
UnsV2OutcomeUnknownError
|
|
43
|
+
UnsV2OutcomeUnknownError、发布超时或取消后保留原路由、载荷和幂等键;允许恢复时原样重发取得原回执,不用新键重复发布。历史回执可能缺少 activeRelease,不得用当前 head 替代。版本冲突先比较当前草稿,不能忽略 expectedRevision。不同写入意图使用不同键;本地 SDK 测试不能代替目标环境联调。
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
# 创建 Stream
|
|
2
2
|
|
|
3
|
-
方法:`stream.
|
|
3
|
+
方法:`stream.documents.create({program})`;POST `/openapi/v2/uns/streams/documents/create`。服务端编译并校验程序,回执包含完整草稿和修订,可直接用于下一步发布。
|
|
4
4
|
|
|
5
5
|
| 字段 | 类型 | 必填 | 说明 |
|
|
6
6
|
| --- | --- | --- | --- |
|
|
7
7
|
| dryRun | boolean | 是 | true 预检,false 保存草稿 |
|
|
8
|
-
|
|
|
9
|
-
|
|
|
10
|
-
|
|
|
11
|
-
|
|
|
12
|
-
|
|
|
8
|
+
| program.name | string | 是 | 1–63 字符 |
|
|
9
|
+
| program.sdkVersion | tier0-stream/v2 | 是 | 运行时脚本版本 |
|
|
10
|
+
| program.script | string | 是 | 完整 module.exports 程序 |
|
|
11
|
+
| program.timeoutMs | integer | 否 | 当前云端上限 30000 ms |
|
|
12
|
+
| program.sourceBindings | array | 否 | sourceId、configRevision、acquisition |
|
|
13
13
|
| idempotencyKey | string | 否 | 最多 128 字符,本次创建的固定意图键 |
|
|
14
14
|
|
|
15
15
|
## HTTP Source 绑定示例
|
|
@@ -39,18 +39,18 @@ export async function createHttpStream(
|
|
|
39
39
|
'};',
|
|
40
40
|
].join('\n'),
|
|
41
41
|
};
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
const saved = await client.stream.programs.create({
|
|
45
|
-
dryRun: false, document, idempotencyKey: intentKey,
|
|
42
|
+
const saved = await client.stream.documents.create({
|
|
43
|
+
dryRun: false, program: document, idempotencyKey: intentKey,
|
|
46
44
|
});
|
|
47
|
-
if (
|
|
48
|
-
return { receipt: saved, current:
|
|
45
|
+
if (!('id' in saved.data)) throw new Error('创建未返回已保存的草稿');
|
|
46
|
+
return { receipt: saved, current: saved.data };
|
|
49
47
|
}
|
|
50
48
|
```
|
|
51
49
|
|
|
52
50
|
将 path="orders"、60 秒周期改为业务实际值。示例保存草稿,不部署,也不将返回值自动写入 UNS;需要写入集合时按执行器能力定义 api.uns 操作。resources=[] 是空冻结资源集,不是通配授权。源地址和凭据留在 Source,不能拷进 acquisition 或脚本。
|
|
53
51
|
|
|
54
|
-
创建 data
|
|
52
|
+
创建 data 为 CloudStreamDocumentView 或预检结果,实际保存返回 id、revision 和完整 document;预检不生成 ID。需要展示预检时单独调用 documents.validate({program});普通保存无需先校验再保存。当前后端创建/校验需要注册 Worker 可用。TEMPORARILY_UNAVAILABLE 不代表脚本一定错误;不要自动改用旧 Flow。
|
|
53
|
+
|
|
54
|
+
兼容入口 programs.create({document}) 仍返回含 id、revision、activeVersion、executionState 的程序摘要回执,可直接使用 revision。恢复旧请求必须保留原路由、原载荷和原幂等键,不能改投 documents.create。
|
|
55
55
|
|
|
56
56
|
原生编辑器形态 `documents.create({dryRun, document})` 的 document 为 name/model/script对象/sources/entries,script 为 `{sdkVersion,code,timeoutMs}`,sources 用 alias/sourceId/configRevision/resources/acquisition。不要把上面的 script 字符串直接塞入原生文档。
|
|
@@ -10,24 +10,25 @@
|
|
|
10
10
|
全部写操作使用各自的 idempotencyKey。expectedSourceRevision 只适用于恰好一个 Source 依赖,且同时保护草稿冻结修订与当前 Source head;多源不能塞入单个修订值。原生文档没有 documents.deploy,当前云端 program 模型发布走 programs.deploy。
|
|
11
11
|
|
|
12
12
|
```ts
|
|
13
|
-
import { createClient, type Tier0Client } from '@tier0/sdk-v2';
|
|
13
|
+
import { createClient, type Tier0Client, type CloudStreamDocumentView } from '@tier0/sdk-v2';
|
|
14
14
|
|
|
15
|
-
export async function deployStream(
|
|
16
|
-
const { data: current } = await client.stream.documents.get({ id, view: 'full' });
|
|
17
|
-
if (!('document' in current)) throw new Error('需要完整 Stream 文档');
|
|
15
|
+
export async function deployStream(current: CloudStreamDocumentView, intentKey: string, client: Tier0Client = createClient()) {
|
|
18
16
|
const sources = current.document.sources ?? [];
|
|
19
17
|
const request = {
|
|
20
|
-
id, expectedRevision: current.revision,
|
|
18
|
+
id: current.id, expectedRevision: current.revision,
|
|
21
19
|
...(sources.length === 1 ? { expectedSourceRevision: sources[0]!.configRevision } : {}),
|
|
22
20
|
};
|
|
23
21
|
const receipt = await client.stream.programs.deploy({
|
|
24
22
|
...request, dryRun: false, idempotencyKey: intentKey,
|
|
25
23
|
});
|
|
26
|
-
if (
|
|
24
|
+
if (!('id' in receipt.data) || receipt.data.activeVersion === '0') {
|
|
27
25
|
throw new Error('未确认发布版本');
|
|
28
26
|
}
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
const release = receipt.data.activeRelease;
|
|
28
|
+
if (release === undefined || BigInt(String(release)) <= 0n) {
|
|
29
|
+
throw new Error('历史回执缺少发布编号;保留原请求,不用当前发布编号替代');
|
|
30
|
+
}
|
|
31
|
+
return { receipt, release };
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
export async function disableStream(id: string, intentKey: string, client: Tier0Client = createClient()) {
|
|
@@ -43,7 +44,7 @@ export async function disableStream(id: string, intentKey: string, client: Tier0
|
|
|
43
44
|
}
|
|
44
45
|
```
|
|
45
46
|
|
|
46
|
-
|
|
47
|
+
将创建或更新回执的 current 传给 deployStream,只发一次发布请求。只有手上没有草稿快照时才先 documents.get。programs 回执的 activeRelease 是这次写入事务的正整数发布序号;无活跃发布或历史回执未记录编号时省略。使用 runs.list 并筛选 String(run.release) === String(release) 才能观察这次发布,观察窗口汇总不能证明该版本运行。发布成功不表示外部连接健康或执行成功。停用保留草稿。仅修改草稿的任务不额外发布/停用。
|
|
47
48
|
|
|
48
49
|
## 回滚到明确版本
|
|
49
50
|
|
|
@@ -61,4 +62,4 @@ export async function rollbackStream(
|
|
|
61
62
|
}
|
|
62
63
|
```
|
|
63
64
|
|
|
64
|
-
检查回执 activeVersion
|
|
65
|
+
检查回执 activeVersion 与 activeRelease。超时或 UnsV2OutcomeUnknownError 时保留原路由、载荷和幂等键;允许恢复时原样重发以取得原回执,不用新键再次发布。旧回执可能没有 activeRelease,不能读取当前 head 后冒充旧请求的发布编号。相同草稿使用不同键仍会创建新发布,expectedRevision 仅保护草稿修订。预览用 dryRun=true,返回 validated/sideEffects,不保证稍后的实际发布一定成功。
|
|
@@ -21,16 +21,14 @@ export async function updateStreamDescription(
|
|
|
21
21
|
const { data: current } = await client.stream.documents.get({ id, view: 'full' });
|
|
22
22
|
if (!('document' in current)) throw new Error('需要完整 Stream 文档');
|
|
23
23
|
const document: CloudStreamDocument = { ...current.document, description };
|
|
24
|
-
const checked = await client.stream.documents.validate({ document });
|
|
25
|
-
if (!checked.data.valid) throw new Error('Stream 文档校验失败,请检查 issues');
|
|
26
24
|
const saved = await client.stream.documents.update({
|
|
27
25
|
id, expectedRevision: current.revision, document, dryRun: false, idempotencyKey: intentKey,
|
|
28
26
|
});
|
|
29
27
|
if (!('id' in saved.data)) throw new Error('未返回已保存文档');
|
|
30
|
-
return { receipt: saved, current:
|
|
28
|
+
return { receipt: saved, current: saved.data };
|
|
31
29
|
}
|
|
32
30
|
```
|
|
33
31
|
|
|
34
|
-
|
|
32
|
+
代码应用可直接用 documents.update({id, expectedRevision, program, dryRun:false, idempotencyKey}),由服务端编译入口和绑定。编辑完整文档时保留 sdkVersion/timeoutMs,并同步 entries/sources;写入本身执行校验,只有需要提前展示问题时才单独 validate。不要对脱敏 programs.get.document 做展开更新。
|
|
35
33
|
|
|
36
|
-
响应是完整 CloudStreamDocumentView
|
|
34
|
+
响应是完整 CloudStreamDocumentView 或预检结果;把 saved.data 直接传给下一步[发布示例](deploy.md),无需为了 revision 再读取。已有当前完整文档时可省去示例最初的 get。保存草稿不会自动部署,activeDocument 可能仍是旧发布版本。VERSION_CONFLICT 后重新读取、比较并决策;不无条件覆盖。
|
|
@@ -34,7 +34,9 @@ export async function createDeviceCollection(
|
|
|
34
34
|
}
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
单条新增默认返回 insertedId/status/replayed。需要创建后的数据、revision 和时间戳时,传 returnDocument:'after',同一次写入返回 value,无需再查询;可配合 projection,调用者必须同时具有读取权限。幂等重放返回原始创建快照,后续读取最新状态仍用 recordGet/recordFind。dryRun 的 value 是未提交预览。
|
|
38
|
+
|
|
39
|
+
批量新增返回 insertedCount/insertedIds 及适用的逐项错误。insertedIds 的键是原始输入从 0 开始的索引。省略自定义 ID 时从成功回执取得实际 ID,不能把名称当成 ID。
|
|
38
40
|
|
|
39
41
|
```ts
|
|
40
42
|
import { createClient, type Tier0Client } from '@tier0/sdk-v2';
|
|
@@ -44,12 +46,11 @@ export async function insertDevice(
|
|
|
44
46
|
client: Tier0Client = createClient(),
|
|
45
47
|
) {
|
|
46
48
|
const receipt = await client.uns.recordInsertOne({
|
|
47
|
-
collectionId, id, data: { name, enabled: true }, dryRun: false, idempotencyKey: intentKey,
|
|
49
|
+
collectionId, id, data: { name, enabled: true }, dryRun: false, idempotencyKey: intentKey, returnDocument: 'after',
|
|
48
50
|
});
|
|
49
51
|
if (receipt.data.status !== 'committed') return { state: 'unconfirmed' as const, receipt };
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
return { state: 'committed' as const, receipt, current: found.data.items[0] };
|
|
52
|
+
if (!receipt.data.value) throw new Error('创建回执缺少请求的快照');
|
|
53
|
+
return { state: 'committed' as const, receipt, created: receipt.data.value };
|
|
53
54
|
}
|
|
54
55
|
```
|
|
55
56
|
|