@tier0/sdk-v2 0.1.0 → 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/LICENSE +21 -21
- package/README.md +13 -11
- package/dist/cjs/app-identity-contract.generated.js.map +1 -1
- package/dist/cjs/capabilities.js.map +1 -1
- package/dist/cjs/cloud-server.js.map +1 -1
- package/dist/cjs/cloud-transport.d.ts +21 -0
- package/dist/cjs/cloud-transport.js +81 -1
- package/dist/cjs/cloud-transport.js.map +1 -1
- package/dist/cjs/controls.js.map +1 -1
- package/dist/cjs/data/bootstrap.d.ts +4 -0
- package/dist/cjs/data/bootstrap.js +15 -6
- package/dist/cjs/data/bootstrap.js.map +1 -1
- package/dist/cjs/data/call.d.ts +26 -9
- package/dist/cjs/data/call.js +179 -14
- package/dist/cjs/data/call.js.map +1 -1
- package/dist/cjs/data/client.d.ts +44 -2
- package/dist/cjs/data/client.js +87 -4
- package/dist/cjs/data/client.js.map +1 -1
- package/dist/cjs/data/collection.d.ts +151 -14
- package/dist/cjs/data/collection.js +213 -50
- package/dist/cjs/data/collection.js.map +1 -1
- package/dist/cjs/data/context.d.ts +4 -2
- package/dist/cjs/data/context.js +14 -3
- package/dist/cjs/data/context.js.map +1 -1
- package/dist/cjs/data/define.d.ts +9 -1
- package/dist/cjs/data/define.js.map +1 -1
- package/dist/cjs/data/error.d.ts +30 -12
- package/dist/cjs/data/error.js +61 -63
- package/dist/cjs/data/error.js.map +1 -1
- package/dist/cjs/data/index.d.ts +6 -3
- package/dist/cjs/data/index.js +7 -1
- package/dist/cjs/data/index.js.map +1 -1
- package/dist/cjs/data/schema.d.ts +7 -1
- package/dist/cjs/data/schema.js +54 -4
- package/dist/cjs/data/schema.js.map +1 -1
- package/dist/cjs/data/subscribe.d.ts +108 -0
- package/dist/cjs/data/subscribe.js +270 -0
- package/dist/cjs/data/subscribe.js.map +1 -0
- package/dist/cjs/mq/broker.js.map +1 -1
- package/dist/cjs/mq/client.js.map +1 -1
- package/dist/cjs/mq/index.js.map +1 -1
- package/dist/cjs/mq/types.js.map +1 -1
- package/dist/cjs/runtime-context.js.map +1 -1
- package/dist/cjs/runtime-env.js.map +1 -1
- package/dist/cjs/uns-v2/client.d.ts +90 -12
- package/dist/cjs/uns-v2/client.js +238 -63
- package/dist/cjs/uns-v2/client.js.map +1 -1
- package/dist/cjs/uns-v2/codec.js.map +1 -1
- package/dist/cjs/uns-v2/generated.d.ts +9619 -6923
- package/dist/cjs/uns-v2/generated.js +124 -119
- package/dist/cjs/uns-v2/generated.js.map +1 -1
- package/dist/cjs/uns-v2/index.js.map +1 -1
- package/dist/cjs/uns-v2/sse.d.ts +20 -0
- package/dist/cjs/uns-v2/sse.js +65 -0
- package/dist/cjs/uns-v2/sse.js.map +1 -0
- package/dist/esm/app-identity-contract.generated.js.map +1 -1
- package/dist/esm/capabilities.js.map +1 -1
- package/dist/esm/cloud-server.js.map +1 -1
- package/dist/esm/cloud-transport.d.ts +21 -0
- package/dist/esm/cloud-transport.js +81 -1
- package/dist/esm/cloud-transport.js.map +1 -1
- package/dist/esm/controls.js.map +1 -1
- package/dist/esm/data/bootstrap.d.ts +4 -0
- package/dist/esm/data/bootstrap.js +14 -5
- package/dist/esm/data/bootstrap.js.map +1 -1
- package/dist/esm/data/call.d.ts +26 -9
- package/dist/esm/data/call.js +177 -13
- package/dist/esm/data/call.js.map +1 -1
- package/dist/esm/data/client.d.ts +44 -2
- package/dist/esm/data/client.js +89 -7
- package/dist/esm/data/client.js.map +1 -1
- package/dist/esm/data/collection.d.ts +151 -14
- package/dist/esm/data/collection.js +212 -49
- package/dist/esm/data/collection.js.map +1 -1
- package/dist/esm/data/context.d.ts +4 -2
- package/dist/esm/data/context.js +13 -3
- package/dist/esm/data/context.js.map +1 -1
- package/dist/esm/data/define.d.ts +9 -1
- package/dist/esm/data/define.js.map +1 -1
- package/dist/esm/data/error.d.ts +30 -12
- package/dist/esm/data/error.js +61 -63
- package/dist/esm/data/error.js.map +1 -1
- package/dist/esm/data/index.d.ts +6 -3
- package/dist/esm/data/index.js +3 -1
- package/dist/esm/data/index.js.map +1 -1
- package/dist/esm/data/schema.d.ts +7 -1
- package/dist/esm/data/schema.js +54 -4
- package/dist/esm/data/schema.js.map +1 -1
- package/dist/esm/data/subscribe.d.ts +108 -0
- package/dist/esm/data/subscribe.js +265 -0
- package/dist/esm/data/subscribe.js.map +1 -0
- package/dist/esm/mq/broker.js.map +1 -1
- package/dist/esm/mq/client.js.map +1 -1
- package/dist/esm/mq/index.js.map +1 -1
- package/dist/esm/mq/types.js.map +1 -1
- package/dist/esm/runtime-context.js.map +1 -1
- package/dist/esm/runtime-env.js.map +1 -1
- package/dist/esm/uns-v2/client.d.ts +90 -12
- package/dist/esm/uns-v2/client.js +236 -63
- package/dist/esm/uns-v2/client.js.map +1 -1
- package/dist/esm/uns-v2/codec.js.map +1 -1
- package/dist/esm/uns-v2/generated.d.ts +9619 -6923
- package/dist/esm/uns-v2/generated.js +123 -118
- package/dist/esm/uns-v2/generated.js.map +1 -1
- package/dist/esm/uns-v2/index.js.map +1 -1
- package/dist/esm/uns-v2/sse.d.ts +20 -0
- package/dist/esm/uns-v2/sse.js +60 -0
- package/dist/esm/uns-v2/sse.js.map +1 -0
- package/package.json +29 -16
- package/skills/tier0-sdk-v2/SKILL.md +14 -14
- package/skills/{tier0-sdk-v2-data → tier0-sdk-v2/tier0-sdk-v2-data}/SKILL.md +3 -2
- package/skills/{tier0-sdk-v2-data → tier0-sdk-v2/tier0-sdk-v2-data}/references/consistency.md +3 -3
- package/skills/{tier0-sdk-v2-data → tier0-sdk-v2/tier0-sdk-v2-data}/references/errors.md +23 -10
- package/skills/{tier0-sdk-v2-data → tier0-sdk-v2/tier0-sdk-v2-data}/references/modeling.md +36 -0
- package/skills/tier0-sdk-v2/tier0-sdk-v2-data/references/read-write.md +172 -0
- package/skills/{tier0-sdk-v2-mq → tier0-sdk-v2/tier0-sdk-v2-mq}/references/quickstart.md +1 -1
- package/skills/{tier0-sdk-v2-source → tier0-sdk-v2/tier0-sdk-v2-source}/SKILL.md +3 -3
- package/skills/{tier0-sdk-v2-source → tier0-sdk-v2/tier0-sdk-v2-source}/references/create.md +3 -6
- package/skills/tier0-sdk-v2/tier0-sdk-v2-source/references/delete.md +39 -0
- package/skills/tier0-sdk-v2/tier0-sdk-v2-source/references/state.md +36 -0
- package/skills/tier0-sdk-v2/tier0-sdk-v2-source/references/update.md +80 -0
- package/skills/{tier0-sdk-v2-stream → tier0-sdk-v2/tier0-sdk-v2-stream}/SKILL.md +6 -6
- package/skills/{tier0-sdk-v2-stream → tier0-sdk-v2/tier0-sdk-v2-stream}/references/create.md +15 -14
- package/skills/{tier0-sdk-v2-stream → tier0-sdk-v2/tier0-sdk-v2-stream}/references/deploy.md +11 -9
- package/skills/{tier0-sdk-v2-stream → tier0-sdk-v2/tier0-sdk-v2-stream}/references/lifecycle.md +2 -1
- package/skills/{tier0-sdk-v2-stream → tier0-sdk-v2/tier0-sdk-v2-stream}/references/update.md +5 -6
- package/skills/tier0-sdk-v2/tier0-sdk-v2-uns/SKILL.md +55 -0
- package/skills/tier0-sdk-v2/tier0-sdk-v2-uns/references/create.md +57 -0
- package/skills/{tier0-sdk-v2-uns → tier0-sdk-v2/tier0-sdk-v2-uns}/references/data-workflows.md +3 -7
- package/skills/tier0-sdk-v2/tier0-sdk-v2-uns/references/delete.md +53 -0
- package/skills/tier0-sdk-v2/tier0-sdk-v2-uns/references/namespace.md +38 -0
- package/skills/tier0-sdk-v2/tier0-sdk-v2-uns/references/read.md +34 -0
- package/skills/tier0-sdk-v2/tier0-sdk-v2-uns/references/restore.md +39 -0
- package/skills/tier0-sdk-v2/tier0-sdk-v2-uns/references/update.md +62 -0
- package/skills/tier0-sdk-v2-data/references/read-write.md +0 -72
- package/skills/tier0-sdk-v2-source/references/delete.md +0 -47
- package/skills/tier0-sdk-v2-source/references/state.md +0 -37
- package/skills/tier0-sdk-v2-source/references/update.md +0 -61
- package/skills/tier0-sdk-v2-uns/SKILL.md +0 -53
- package/skills/tier0-sdk-v2-uns/references/create.md +0 -85
- package/skills/tier0-sdk-v2-uns/references/delete.md +0 -69
- package/skills/tier0-sdk-v2-uns/references/namespace.md +0 -75
- package/skills/tier0-sdk-v2-uns/references/read.md +0 -32
- package/skills/tier0-sdk-v2-uns/references/restore.md +0 -41
- package/skills/tier0-sdk-v2-uns/references/update.md +0 -86
- /package/skills/{tier0-sdk-v2-agent → tier0-sdk-v2/tier0-sdk-v2-agent}/SKILL.md +0 -0
- /package/skills/{tier0-sdk-v2-agent → tier0-sdk-v2/tier0-sdk-v2-agent}/references/results.md +0 -0
- /package/skills/{tier0-sdk-v2-controls → tier0-sdk-v2/tier0-sdk-v2-controls}/SKILL.md +0 -0
- /package/skills/{tier0-sdk-v2-data → tier0-sdk-v2/tier0-sdk-v2-data}/references/schema-evolution.md +0 -0
- /package/skills/{tier0-sdk-v2-files → tier0-sdk-v2/tier0-sdk-v2-files}/SKILL.md +0 -0
- /package/skills/{tier0-sdk-v2-files → tier0-sdk-v2/tier0-sdk-v2-files}/references/delete.md +0 -0
- /package/skills/{tier0-sdk-v2-files → tier0-sdk-v2/tier0-sdk-v2-files}/references/download.md +0 -0
- /package/skills/{tier0-sdk-v2-files → tier0-sdk-v2/tier0-sdk-v2-files}/references/multipart.md +0 -0
- /package/skills/{tier0-sdk-v2-files → tier0-sdk-v2/tier0-sdk-v2-files}/references/upload.md +0 -0
- /package/skills/{tier0-sdk-v2-files → tier0-sdk-v2/tier0-sdk-v2-files}/references/url.md +0 -0
- /package/skills/{tier0-sdk-v2-members → tier0-sdk-v2/tier0-sdk-v2-members}/SKILL.md +0 -0
- /package/skills/{tier0-sdk-v2-members → tier0-sdk-v2/tier0-sdk-v2-members}/references/app-members.md +0 -0
- /package/skills/{tier0-sdk-v2-members → tier0-sdk-v2/tier0-sdk-v2-members}/references/platform-members.md +0 -0
- /package/skills/{tier0-sdk-v2-mq → tier0-sdk-v2/tier0-sdk-v2-mq}/SKILL.md +0 -0
- /package/skills/{tier0-sdk-v2-notifications → tier0-sdk-v2/tier0-sdk-v2-notifications}/SKILL.md +0 -0
- /package/skills/{tier0-sdk-v2-notifications → tier0-sdk-v2/tier0-sdk-v2-notifications}/references/get.md +0 -0
- /package/skills/{tier0-sdk-v2-notifications → tier0-sdk-v2/tier0-sdk-v2-notifications}/references/send.md +0 -0
- /package/skills/{tier0-sdk-v2-source → tier0-sdk-v2/tier0-sdk-v2-source}/references/changes.md +0 -0
- /package/skills/{tier0-sdk-v2-source → tier0-sdk-v2/tier0-sdk-v2-source}/references/protocols.md +0 -0
- /package/skills/{tier0-sdk-v2-source → tier0-sdk-v2/tier0-sdk-v2-source}/references/read.md +0 -0
- /package/skills/{tier0-sdk-v2-stream → tier0-sdk-v2/tier0-sdk-v2-stream}/references/delete.md +0 -0
- /package/skills/{tier0-sdk-v2-stream → tier0-sdk-v2/tier0-sdk-v2-stream}/references/read.md +0 -0
- /package/skills/{tier0-sdk-v2-system → tier0-sdk-v2/tier0-sdk-v2-system}/SKILL.md +0 -0
- /package/skills/{tier0-sdk-v2-system → tier0-sdk-v2/tier0-sdk-v2-system}/references/info.md +0 -0
- /package/skills/{tier0-sdk-v2-system → tier0-sdk-v2/tier0-sdk-v2-system}/references/whoami.md +0 -0
|
@@ -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
|
|
package/skills/{tier0-sdk-v2-source → tier0-sdk-v2/tier0-sdk-v2-source}/references/create.md
RENAMED
|
@@ -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
|
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# 删除 Source
|
|
2
|
+
|
|
3
|
+
方法:`source.delete`;POST `/openapi/v2/uns/sources/delete`。
|
|
4
|
+
|
|
5
|
+
| 字段 | 类型 | 必填 | 说明 |
|
|
6
|
+
| --- | --- | --- | --- |
|
|
7
|
+
| dryRun | boolean | 是 | true 预检;false 提交 |
|
|
8
|
+
| items | array | 是 | 1–200 项 |
|
|
9
|
+
| items[].id | string | 是 | 正十进制 int64 字符串 |
|
|
10
|
+
| items[].expectedRevision | integer | 是 | 已知的当前修订 |
|
|
11
|
+
| items[].confirmedImpactRevision | string | 有引用时 | action=delete 的影响回执 |
|
|
12
|
+
| mode | auto/sync | 否 | 不支持 async |
|
|
13
|
+
| allowPartial | false | 否 | 不支持部分提交 |
|
|
14
|
+
| idempotencyKey | string | 否 | 最多 128 字符 |
|
|
15
|
+
|
|
16
|
+
Source 必须已停用。没有任何 Stream 引用时,直接用已有 ID 和 revision 提交;有引用时先 impact(action=delete) 并[遍历和评估影响](changes.md),再显式传入回执。服务端在同一事务中检查依赖和修订,不自动删除或停用依赖流。
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
import { createClient, type Tier0Client, type UnsV2Request } from '@tier0/sdk-v2';
|
|
20
|
+
|
|
21
|
+
export async function deleteSource(
|
|
22
|
+
id: string, expectedRevision: UnsV2Request<'sourceImpact'>['expectedRevision'],
|
|
23
|
+
intentKey: string, client: Tier0Client = createClient(), confirmedImpactRevision?: string,
|
|
24
|
+
) {
|
|
25
|
+
const receipt = await client.source.delete({
|
|
26
|
+
dryRun: false, mode: 'sync', allowPartial: false, idempotencyKey: intentKey,
|
|
27
|
+
items: [{ id, expectedRevision,
|
|
28
|
+
...(confirmedImpactRevision === undefined ? {} : { confirmedImpactRevision }) }],
|
|
29
|
+
});
|
|
30
|
+
if (receipt.data.status !== 'committed' || receipt.data.results?.some(item => item.error)) {
|
|
31
|
+
return { state: 'unconfirmed' as const, receipt };
|
|
32
|
+
}
|
|
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 };
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
返回 Outcome,HTTP 200/validated/accepted 不等于完成。已提交回执中对应 ID 的 deleted 结果足以确认归档,无需遍历归档列表;不能把任意 get 异常当成成功。传输结果未知时保留原请求、requestId 和意图键,不自动重试。补充确认回执是新请求,应使用新幂等键。
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# 启用、停用 Source
|
|
2
|
+
|
|
3
|
+
方法:`source.state.update`;POST `/openapi/v2/uns/sources/state/update`。
|
|
4
|
+
|
|
5
|
+
| 字段 | 类型 | 必填 | 说明 |
|
|
6
|
+
| --- | --- | --- | --- |
|
|
7
|
+
| dryRun | boolean | 是 | 提交 false |
|
|
8
|
+
| items[].id / expectedRevision | string / integer | 是 | 当前对象和已知修订 |
|
|
9
|
+
| items[].desiredState | enabled/disabled | 是 | 目标状态 |
|
|
10
|
+
| items[].confirmedImpactRevision | string | 有引用时 | enable/disable 对应的影响回执 |
|
|
11
|
+
| mode / allowPartial | string / boolean | 否 | 单项示例 sync / false |
|
|
12
|
+
| idempotencyKey | string | 否 | 本次状态变更的独立意图键 |
|
|
13
|
+
|
|
14
|
+
没有任何 Stream 引用时,一次请求即可提交期望状态。有引用时先以相同修订执行 impact(action=enable 或 disable),按[影响分析](changes.md)遍历并评估,再显式传入回执。服务端在同一事务中检查引用和修订,不会因未传回执而自动同意影响。
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import { createClient, type Tier0Client, type UnsV2Request } from '@tier0/sdk-v2';
|
|
18
|
+
|
|
19
|
+
export async function setSourceState(
|
|
20
|
+
id: string, expectedRevision: UnsV2Request<'sourceImpact'>['expectedRevision'],
|
|
21
|
+
desiredState: 'enabled' | 'disabled', intentKey: string,
|
|
22
|
+
client: Tier0Client = createClient(), confirmedImpactRevision?: string,
|
|
23
|
+
) {
|
|
24
|
+
const receipt = await client.source.state.update({
|
|
25
|
+
dryRun: false, mode: 'sync', allowPartial: false, idempotencyKey: intentKey,
|
|
26
|
+
items: [{ id, expectedRevision, desiredState,
|
|
27
|
+
...(confirmedImpactRevision === undefined ? {} : { confirmedImpactRevision }) }],
|
|
28
|
+
});
|
|
29
|
+
if (receipt.data.status !== 'committed' || receipt.data.results?.some(item => item.error)) {
|
|
30
|
+
return { state: 'unconfirmed' as const, receipt };
|
|
31
|
+
}
|
|
32
|
+
return { state: 'committed' as const, receipt };
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
响应为 Outcome;committed 且无项错误即可确认期望状态已提交,无需额外 get。accepted/jobId 仅表示受理,继续查询作业。enabled 是期望状态;健康与连通性另用 observations/runtime 检查。提供的回执始终严格校验,确认过期或依赖变化不能自动刷新同意。
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# 修改 Source
|
|
2
|
+
|
|
3
|
+
方法:`source.documents.update`;POST `/openapi/v2/uns/sources/documents/update`。
|
|
4
|
+
|
|
5
|
+
| 字段 | 类型 | 必填 | 说明 |
|
|
6
|
+
| --- | --- | --- | --- |
|
|
7
|
+
| id | string | 是 | 已知的 Source ID |
|
|
8
|
+
| patch / document | object / CloudSourceDocument | 二选一 | patch 保留未提供的字段;document 完整替换 |
|
|
9
|
+
| expectedRevision | integer | 是 | 已读取或上次提交返回的 revision,原样传回 |
|
|
10
|
+
| confirmedImpactRevision | string | 有引用时 | 相同修改意图和修订的 impact 回执 |
|
|
11
|
+
| dryRun | boolean | 是 | true 预检;false 提交 |
|
|
12
|
+
| idempotencyKey | string | 否 | 最多 128 字符 |
|
|
13
|
+
|
|
14
|
+
## 示例:一次请求修改名称
|
|
15
|
+
|
|
16
|
+
已有 ID 和 revision 时直接提交 patch。服务端在同一事务和依赖锁下检查引用并修改:没有任何草稿、活动或历史 Stream 引用时无需 impact。patch 中未提供的字段保留原值,config 如果提供则整体替换;完整 document 更新需先取得完整文档并保留未改配置和认证引用。
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
import { createClient, type Tier0Client, type UnsV2Request } from '@tier0/sdk-v2';
|
|
20
|
+
|
|
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
|
+
) {
|
|
46
|
+
const intent: UnsV2Request<'sourceImpact'> = {
|
|
47
|
+
id, expectedRevision, action: 'update', patch: { name },
|
|
48
|
+
};
|
|
49
|
+
const impact = await client.source.impact({ ...intent, offset: 0, size: 200 });
|
|
50
|
+
const references = [...impact.data.references];
|
|
51
|
+
let offset = impact.data.nextOffset;
|
|
52
|
+
const seen = new Set<string>();
|
|
53
|
+
while (String(offset) !== '0') {
|
|
54
|
+
if (seen.has(String(offset))) throw new Error('影响分页未前进');
|
|
55
|
+
seen.add(String(offset));
|
|
56
|
+
const next = await client.source.impact({
|
|
57
|
+
...intent, offset, size: 200, impactRevision: impact.data.impactRevision,
|
|
58
|
+
});
|
|
59
|
+
if (next.data.impactRevision !== impact.data.impactRevision) throw new Error('影响确认值发生变化');
|
|
60
|
+
references.push(...next.data.references);
|
|
61
|
+
offset = next.data.nextOffset;
|
|
62
|
+
}
|
|
63
|
+
return { intent, impact: { ...impact.data, references, nextOffset: offset } };
|
|
64
|
+
}
|
|
65
|
+
export async function commitSourceRename(
|
|
66
|
+
plan: { intent: UnsV2Request<'sourceImpact'>; impact: { impactRevision: string } },
|
|
67
|
+
intentKey: string, client: Tier0Client = createClient(),
|
|
68
|
+
) {
|
|
69
|
+
const { id, expectedRevision, patch, action } = plan.intent;
|
|
70
|
+
if (action !== 'update' || !patch) throw new Error('需要修改意图');
|
|
71
|
+
const saved = await client.source.documents.update({
|
|
72
|
+
id, expectedRevision, patch, confirmedImpactRevision: plan.impact.impactRevision,
|
|
73
|
+
dryRun: false, idempotencyKey: intentKey,
|
|
74
|
+
});
|
|
75
|
+
if (!('id' in saved.data)) throw new Error('未收到更新提交结果');
|
|
76
|
+
return { receipt: saved, current: saved.data };
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
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 测试不能代替目标环境联调。
|
package/skills/{tier0-sdk-v2-stream → tier0-sdk-v2/tier0-sdk-v2-stream}/references/create.md
RENAMED
|
@@ -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 绑定示例
|
|
@@ -23,7 +23,8 @@ export async function createHttpStream(
|
|
|
23
23
|
sourceId: string, name: string, intentKey: string, client: Tier0Client = createClient(),
|
|
24
24
|
) {
|
|
25
25
|
if (!/^[1-9][0-9]*$/.test(sourceId)) throw new Error('Source ID 必须是正十进制字符串');
|
|
26
|
-
const source = await client.source.documents.get({ id: sourceId });
|
|
26
|
+
const source = await client.source.documents.get({ id: sourceId, view: 'full' });
|
|
27
|
+
if (!('document' in source.data)) throw new Error('需要完整 Source 文档');
|
|
27
28
|
if (source.data.document.protocol !== 'http') throw new Error('此示例需要 HTTP Source');
|
|
28
29
|
const document: RuntimeStreamDefinition = {
|
|
29
30
|
name, sdkVersion: 'tier0-stream/v2', timeoutMs: 5000,
|
|
@@ -38,18 +39,18 @@ export async function createHttpStream(
|
|
|
38
39
|
'};',
|
|
39
40
|
].join('\n'),
|
|
40
41
|
};
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
const saved = await client.stream.programs.create({
|
|
44
|
-
dryRun: false, document, idempotencyKey: intentKey,
|
|
42
|
+
const saved = await client.stream.documents.create({
|
|
43
|
+
dryRun: false, program: document, idempotencyKey: intentKey,
|
|
45
44
|
});
|
|
46
|
-
if (
|
|
47
|
-
return { receipt: saved, current:
|
|
45
|
+
if (!('id' in saved.data)) throw new Error('创建未返回已保存的草稿');
|
|
46
|
+
return { receipt: saved, current: saved.data };
|
|
48
47
|
}
|
|
49
48
|
```
|
|
50
49
|
|
|
51
50
|
将 path="orders"、60 秒周期改为业务实际值。示例保存草稿,不部署,也不将返回值自动写入 UNS;需要写入集合时按执行器能力定义 api.uns 操作。resources=[] 是空冻结资源集,不是通配授权。源地址和凭据留在 Source,不能拷进 acquisition 或脚本。
|
|
52
51
|
|
|
53
|
-
创建 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。
|
|
54
55
|
|
|
55
56
|
原生编辑器形态 `documents.create({dryRun, document})` 的 document 为 name/model/script对象/sources/entries,script 为 `{sdkVersion,code,timeoutMs}`,sources 用 alias/sourceId/configRevision/resources/acquisition。不要把上面的 script 字符串直接塞入原生文档。
|
package/skills/{tier0-sdk-v2-stream → tier0-sdk-v2/tier0-sdk-v2-stream}/references/deploy.md
RENAMED
|
@@ -10,23 +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 });
|
|
15
|
+
export async function deployStream(current: CloudStreamDocumentView, intentKey: string, client: Tier0Client = createClient()) {
|
|
17
16
|
const sources = current.document.sources ?? [];
|
|
18
17
|
const request = {
|
|
19
|
-
id, expectedRevision: current.revision,
|
|
18
|
+
id: current.id, expectedRevision: current.revision,
|
|
20
19
|
...(sources.length === 1 ? { expectedSourceRevision: sources[0]!.configRevision } : {}),
|
|
21
20
|
};
|
|
22
21
|
const receipt = await client.stream.programs.deploy({
|
|
23
22
|
...request, dryRun: false, idempotencyKey: intentKey,
|
|
24
23
|
});
|
|
25
|
-
if (
|
|
24
|
+
if (!('id' in receipt.data) || receipt.data.activeVersion === '0') {
|
|
26
25
|
throw new Error('未确认发布版本');
|
|
27
26
|
}
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
const release = receipt.data.activeRelease;
|
|
28
|
+
if (release === undefined || BigInt(String(release)) <= 0n) {
|
|
29
|
+
throw new Error('历史回执缺少发布编号;保留原请求,不用当前发布编号替代');
|
|
30
|
+
}
|
|
31
|
+
return { receipt, release };
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
export async function disableStream(id: string, intentKey: string, client: Tier0Client = createClient()) {
|
|
@@ -42,7 +44,7 @@ export async function disableStream(id: string, intentKey: string, client: Tier0
|
|
|
42
44
|
}
|
|
43
45
|
```
|
|
44
46
|
|
|
45
|
-
|
|
47
|
+
将创建或更新回执的 current 传给 deployStream,只发一次发布请求。只有手上没有草稿快照时才先 documents.get。programs 回执的 activeRelease 是这次写入事务的正整数发布序号;无活跃发布或历史回执未记录编号时省略。使用 runs.list 并筛选 String(run.release) === String(release) 才能观察这次发布,观察窗口汇总不能证明该版本运行。发布成功不表示外部连接健康或执行成功。停用保留草稿。仅修改草稿的任务不额外发布/停用。
|
|
46
48
|
|
|
47
49
|
## 回滚到明确版本
|
|
48
50
|
|
|
@@ -60,4 +62,4 @@ export async function rollbackStream(
|
|
|
60
62
|
}
|
|
61
63
|
```
|
|
62
64
|
|
|
63
|
-
检查回执 activeVersion
|
|
65
|
+
检查回执 activeVersion 与 activeRelease。超时或 UnsV2OutcomeUnknownError 时保留原路由、载荷和幂等键;允许恢复时原样重发以取得原回执,不用新键再次发布。旧回执可能没有 activeRelease,不能读取当前 head 后冒充旧请求的发布编号。相同草稿使用不同键仍会创建新发布,expectedRevision 仅保护草稿修订。预览用 dryRun=true,返回 validated/sideEffects,不保证稍后的实际发布一定成功。
|
package/skills/{tier0-sdk-v2-stream → tier0-sdk-v2/tier0-sdk-v2-stream}/references/lifecycle.md
RENAMED
|
@@ -12,7 +12,8 @@ import { createClient, type Tier0Client, type UnsV2JSONValue } from '@tier0/sdk-
|
|
|
12
12
|
export async function testStreamSample(
|
|
13
13
|
id: string, entryId: string, input: UnsV2JSONValue, client: Tier0Client = createClient(),
|
|
14
14
|
) {
|
|
15
|
-
const { data: current } = await client.stream.documents.get({ id });
|
|
15
|
+
const { data: current } = await client.stream.documents.get({ id, view: 'full' });
|
|
16
|
+
if (!('document' in current)) throw new Error('需要完整 Stream 文档');
|
|
16
17
|
if (!current.document.entries?.some(entry => entry.id === entryId)) throw new Error('入口不在草稿中');
|
|
17
18
|
const response = await client.stream.sampleTest({ document: current.document, entryId, input });
|
|
18
19
|
return { status: response.data.status, errorCode: response.data.errorCode,
|
package/skills/{tier0-sdk-v2-stream → tier0-sdk-v2/tier0-sdk-v2-stream}/references/update.md
RENAMED
|
@@ -18,18 +18,17 @@ import { createClient, type Tier0Client, type CloudStreamDocument } from '@tier0
|
|
|
18
18
|
export async function updateStreamDescription(
|
|
19
19
|
id: string, description: string, intentKey: string, client: Tier0Client = createClient(),
|
|
20
20
|
) {
|
|
21
|
-
const { data: current } = await client.stream.documents.get({ id });
|
|
21
|
+
const { data: current } = await client.stream.documents.get({ id, view: 'full' });
|
|
22
|
+
if (!('document' in current)) throw new Error('需要完整 Stream 文档');
|
|
22
23
|
const document: CloudStreamDocument = { ...current.document, description };
|
|
23
|
-
const checked = await client.stream.documents.validate({ document });
|
|
24
|
-
if (!checked.data.valid) throw new Error('Stream 文档校验失败,请检查 issues');
|
|
25
24
|
const saved = await client.stream.documents.update({
|
|
26
25
|
id, expectedRevision: current.revision, document, dryRun: false, idempotencyKey: intentKey,
|
|
27
26
|
});
|
|
28
27
|
if (!('id' in saved.data)) throw new Error('未返回已保存文档');
|
|
29
|
-
return { receipt: saved, current:
|
|
28
|
+
return { receipt: saved, current: saved.data };
|
|
30
29
|
}
|
|
31
30
|
```
|
|
32
31
|
|
|
33
|
-
|
|
32
|
+
代码应用可直接用 documents.update({id, expectedRevision, program, dryRun:false, idempotencyKey}),由服务端编译入口和绑定。编辑完整文档时保留 sdkVersion/timeoutMs,并同步 entries/sources;写入本身执行校验,只有需要提前展示问题时才单独 validate。不要对脱敏 programs.get.document 做展开更新。
|
|
34
33
|
|
|
35
|
-
响应是完整 CloudStreamDocumentView
|
|
34
|
+
响应是完整 CloudStreamDocumentView 或预检结果;把 saved.data 直接传给下一步[发布示例](deploy.md),无需为了 revision 再读取。已有当前完整文档时可省去示例最初的 get。保存草稿不会自动部署,activeDocument 可能仍是旧发布版本。VERSION_CONFLICT 后重新读取、比较并决策;不无条件覆盖。
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: tier0-sdk-v2-uns
|
|
3
|
+
description: 使用 @tier0/sdk-v2 的 client.uns 完成新架构集合、Schema、目录、Topic、Record 增删改查、归档恢复、采样历史与事务,区分 App 和 Workspace 数据权限。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# UNS 数据操作
|
|
7
|
+
|
|
8
|
+
UNS 管数据命名空间、集合、Record 和 Sample;外部连接与脚本分别用 [Source](../tier0-sdk-v2-source/SKILL.md)、[Stream](../tier0-sdk-v2-stream/SKILL.md)。服务端 `createClient()` 读取 TIER0_API_HOST / TIER0_API_KEY,或显式传 apiHost/apiKey。数据权限不自动授予管理权限:管理操作中 App 凭证只能调用本 App 集合的 appCollectionsEnsure / SchemaReplace / Archive / Restore;appDataPolicyGet/Update 的调用方必须是持有 uns.write 的登录会话、持有 uns.write 且 full_access、不绑定 App 的 personal / agent Key,或治理会话;运行时 App 凭证不能管理数据授权。
|
|
9
|
+
|
|
10
|
+
## 按任务加载参考
|
|
11
|
+
|
|
12
|
+
| 用户任务 | 参考 | 方法 |
|
|
13
|
+
| --- | --- | --- |
|
|
14
|
+
| 创建集合、定义 Schema、新增记录 | [创建](references/create.md) | metadataInsertOne/Many、recordInsertOne |
|
|
15
|
+
| 查询集合、筛选/排序记录 | [查询](references/read.md) | dataAppContext、metadataFind/FindOne(非 App 凭证)、collectionDescribe、recordFind |
|
|
16
|
+
| 改记录、集合名称、扩展 Schema | [修改](references/update.md) | recordUpdateOne、metadataUpdateOne/Many、metadataFindOneAndUpdate |
|
|
17
|
+
| 删记录、软删除集合 | [删除](references/delete.md) | recordDeleteOne、metadataDeleteOne/Many |
|
|
18
|
+
| 恢复集合或记录 | [恢复](references/restore.md) | metadataRestore、recordRestore |
|
|
19
|
+
| 分页、历史采样、事务、写入结果 | [数据工作流](references/data-workflows.md) | sampleFind、recordTransactionExecute |
|
|
20
|
+
| 目录、Topic 元数据及组合事务 | [命名空间](references/namespace.md) | metadata*、metadataBulkWrite、metadataTransact |
|
|
21
|
+
|
|
22
|
+
## 找到正确数据上下文
|
|
23
|
+
|
|
24
|
+
托管 App 使用 dataAppContext,按业务 alias 精确选择授权集合;它同时给出每个集合的 revision、索引就绪状态(indexStatus)、TTL 和平台限额(limits)。App 凭证不能调用 metadataFind/FindOne、async 模式与 jobs 查询;Workspace 集成(会话、personal / agent Key)用 dataDiscover/metadataFind,不强制 App 上下文。通用元数据管理写入不向 App 凭证开放。App 自有业务数据优先用 [App 数据层](../tier0-sdk-v2-data/SKILL.md)(`@tier0/sdk-v2/data`),变更订阅只在那里提供(`data.subscribe`)。
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
import { createClient } from '@tier0/sdk-v2';
|
|
28
|
+
|
|
29
|
+
export async function findAppCollection(alias: string) {
|
|
30
|
+
const client = createClient();
|
|
31
|
+
const context = await client.uns.dataAppContext({});
|
|
32
|
+
const collection = context.data.collections.find(item => item.alias === alias);
|
|
33
|
+
if (!collection) throw new Error('当前 App 未获得目标集合授权');
|
|
34
|
+
return client.uns.collectionDescribe({ collectionId: collection.collectionId });
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
不要把 alias/名称当 collectionId,不猜第一个集合是业务目标。
|
|
39
|
+
|
|
40
|
+
平台签发的 App Key 声明本 App 自有集合使用 appCollectionsEnsure,修改结构、停用及恢复分别使用 appCollectionsSchemaReplace/Archive/Restore;这些保留专用 App 归属和授权语义,不能改成 metadataInsertOne 或普通元数据写入。会话或个人 Key 查询自有集合使用 appCollectionsList。Builder 创建普通授权集合可在 metadataInsertOne 中提供 appId/alias,具体凭证与权限由服务端复验。
|
|
41
|
+
|
|
42
|
+
## 核心约束
|
|
43
|
+
|
|
44
|
+
1. 记录行是 id/data/revision;filter 与 update 的业务路径用 name、customer.name,不加 data.;系统路径用 _id、_meta.revision。
|
|
45
|
+
2. 改/删前读取 expectedRevision;不同操作使用独立意图键,长度上限以当前操作契约为准。
|
|
46
|
+
3. metadataUpdateOne 的 $set.recordSchema 替换完整 Schema,不迁移已有记录;未更新的 sampleSchema/indexes 保留。先预检兼容性。
|
|
47
|
+
4. 集合无硬删除入口,用 metadataDeleteOne/Many 软删除;Record 删除会物理删除关联 Sample,恢复 Record 不会恢复这些 Sample。
|
|
48
|
+
5. recordFind 使用 pageInfo.hasMore/nextCursor,limit 1–200;sort 为 [['field',1]]。单页不是全量,跨页不是稳定快照。
|
|
49
|
+
6. Sample 的时间是 Unix 毫秒,查询范围 [from,to)。Record 代表业务实体,不用覆盖它来保存历史。
|
|
50
|
+
|
|
51
|
+
## 结果与异常
|
|
52
|
+
|
|
53
|
+
响应为 code/msg/requestId/data。元数据 find 返回 items/pageInfo;findOne 与 findOneAnd* 返回 value;普通写入返回对应计数和 ID。metadataTransact 的 results 按 opId 对应。检查 committed、每项错误和实际计数;dryRun 只校验。metadataBulkWrite 默认原子,显式非原子时必须逐项检查 results/errors;跨 kind 的依赖写入使用 metadataTransact。recordBulkWrite 不保证跨集合事务。
|
|
54
|
+
|
|
55
|
+
大整数与 JSON 精确数字可能为 bigint/UnsV2RawNumber;保留读取到的 revision 类型,不用 Number 或普通 JSON.stringify 做无损往返。错误带 execution(notStarted / rolledBack / committed / unknown)与 retry(safe、afterMs)。UnsV2OutcomeUnknownError(execution 为 unknown)先读回核对内容;retry.safe 时可以用同一请求、同一幂等键原样重发。取消请求不撤销写入;client.uns 不自动重试。
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# 创建集合与记录
|
|
2
|
+
|
|
3
|
+
元数据使用 metadataInsertOne(POST /openapi/v2/uns/metadata)或 metadataInsertMany;kind 指定 directory/topic/collection。单条提供 data、可选 id;批量提供同一 kind 的 documents[{id?,data}]。idempotencyKey 标识一个写入意图;dryRun=true 仅校验。schemaVersion 由服务端赋默认值。
|
|
4
|
+
|
|
5
|
+
集合 data 必须包含 name、recordSchema,可选 description、parentId、sampleSchema、indexes、semantics。parentId=null 表示根目录。索引创建后仍需确认状态为 ready。
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { createClient, type Tier0Client, type UnsV2Request } from '@tier0/sdk-v2';
|
|
9
|
+
|
|
10
|
+
export async function createDeviceCollection(
|
|
11
|
+
collectionId: string, name: string, intentKey: string, client: Tier0Client = createClient(),
|
|
12
|
+
) {
|
|
13
|
+
const request: UnsV2Request<'metadataInsertOne'> = {
|
|
14
|
+
kind: 'collection', id: collectionId,
|
|
15
|
+
data: {
|
|
16
|
+
name, parentId: null,
|
|
17
|
+
recordSchema: {
|
|
18
|
+
type: 'object', additionalProperties: false, required: ['name', 'enabled'],
|
|
19
|
+
properties: { name: { type: 'string' }, enabled: { type: 'boolean' } },
|
|
20
|
+
},
|
|
21
|
+
sampleSchema: {
|
|
22
|
+
type: 'object', additionalProperties: false, required: ['temperature'],
|
|
23
|
+
properties: { temperature: { type: 'number', unit: '°C' } },
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
const preview = await client.uns.metadataInsertOne({ ...request, dryRun: true });
|
|
28
|
+
if (preview.data.status !== 'validated') throw new Error('集合预检未通过');
|
|
29
|
+
const receipt = await client.uns.metadataInsertOne({ ...request, dryRun: false, idempotencyKey: intentKey });
|
|
30
|
+
if (receipt.data.status !== 'committed') return { state: 'unconfirmed' as const, receipt };
|
|
31
|
+
const current = await client.uns.metadataFindOne({ kind: 'collection', filter: { _id: collectionId } });
|
|
32
|
+
if (!current.data.value) throw new Error('集合创建有回执,读回未确认');
|
|
33
|
+
return { state: 'committed' as const, receipt, current: current.data.value };
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
单条新增默认返回 insertedId/status/replayed。需要创建后的数据、revision 和时间戳时,传 returnDocument:'after',同一次写入返回 value,无需再查询;可配合 projection,调用者必须同时具有读取权限。幂等重放返回原始创建快照,后续读取最新状态仍用 recordGet/recordFind。dryRun 的 value 是未提交预览。
|
|
38
|
+
|
|
39
|
+
批量新增返回 insertedCount/insertedIds 及适用的逐项错误。insertedIds 的键是原始输入从 0 开始的索引。省略自定义 ID 时从成功回执取得实际 ID,不能把名称当成 ID。
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
import { createClient, type Tier0Client } from '@tier0/sdk-v2';
|
|
43
|
+
|
|
44
|
+
export async function insertDevice(
|
|
45
|
+
collectionId: string, id: string, name: string, intentKey: string,
|
|
46
|
+
client: Tier0Client = createClient(),
|
|
47
|
+
) {
|
|
48
|
+
const receipt = await client.uns.recordInsertOne({
|
|
49
|
+
collectionId, id, data: { name, enabled: true }, dryRun: false, idempotencyKey: intentKey, returnDocument: 'after',
|
|
50
|
+
});
|
|
51
|
+
if (receipt.data.status !== 'committed') return { state: 'unconfirmed' as const, receipt };
|
|
52
|
+
if (!receipt.data.value) throw new Error('创建回执缺少请求的快照');
|
|
53
|
+
return { state: 'committed' as const, receipt, created: receipt.data.value };
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
记录创建与集合创建使用不同意图键。未知结果先按 ID 核对内容,不仅检查存在;SDK 不自动重试。
|
package/skills/{tier0-sdk-v2-uns → tier0-sdk-v2/tier0-sdk-v2-uns}/references/data-workflows.md
RENAMED
|
@@ -52,22 +52,18 @@ export async function insertRecord(collectionId: string, id: string, data: Recor
|
|
|
52
52
|
});
|
|
53
53
|
} catch (error) {
|
|
54
54
|
if (error instanceof UnsV2OutcomeUnknownError) {
|
|
55
|
-
return { state: 'unknown' as const, requestId: error.requestId };
|
|
55
|
+
return { state: 'unknown' as const, requestId: error.requestId, replaySafe: error.retry.safe };
|
|
56
56
|
}
|
|
57
57
|
if (error instanceof UnsV2Error) {
|
|
58
|
-
return { state: 'rejected' as const, code: error.code,
|
|
58
|
+
return { state: 'rejected' as const, code: error.code, execution: error.execution, retry: error.retry };
|
|
59
59
|
}
|
|
60
60
|
throw error;
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
```
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
该函数执行真实写入,仅用于已授权的新增意图。错误的 `execution` 说明执行到哪一步:`notStarted` / `rolledBack` 没有任何改动,`committed` 已生效,`unknown`(只出现在写入)可能已生效,此时抛出的是 `UnsV2OutcomeUnknownError`。`retry.safe` 表示可以用同一请求(同一幂等键)重发,`retry.afterMs` 是建议等待时间。`client.uns` 从不自动重试;unknown 时按指定 id 读取并核对业务内容,不能只凭记录存在就判定本次成功。
|
|
66
66
|
|
|
67
67
|
recordTransactionExecute 用 opId/dependsOn 表达依赖;不要拆成若干普通请求后声称具备事务性。allowPartial 适用于支持它的普通数组写入,事务不接受。部分成功检查每项结果,修正后的失败项是新的业务请求,不能原样重放整个批次。
|
|
68
68
|
|
|
69
69
|
recordLock 返回的 token 是租约凭据,更新/续租/解锁按原记录匹配;超时或租约失效不能继续假定持锁。只读响应中的 lock 状态不包含可用于写入的令牌。
|
|
70
|
-
|
|
71
|
-
## 变更轮询
|
|
72
|
-
|
|
73
|
-
recordChangesPoll 使用 collectionId、可选 recordIds/cursor/limit/waitMs;limit 最大 100,waitMs 最大 5000。保存返回游标并在取消或目标完成后停止。收到变更后按事件语义重新读取需要的记录;这不是任意 SQL CDC,也不代表有 Source/Stream 管理权限。
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# 删除记录与元数据
|
|
2
|
+
|
|
3
|
+
记录删除仍使用专属数据接口:
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import { createClient, type Tier0Client } from '@tier0/sdk-v2';
|
|
7
|
+
|
|
8
|
+
export async function deleteDevice(
|
|
9
|
+
collectionId: string, id: string, intentKey: string, client: Tier0Client = createClient(),
|
|
10
|
+
) {
|
|
11
|
+
const current = (await client.uns.recordFind({ collectionId, filter: { _id: id }, limit: 1 })).data.items[0];
|
|
12
|
+
if (!current) throw new Error('记录不存在或不可见,未执行删除');
|
|
13
|
+
const receipt = await client.uns.recordDeleteOne({
|
|
14
|
+
collectionId, filter: { _id: id }, expectedRevision: current.revision,
|
|
15
|
+
dryRun: false, idempotencyKey: intentKey,
|
|
16
|
+
});
|
|
17
|
+
if (receipt.data.status !== 'committed' || String(receipt.data.deletedCount) !== '1') {
|
|
18
|
+
return { state: 'unconfirmed' as const, receipt };
|
|
19
|
+
}
|
|
20
|
+
const after = await client.uns.recordFind({ collectionId, filter: { _id: id }, limit: 1 });
|
|
21
|
+
if (after.data.items.length) throw new Error('删除有回执,但记录仍可见或已被恢复');
|
|
22
|
+
return { state: 'committed' as const, receipt };
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
元数据使用 metadataDeleteOne/Many,或 metadataFindOneAndDelete 返回删除前对象。它们执行软删除,增加 revision,不隐式级联目录后代、引用对象或集合数据。引用约束与版本冲突必须处理,不能自行扩展删除范围。
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
import { createClient, type Tier0Client } from '@tier0/sdk-v2';
|
|
30
|
+
|
|
31
|
+
export async function deleteCollection(
|
|
32
|
+
id: string, intentKey: string, client: Tier0Client = createClient(),
|
|
33
|
+
) {
|
|
34
|
+
const current = (await client.uns.metadataFindOne({ kind: 'collection', filter: { _id: id } })).data.value;
|
|
35
|
+
if (!current) throw new Error('集合不存在或不可见,未执行删除');
|
|
36
|
+
const receipt = await client.uns.metadataDeleteOne({
|
|
37
|
+
kind: 'collection', filter: { _id: id }, expectedRevision: current.revision,
|
|
38
|
+
dryRun: false, idempotencyKey: intentKey,
|
|
39
|
+
});
|
|
40
|
+
if (receipt.data.status !== 'committed' || String(receipt.data.deletedCount) !== '1') {
|
|
41
|
+
return { state: 'unconfirmed' as const, receipt };
|
|
42
|
+
}
|
|
43
|
+
const deleted = await client.uns.metadataFindOne({ kind: 'collection', filter: { _id: id }, state: 'deleted' });
|
|
44
|
+
if (!deleted.data.value) throw new Error('删除有回执,但删除状态读回未确认');
|
|
45
|
+
return { state: 'deleted' as const, receipt, deleted: deleted.data.value };
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
metadataDeleteMany 使用明确 filter 和 maxDocs。默认原子;超过上限整体拒绝,不会静默只删第一批。批量删除前可 dryRun。state 默认 active,删除对象通过 state=deleted 查询;all 同时包含两种状态。
|
|
50
|
+
|
|
51
|
+
业务记录使用 recordDeleteOne/Many,传 collectionId 和明确 filter,编辑型流程同时传 expectedRevision。不要把元数据软删除解释成逐条删除该集合的 Record。Sample 的删除是独立的显式时间范围操作,具体影响遵循当前数据 API 契约。
|
|
52
|
+
|
|
53
|
+
恢复见[恢复](restore.md)。未知写入结果保留原意图键,先按对象身份核对,不自动换键重试。
|