@tier0/sdk-v2 0.9.6 → 0.9.9
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/CHANGELOG.md +24 -0
- package/README.md +17 -5
- package/dist/cjs/capabilities.js +7 -4
- package/dist/cjs/capabilities.js.map +1 -1
- package/dist/cjs/client.d.ts +4 -6
- package/dist/cjs/data/bootstrap.js +6 -1
- package/dist/cjs/data/bootstrap.js.map +1 -1
- package/dist/cjs/data/define.d.ts +7 -0
- package/dist/cjs/data/define.js +21 -1
- package/dist/cjs/data/define.js.map +1 -1
- package/dist/cjs/data/schema.d.ts +1 -0
- package/dist/cjs/data/schema.js +9 -0
- package/dist/cjs/data/schema.js.map +1 -1
- package/dist/cjs/openapi/api.generated.d.ts +1 -0
- package/dist/cjs/openapi/codecs.generated.d.ts +54 -28
- package/dist/cjs/openapi/codecs.generated.js +1 -1
- package/dist/cjs/openapi/codecs.generated.js.map +1 -1
- package/dist/cjs/openapi/types.generated.d.ts +19 -2
- package/dist/cjs/openapi/types.generated.js.map +1 -1
- package/dist/cjs/source.d.ts +0 -3
- package/dist/cjs/source.js +0 -1
- package/dist/cjs/source.js.map +1 -1
- package/dist/cjs/uns-v2/generated.d.ts +73 -86
- package/dist/cjs/uns-v2/generated.js +20 -21
- package/dist/cjs/uns-v2/generated.js.map +1 -1
- package/dist/esm/capabilities.js +7 -4
- package/dist/esm/capabilities.js.map +1 -1
- package/dist/esm/client.d.ts +4 -6
- package/dist/esm/data/bootstrap.js +6 -1
- package/dist/esm/data/bootstrap.js.map +1 -1
- package/dist/esm/data/define.d.ts +7 -0
- package/dist/esm/data/define.js +19 -0
- package/dist/esm/data/define.js.map +1 -1
- package/dist/esm/data/schema.d.ts +1 -0
- package/dist/esm/data/schema.js +10 -1
- package/dist/esm/data/schema.js.map +1 -1
- package/dist/esm/openapi/api.generated.d.ts +1 -0
- package/dist/esm/openapi/codecs.generated.d.ts +54 -28
- package/dist/esm/openapi/codecs.generated.js +1 -1
- package/dist/esm/openapi/codecs.generated.js.map +1 -1
- package/dist/esm/openapi/types.generated.d.ts +19 -2
- package/dist/esm/openapi/types.generated.js.map +1 -1
- package/dist/esm/source.d.ts +0 -3
- package/dist/esm/source.js +0 -1
- package/dist/esm/source.js.map +1 -1
- package/dist/esm/uns-v2/generated.d.ts +73 -86
- package/dist/esm/uns-v2/generated.js +20 -21
- package/dist/esm/uns-v2/generated.js.map +1 -1
- package/package.json +2 -1
- package/skills/tier0-sdk-v2/tier0-sdk-v2-data/SKILL.md +4 -1
- package/skills/tier0-sdk-v2/tier0-sdk-v2-data/references/consistency.md +3 -3
- package/skills/tier0-sdk-v2/tier0-sdk-v2-data/references/errors.md +4 -0
- package/skills/tier0-sdk-v2/tier0-sdk-v2-data/references/modeling.md +32 -1
- package/skills/tier0-sdk-v2/tier0-sdk-v2-data/references/read-write.md +3 -3
- package/skills/tier0-sdk-v2/tier0-sdk-v2-data/references/schema-evolution.md +3 -2
- package/skills/tier0-sdk-v2/tier0-sdk-v2-source/SKILL.md +5 -6
- package/skills/tier0-sdk-v2/tier0-sdk-v2-source/references/changes.md +2 -2
- package/skills/tier0-sdk-v2/tier0-sdk-v2-source/references/create.md +6 -6
- package/skills/tier0-sdk-v2/tier0-sdk-v2-source/references/delete.md +1 -1
- package/skills/tier0-sdk-v2/tier0-sdk-v2-source/references/read.md +1 -1
- package/skills/tier0-sdk-v2/tier0-sdk-v2-source/references/update.md +8 -8
- package/skills/tier0-sdk-v2/tier0-sdk-v2-stream/SKILL.md +1 -1
- package/skills/tier0-sdk-v2/tier0-sdk-v2-stream/references/create.md +1 -1
- package/skills/tier0-sdk-v2/tier0-sdk-v2-stream/references/delete.md +1 -1
- package/skills/tier0-sdk-v2/tier0-sdk-v2-uns/SKILL.md +5 -2
- package/skills/tier0-sdk-v2/tier0-sdk-v2-uns/references/delete.md +27 -1
- package/skills/tier0-sdk-v2/tier0-sdk-v2-uns/references/namespace.md +17 -1
- package/skills/tier0-sdk-v2/tier0-sdk-v2-uns/references/read.md +22 -0
- package/skills/tier0-sdk-v2/tier0-sdk-v2-source/references/state.md +0 -36
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tier0/sdk-v2",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.9",
|
|
4
4
|
"description": "Tier0 Cloud new-architecture SDK for TypeScript and JavaScript",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"dist",
|
|
23
23
|
"skills",
|
|
24
24
|
"README.md",
|
|
25
|
+
"CHANGELOG.md",
|
|
25
26
|
"LICENSE",
|
|
26
27
|
"NOTICE.md"
|
|
27
28
|
],
|
|
@@ -5,7 +5,7 @@ description: 使用 @tier0/sdk-v2/data 在 App 服务端声明并读写本 App
|
|
|
5
5
|
|
|
6
6
|
# App 数据层
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
创建 App 集合必须在 `defineCollection` 中声明 `directoryPath`:从 Workspace 根开始的实际业务目录名称数组,例如已确认的 `['苏州工厂', '装配一线', 'MES']`(代码写法见下方)。工业场景先按[工业命名空间指导](../tier0-sdk-v2-uns/references/namespace.md)查询并复用已有 ISA-95 目录,只声明业务需要且已知的层级;不同集合按实际范围放置,共享数据放到共同祖先。ensure 按路径逐级复用或创建目录,不再把所有集合放入缺少业务范围的根级 App 目录;允许在已知工厂、产线等业务范围下声明 App 或业务模块目录,例如“工厂 / 产线 / MES / 工单”中的 MES。不强行补满五层,不编造企业、工厂或设备。非工业场景使用真实业务目录。
|
|
9
9
|
|
|
10
10
|
`@tier0/sdk-v2/data` 是 App 业务数据的服务端入口:用 `defineCollection` 声明集合,`createDataClient` 在首次访问时一次性 ensure 全部声明,然后用 Mongo 驱动风格的集合句柄读写 UNS 记录。没有 SQL、ORM、连接串或迁移步骤;Preview 与部署后的 App 共用同一批集合和记录。
|
|
11
11
|
|
|
@@ -37,6 +37,7 @@ import { defineCollection, type Doc } from '@tier0/sdk-v2/data/define';
|
|
|
37
37
|
export const workOrders = defineCollection({
|
|
38
38
|
alias: 'work_orders',
|
|
39
39
|
name: '生产工单',
|
|
40
|
+
directoryPath: ['苏州工厂', '装配一线', 'MES'],
|
|
40
41
|
description: 'Production work orders',
|
|
41
42
|
schema: z.object({
|
|
42
43
|
code: z.string(),
|
|
@@ -56,6 +57,8 @@ export async function releasedOrders(): Promise<WorkOrder[]> {
|
|
|
56
57
|
}
|
|
57
58
|
```
|
|
58
59
|
|
|
60
|
+
示例假设“苏州工厂 / 装配一线 / MES”已由业务确认;替换为实际目录,不照抄示例工厂。每段为独立业务名称,不使用斜杠拼接。新建缺少路径会报 `INVALID_REQUEST`;已有 alias 可兼容省略路径,后续声明的路径变化不会自动搬迁集合,返回 `DIRECTORY_PATH_NOT_APPLIED` 提示。
|
|
61
|
+
|
|
59
62
|
## 核心约束
|
|
60
63
|
|
|
61
64
|
1. 声明文件只从 `@tier0/sdk-v2/data/define` 导入,客户端代码可以安全引用它;由 ensure 管理的自有集合放在一个 `collections` 列表里(每个 App 最多 64 个),只建一个进程级 client;不要手动 ensure、不要在 import 时访问数据、不要自建“已初始化”标记。
|
|
@@ -14,9 +14,9 @@
|
|
|
14
14
|
import { z } from 'zod';
|
|
15
15
|
import { createDataClient, defineCollection } from '@tier0/sdk-v2/data';
|
|
16
16
|
|
|
17
|
-
const stock = defineCollection({ alias: 'stock', name: '库存', schema: z.object({ sku: z.string(), available: z.number().int().min(0).meta({ format: 'int32' }) }) });
|
|
18
|
-
const reservations = defineCollection({ alias: 'reservations', name: '库存预留', schema: z.object({ sku: z.string(), qty: z.number().int().min(1).meta({ format: 'int32' }), orderId: z.string() }) });
|
|
19
|
-
const counters = defineCollection({ alias: 'counters', name: '业务计数器', schema: z.object({ value: z.number().int().meta({ format: 'int32' }) }) });
|
|
17
|
+
const stock = defineCollection({ alias: 'stock', name: '库存', directoryPath: ['仓储管理'], schema: z.object({ sku: z.string(), available: z.number().int().min(0).meta({ format: 'int32' }) }) });
|
|
18
|
+
const reservations = defineCollection({ alias: 'reservations', name: '库存预留', directoryPath: ['仓储管理'], schema: z.object({ sku: z.string(), qty: z.number().int().min(1).meta({ format: 'int32' }), orderId: z.string() }) });
|
|
19
|
+
const counters = defineCollection({ alias: 'counters', name: '业务计数器', directoryPath: ['业务管理'], schema: z.object({ value: z.number().int().meta({ format: 'int32' }) }) });
|
|
20
20
|
const data = createDataClient({ collections: [stock, reservations, counters] });
|
|
21
21
|
|
|
22
22
|
/** 扣减库存并写预留:库存不足时整体不提交。 */
|
|
@@ -59,6 +59,10 @@ export function toResponse(error: unknown): Response {
|
|
|
59
59
|
| code | 含义与处理 |
|
|
60
60
|
| --- | --- |
|
|
61
61
|
| `SCHEMA_CONFLICT` (409) | 声明无法与存储合并,没有任何改动。`data.conflicts` 列出 `{ alias, path, reason, current?, declared? }`:`TYPE_CONFLICT` → 换新字段名;`LIMIT_EXCEEDED` → 拆分结构;`JOIN_UNSUPPORTED` → 存储中有声明无法放宽的节点,用 `schema:replace` 修复;`DATA_RULE_FIELD` → 员工数据规则(owner/state 字段)在用该路径,保持必填且类型不变,或请管理员修改规则;`OPEN_OBJECT` → 用 `z.object`;`ALIAS_IN_USE` → 选择未占用的 alias;`NAME_IN_USE` → 选择同目录下不同且有业务含义的 name,保留 alias,不拼随机后缀;`INVALID_INDEX` → 修正索引 |
|
|
62
|
+
| `INVALID_REQUEST` (400),`path=collections/<i>/directoryPath` | 新建集合缺少目录或路径非法。声明实际业务路径后再调用;不回退到缺少业务范围的根级 App 目录,不添加虚构层级 |
|
|
63
|
+
| `SCHEMA_CONFLICT`,`data.reason=DIRECTORY_PATH_CONFLICT` | 路径被已归档目录或同名非目录对象占用;核对真实目录,不能自动恢复、改名或扩大授权 |
|
|
64
|
+
| `DIRECTORY_PATH_NOT_APPLIED`(`ignored` 提示) | 已有 alias 的目录与声明不同;保留当前位置继续服务。需要移动时走明确授权的元数据操作,ensure 不迁移 |
|
|
65
|
+
| `CONDITION_FAILED`,`data.reason=DIRECTORY_UNAVAILABLE` | 恢复集合时原父目录或祖先不可用;明确恢复目录后再试,不自动恢复父链或改挂集合 |
|
|
62
66
|
| `ANNOTATION_NOT_APPLIED`(`ignored` 提示) | 不是错误:ensure 保留已存储的描述,用 `schema:replace` 应用修改 |
|
|
63
67
|
| `REQUIRED_NOT_ENFORCED`(警告) | 声明要求的字段在存储中仍是可选;改回可选,或回填后用 `schema:replace` 收窄 |
|
|
64
68
|
| `INDEX_BUILD_FAILED`(警告) | 已有记录违反新唯一索引;修正重复数据,下次 ensure 会重试建索引 |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 声明集合与建模
|
|
2
2
|
|
|
3
|
-
`defineCollection({ alias, name, schema, indexes?, description?, seed? })` 返回冻结的声明;全部声明放进一个数组传给 `createDataClient({ collections })`。声明文件从 `@tier0/sdk-v2/data/define` 导入:它不含数据运行时,组件和 route 也能引用这些声明与 `Doc` 类型。alias 在 App 内唯一、不区分大小写,匹配 `^[A-Za-z][A-Za-z0-9_-]{0,62}$`。
|
|
3
|
+
`defineCollection({ alias, name, directoryPath, schema, indexes?, description?, seed? })` 返回冻结的声明;全部声明放进一个数组传给 `createDataClient({ collections })`。声明文件从 `@tier0/sdk-v2/data/define` 导入:它不含数据运行时,组件和 route 也能引用这些声明与 `Doc` 类型。alias 在 App 内唯一、不区分大小写,匹配 `^[A-Za-z][A-Za-z0-9_-]{0,62}$`。
|
|
4
4
|
|
|
5
5
|
`name` 是必填的业务名称,界面直接展示,例如“生产工单”;1–63 个 Unicode 字符,无首尾空白,不含 `/` 或空字符。`alias` 是独立、稳定的程序标识,例如 `production_orders`,改业务名称不改 alias 或集合 ID。名称应表达实际用途,语言遵循用户偏好和已有业务术语,不附加无业务含义的随机串、时间戳或 ID。没有 displayName,也不从 alias 回退生成名称。ensure 只在首次创建时写入 name;命中已有 alias 时保留用户当前名称,显式改名走 MCP 元数据更新。
|
|
6
6
|
|
|
@@ -27,6 +27,7 @@ import { defineCollection, type Doc } from '@tier0/sdk-v2/data/define';
|
|
|
27
27
|
export const customers = defineCollection({
|
|
28
28
|
alias: 'customers',
|
|
29
29
|
name: '客户',
|
|
30
|
+
directoryPath: ['销售管理'],
|
|
30
31
|
schema: z.object({ name: z.string(), tier: z.enum(['A', 'B', 'C']) }),
|
|
31
32
|
indexes: [{ path: 'name' }],
|
|
32
33
|
seed: [
|
|
@@ -38,6 +39,7 @@ export const customers = defineCollection({
|
|
|
38
39
|
export const salesOrders = defineCollection({
|
|
39
40
|
alias: 'sales_orders',
|
|
40
41
|
name: '销售订单',
|
|
42
|
+
directoryPath: ['销售管理', '订单履约'],
|
|
41
43
|
description: 'Customer sales orders',
|
|
42
44
|
schema: z.object({
|
|
43
45
|
code: z.string(),
|
|
@@ -63,6 +65,34 @@ export const createSalesOrderInput = salesOrders.schema
|
|
|
63
65
|
.extend({ code: z.string().trim().min(1).max(32) });
|
|
64
66
|
```
|
|
65
67
|
|
|
68
|
+
## 按实际业务范围声明目录
|
|
69
|
+
|
|
70
|
+
新建集合必须提供 `directoryPath`,类型保留可选仅为兼容已经存在的 alias。路径从 Workspace 根开始,包含 1–16 个名称;每段 1–63 个 Unicode 字符,无首尾空白、`/` 或空字符。SDK 校验后复制并冻结数组,按原样发送,不自动追加 App 名称或补层级。已知业务范围下可以显式声明 App 或业务模块目录,例如 `directoryPath: ['工厂', '产线', 'MES']` 后放“工单”集合;不应仅用根级 App 名称代替已知工厂和产线范围。
|
|
71
|
+
|
|
72
|
+
工业场景按实际 ISA-95 设备层级组织:企业 → 工厂/站点 → 区域/车间 → 工作中心(产线等)→ 工作单元。先通过元数据查询确认已有目录、名称与父子关系,复用完整已知路径。只保留业务实际需要且已知的层级,不要求五层齐全;跨产线共享集合放在共同祖先,不为每个集合重复建一套树。非工业业务按真实业务目录组织。
|
|
73
|
+
|
|
74
|
+
例如业务已明确“苏州工厂”的“装配一线”及 MES 应用,产线工单放在 MES 下,工厂共享物料仍放在工厂下;无需编造企业或车间:
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
import { z } from 'zod';
|
|
78
|
+
import { defineCollection } from '@tier0/sdk-v2/data/define';
|
|
79
|
+
|
|
80
|
+
export const lineOrders = defineCollection({
|
|
81
|
+
alias: 'line_orders', name: '产线工单',
|
|
82
|
+
directoryPath: ['苏州工厂', '装配一线', 'MES'],
|
|
83
|
+
schema: z.object({ code: z.string() }),
|
|
84
|
+
});
|
|
85
|
+
export const siteMaterials = defineCollection({
|
|
86
|
+
alias: 'site_materials', name: '工厂物料',
|
|
87
|
+
directoryPath: ['苏州工厂'],
|
|
88
|
+
schema: z.object({ code: z.string() }),
|
|
89
|
+
});
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
后端在同一事务中逐级复用或创建活动目录,再创建集合;已归档目录或同名非目录对象会明确报冲突,不自动恢复或追加随机后缀。目录位置不代表数据授权或 App 所属关系;共享目录不会成为某个 App 的专属资源。
|
|
93
|
+
|
|
94
|
+
目录声明只决定首次创建位置。ensure 找到已有 alias 时保留集合 ID、现有目录和用户改名;不同路径返回 `DIRECTORY_PATH_NOT_APPLIED`,不移动已有集合,也不为了被忽略的路径创建目录。要整理存量数据,需要明确迁移授权后用元数据接口操作。不要通过改 alias 复制集合来绕过这一规则。
|
|
95
|
+
|
|
66
96
|
## 可存储的 Zod 子集
|
|
67
97
|
|
|
68
98
|
集合 Schema 会转成 UNS 记录 Schema 子集;不能存储的写法在 `compileCollection` / 首次 ensure 时抛 `CollectionSchemaError`,并给出替代写法。
|
|
@@ -111,6 +141,7 @@ import { defineCollection } from '@tier0/sdk-v2/data/define';
|
|
|
111
141
|
export const loginCodes = defineCollection({
|
|
112
142
|
alias: 'login_codes',
|
|
113
143
|
name: '登录验证码',
|
|
144
|
+
directoryPath: ['账号管理', '登录验证'],
|
|
114
145
|
schema: z.object({ phone: z.string(), code: z.string(), issuedAt: z.number().int().meta({ format: 'int32' }) }),
|
|
115
146
|
// issuedAt 存 Date.now();签发 5 分钟后删除。
|
|
116
147
|
indexes: [{ path: 'phone' }, { path: 'issuedAt', expireAfterSeconds: 300 }],
|
|
@@ -8,7 +8,7 @@ import { createDataClient, defineCollection } from '@tier0/sdk-v2/data';
|
|
|
8
8
|
|
|
9
9
|
const workOrders = defineCollection({
|
|
10
10
|
alias: 'work_orders',
|
|
11
|
-
name: '生产工单',
|
|
11
|
+
name: '生产工单', directoryPath: ['生产管理'],
|
|
12
12
|
schema: z.object({
|
|
13
13
|
code: z.string(),
|
|
14
14
|
status: z.enum(['DRAFT', 'RELEASED', 'DONE']),
|
|
@@ -64,7 +64,7 @@ import { createDataClient, defineCollection } from '@tier0/sdk-v2/data';
|
|
|
64
64
|
|
|
65
65
|
const workOrders = defineCollection({
|
|
66
66
|
alias: 'work_orders',
|
|
67
|
-
name: '生产工单',
|
|
67
|
+
name: '生产工单', directoryPath: ['生产管理'],
|
|
68
68
|
schema: z.object({ code: z.string(), status: z.enum(['DRAFT', 'RELEASED', 'DONE']) }),
|
|
69
69
|
indexes: [{ path: 'code', unique: true }, { path: 'status' }],
|
|
70
70
|
});
|
|
@@ -163,7 +163,7 @@ export async function bumpAll(items: Collection<{ status: string; views: number
|
|
|
163
163
|
import { z } from 'zod';
|
|
164
164
|
import { createDataClient, defineCollection, runAsApp, type SubscriptionEvent } from '@tier0/sdk-v2/data';
|
|
165
165
|
|
|
166
|
-
const tickets = defineCollection({ alias: 'tickets', name: '问题工单', schema: z.object({ title: z.string(), status: z.enum(['OPEN', 'DONE']) }) });
|
|
166
|
+
const tickets = defineCollection({ alias: 'tickets', name: '问题工单', directoryPath: ['问题管理'], schema: z.object({ title: z.string(), status: z.enum(['OPEN', 'DONE']) }) });
|
|
167
167
|
const data = createDataClient({ collections: [tickets] });
|
|
168
168
|
|
|
169
169
|
/** 一个进程一条 App 级订阅,在进程内扇出给浏览器。 */
|
|
@@ -6,6 +6,7 @@ Preview 与部署后的 App 共用集合,较旧的部署版本随时可能用
|
|
|
6
6
|
| --- | --- |
|
|
7
7
|
| 新增可选字段(任意层级,含数组元素内)、新增枚举值、字段改为可选、`int` → `number`、放宽或去掉 `min`/`max`、新增索引 | 修改声明;下次访问时 ensure 生效 |
|
|
8
8
|
| 修改集合名称 | 通过 tier0 MCP 的 metadata:updateOne 显式修改 name,保留 alias/ID;修改声明后重复 ensure 不覆盖已有名称 |
|
|
9
|
+
| 修改集合目录 | `directoryPath` 只决定首次创建位置;已有 alias 不自动移动,差异报告 `DIRECTORY_PATH_NOT_APPLIED`。确认迁移授权后使用元数据接口,不通过改 alias 重建集合 |
|
|
9
10
|
| 修改 `.describe()` 文本或集合 `description` | 修改声明,然后通过 tier0 MCP 执行 `schema:replace`,请求体从 `compileCollection(declaration)` 中只选 `alias`、`recordSchema`、`indexes`、`description`,不传 `name`(先 `dryRun`:期望 `widened`;`narrowed` 说明存储里还有该声明之外的内容,例如其他版本的字段——合并进请求体或停止)。ensure 保留已存储的描述并报告 `ANNOTATION_NOT_APPLIED`;新字段和新集合在创建时带上描述 |
|
|
10
11
|
| 新增每条记录都必须有的字段 | 先以 `.optional()` 加入,所有写入路径都写它,回填旧记录,等部署版本也写入后再用 `schema:replace` 改为必填 |
|
|
11
12
|
| 删除或重命名字段 | 代码不再使用旧字段(它可以留在存储里);新名字就是新的可选字段。通过 tier0 MCP 一次性回填(`records:updateMany` 配 `$rename` / `$set`) |
|
|
@@ -16,7 +17,7 @@ Preview 与部署后的 App 共用集合,较旧的部署版本随时可能用
|
|
|
16
17
|
|
|
17
18
|
## 运行时代码与 worker 操作的边界
|
|
18
19
|
|
|
19
|
-
`schema:replace`、`archive`、`restore` 由 worker 通过 tier0 MCP 显式执行(`/uns/app/collections/schema:replace`、`archive`、`restore`),App 运行时代码不调用。数据层导出的 `compileCollection(declaration)` 给出与运行时 ensure 完全一致的条目 `{ alias, name, recordSchema, indexes, description? }`,可用于 ensure `dryRun`。`schema:replace` 只接收其中的 alias、recordSchema、indexes、description,不接收 name;不能把 compileCollection 的完整结果直接作为替换请求。
|
|
20
|
+
`schema:replace`、`archive`、`restore` 由 worker 通过 tier0 MCP 显式执行(`/uns/app/collections/schema:replace`、`archive`、`restore`),App 运行时代码不调用。数据层导出的 `compileCollection(declaration)` 给出与运行时 ensure 完全一致的条目 `{ alias, name, directoryPath?, recordSchema, indexes, description? }`,可用于 ensure `dryRun`。`schema:replace` 只接收其中的 alias、recordSchema、indexes、description,不接收 name 或 directoryPath;不能把 compileCollection 的完整结果直接作为替换请求。
|
|
20
21
|
|
|
21
22
|
```ts
|
|
22
23
|
import { z } from 'zod';
|
|
@@ -24,7 +25,7 @@ import { compileCollection, defineCollection } from '@tier0/sdk-v2/data';
|
|
|
24
25
|
|
|
25
26
|
const tickets = defineCollection({
|
|
26
27
|
alias: 'tickets',
|
|
27
|
-
name: '问题工单',
|
|
28
|
+
name: '问题工单', directoryPath: ['问题管理'],
|
|
28
29
|
schema: z.object({ title: z.string().describe('Short summary'), severity: z.enum(['LOW', 'HIGH']).optional() }),
|
|
29
30
|
});
|
|
30
31
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: tier0-sdk-v2-source
|
|
3
|
-
description: 使用 @tier0/sdk-v2 的 client.source
|
|
3
|
+
description: 使用 @tier0/sdk-v2 的 client.source 查询、创建、修改和删除新架构 Source,配置协议连接、处理影响分析、版本冲突与运行观测。
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Source 数据接入
|
|
@@ -15,8 +15,7 @@ Source 维护连接配置、认证引用与源状态。脚本处理用 [Stream](
|
|
|
15
15
|
| 创建 HTTP/SQL 带凭据源 | [创建](references/create.md) | documents.create |
|
|
16
16
|
| 列表、详情、运行状态 | [查询](references/read.md) | documents.list/get、observations.get、runtime.get |
|
|
17
17
|
| 修改名称、端点或配置 | [修改](references/update.md) | documents.update |
|
|
18
|
-
|
|
|
19
|
-
| 启用、停用 | [状态变更](references/state.md) | state.update |
|
|
18
|
+
| 查看修改/删除影响 | [影响分析](references/changes.md) | impact |
|
|
20
19
|
| 删除并核验 | [删除](references/delete.md) | delete |
|
|
21
20
|
|
|
22
21
|
先加载对应操作文档,使用其中的参数表、完整请求及返回判断。只执行用户任务所需步骤;示例函数调用才会执行写入,导入不执行。新建→查询→修改→删除可组成生命周期,但查询任务不触发其他操作。
|
|
@@ -25,13 +24,13 @@ Source 维护连接配置、认证引用与源状态。脚本处理用 [Stream](
|
|
|
25
24
|
|
|
26
25
|
1. Source 配置统一使用 documents.*,原生文档为 `{name,protocol,config}`,HTTP API 显式指定 `config.role`。HTTP 统一为 `protocol: http-api` + `config.role: client/server`;Client 主动请求,Server 接收入站请求,不能互换。HTTPServer 统一接收入站请求,固定前缀后的路由和响应时机属于 Stream。
|
|
27
26
|
2. 已有 ID 和 revision 时优先用 documents.update 的 patch,避免额外 get;完整替换 document 前读取完整文档,安全列表摘要不能无损回写。原生 ID 是十进制字符串,revision 原样传递,不转 Number。
|
|
28
|
-
3. 现有 Source
|
|
27
|
+
3. 现有 Source 修改、删除都要 expectedRevision。仅修改名称、描述可直接提交;修改连接配置或删除时,没有任何 Stream 引用可直接提交,有草稿、活动、历史或不可见引用则需 confirmedImpactRevision。先形成精确意图,再读取全部影响分页;回执不可合成、跨意图复用或静默刷新。提供的回执始终严格校验,不自动获取并同意影响。
|
|
29
28
|
4. 配置中保留认证/TLS 引用,不写明文秘密;HTTP Client 与 SQL 新凭据走创建专用 credentials(SQL 使用 usernamePassword,省略 method),普通创建省略 credentials.method(服务端默认 REQUEST 基址绑定),真实 method/path/query 由 Stream 定义。Basic/Bearer 显式凭据方法限制精确 URL 和方法,OAuth 旧 GET 保留既有兼容语义,不自动扩大旧授权;Server 不携带 Client auth/TLS 配置。
|
|
30
|
-
5.
|
|
29
|
+
5. Source 创建后即可供 Stream 使用,不提供启停开关。删除 Source 后停止后续使用;runtimeState 和 health 是运行观测,不是用户开关。保存配置不代表 Stream 已切换至新 configRevision。
|
|
31
30
|
|
|
32
31
|
## 返回值与异常
|
|
33
32
|
|
|
34
|
-
所有调用返回完整 code/msg/requestId/data。documents.create/update 的预检返回 validated/sideEffects,提交返回完整 Source view,可直接使用其中的文档和 revision,无需额外 get
|
|
33
|
+
所有调用返回完整 code/msg/requestId/data。documents.create/update 的预检返回 validated/sideEffects,提交返回完整 Source view,可直接使用其中的文档和 revision,无需额外 get;批量删除返回 Outcome。只有 committed 且无项错误才可报告提交成功;accepted/jobId 继续查询作业。创建、更新的 dryRun 默认 false;预检按需使用,不是每次提交的必经步骤。
|
|
35
34
|
|
|
36
35
|
需要查看历史用 source.history({id,beforeRevision,size}),按 nextBeforeRevision 继续;探测用 source.probe,其外部系统访问应属于当前任务。配置校验或连接探测成功不证明某个 Stream 请求已获授权或采集成功,实际结果看对应发布的 runs。连接配置修改使用 documents.update,遵守同一影响确认契约。
|
|
37
36
|
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
| 字段 | 类型 | 必填 | 说明 |
|
|
6
6
|
| --- | --- | --- | --- |
|
|
7
7
|
| id / expectedRevision | string / integer | 是 | 当前 ID 和最近写回执或读取取得的修订 |
|
|
8
|
-
| action | update/
|
|
8
|
+
| action | update/delete | 是 | 精确变更类型 |
|
|
9
9
|
| patch / document | CloudSourceDocumentPatch / CloudSourceDocument | 更新时二选一 | 与随后提交完全相同的补丁或完整文档;其他 action 不传 |
|
|
10
10
|
| offset / size | integer | 否 | 首次 0;size 1–200 |
|
|
11
11
|
| impactRevision | string | 翻页时 | 沿用首次返回的确认值 |
|
|
@@ -37,4 +37,4 @@ export async function collectSourceImpact(
|
|
|
37
37
|
|
|
38
38
|
data:impactRevision、sourceID/sourceRevision、action、counts(draft/activeRelease/historicalRelease)、restrictedCount、references、nextOffset、expiresAt。nextOffset=0 结束;complete=1 表示计算完成,不表示首页有全部引用。
|
|
39
39
|
|
|
40
|
-
确认值绑定身份、修订、精确意图,不能合成或自动刷新。分析时用 patch,提交时就沿用该 patch;不要另行补齐为 document 或更改字段。评估 counts 和 restrictedCount;有受限引用时不能说“无影响”。Source 更新不会自动替换 Stream
|
|
40
|
+
确认值绑定身份、修订、精确意图,不能合成或自动刷新。分析时用 patch,提交时就沿用该 patch;不要另行补齐为 document 或更改字段。评估 counts 和 restrictedCount;有受限引用时不能说“无影响”。Source 更新不会自动替换 Stream 冻结修订。已有明确操作授权时不重复索要确认;影响分析本身不授予额外操作权限。仅修改名称、描述或没有任何引用时可直接变更,见 [修改](update.md),不必先执行本流程。
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
| 字段 | 类型 | 必填 | 说明 |
|
|
6
6
|
| --- | --- | --- | --- |
|
|
7
7
|
| dryRun | boolean | 否 | 默认 false 提交;true 仅预检 |
|
|
8
|
-
| document.name | string | 是 | 1–
|
|
8
|
+
| document.name | string | 是 | 1–63 个 Unicode 字符 |
|
|
9
9
|
| document.protocol | string | 是 | 原生协议,如 http-api、mqtt、modbus |
|
|
10
10
|
| document.config.role | client / server | HTTP API 显式指定 | client 主动请求;server 接收入站请求 |
|
|
11
11
|
| document.config | object | 是 | endpoint、options;见[协议配置](protocols.md) |
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
## 完整示例:HTTP Client 数据源
|
|
17
17
|
|
|
18
|
-
传入实际端点、名称和意图键。此函数创建 `http-api` + `config.role: client`
|
|
18
|
+
传入实际端点、名称和意图键。此函数创建 `http-api` + `config.role: client` 配置,创建后即可供 Stream 使用。client 从服务端环境读取 API Host/Key,也可显式注入。
|
|
19
19
|
|
|
20
20
|
```ts
|
|
21
21
|
import { createClient, type Tier0Client, type CloudSourceDocument } from '@tier0/sdk-v2';
|
|
@@ -35,7 +35,7 @@ export async function createHttpSource(
|
|
|
35
35
|
}
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
-
一次提交即可返回完整 data:id、revision、document、
|
|
38
|
+
一次提交即可返回完整 data:id、revision、document、runtimeState、health、createdAt、updatedAt,无需另行 get。需要单独预检时可先传 `dryRun: true`,返回 `{ validated: true, sideEffects: false }`,没有 ID。预检不保留提交条件;实际保存仍会校验。ID 原样保留十进制字符串。
|
|
39
39
|
|
|
40
40
|
HTTP Server 同样使用 `protocol: http-api`,角色为 `config.role: server`;其配置以 `source.protocols.list({})` 返回的正式合同为准,不能复用下方 Client 的主动请求示例。入站业务统一绑定 HTTPServer Source,路由和响应时机在 Stream 定义;202 状态不能据此报告 Stream 执行完成。
|
|
41
41
|
|
|
@@ -62,11 +62,11 @@ export async function createAuthenticatedHttpSource(
|
|
|
62
62
|
}
|
|
63
63
|
```
|
|
64
64
|
|
|
65
|
-
配置保存不代表连接健康;见[查询](read.md)
|
|
65
|
+
配置保存不代表连接健康;见[查询](read.md)。Source 无需额外启用。结果未知时保留原请求、requestId、意图键,先核对列表/历史,不自动重复创建。
|
|
66
66
|
|
|
67
67
|
## HTTP Server:管理配置
|
|
68
68
|
|
|
69
|
-
正式协议目录规定 Server 的 `config.endpoint` 为 `http://managed`、`options` 为 `{}`,不携带 Client 的 `auth` / `tls` 或创建专用 `credentials`。该函数只保存 Source
|
|
69
|
+
正式协议目录规定 Server 的 `config.endpoint` 为 `http://managed`、`options` 为 `{}`,不携带 Client 的 `auth` / `tls` 或创建专用 `credentials`。该函数只保存 Source,不创建 Stream,也不发起入站请求。
|
|
70
70
|
|
|
71
71
|
```ts
|
|
72
72
|
import { createClient, type Tier0Client, type CloudSourceDocument } from '@tier0/sdk-v2';
|
|
@@ -125,4 +125,4 @@ export async function createHttpSourceWithCredentials(
|
|
|
125
125
|
|
|
126
126
|
SQL 单条创建可直接传入 `credentials: { kind: "usernamePassword", material: { username, password } }`,不再预先调用独立凭据接口。`document.protocol` 为 `sql`,`config.endpoint` 只包含数据库地址(不包含用户名或密码),`credentials.method` 必须省略。仍需 `idempotencyKey`。秘密由 Vault 保存,Source 文档仅含引用;预检不写入凭据。
|
|
127
127
|
|
|
128
|
-
`dryRun` 省略时直接提交;需要预检才设为 true
|
|
128
|
+
`dryRun` 省略时直接提交;需要预检才设为 true。后续绑定 Source 并部署 Stream,并通过运行记录和 Collection 读回确认采集成功。
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
| allowPartial | false | 否 | 不支持部分提交 |
|
|
14
14
|
| idempotencyKey | string | 否 | 最多 128 字符 |
|
|
15
15
|
|
|
16
|
-
Source
|
|
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';
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
| 方法 | POST 路径 | 输入 | data |
|
|
4
4
|
| --- | --- | --- | --- |
|
|
5
|
-
| source.documents.list | /openapi/v2/uns/sources/documents/list | page、size、search、protocol、
|
|
5
|
+
| source.documents.list | /openapi/v2/uns/sources/documents/list | page、size、search、protocol、health、archived、includeDocument | list、total、page、hasMore、nextPage |
|
|
6
6
|
| source.documents.get | /openapi/v2/uns/sources/documents/get | id(必填)、view:'summary' 或 'full' | id、revision、状态;full 才包含完整 document |
|
|
7
7
|
| source.runtime.get | /openapi/v2/uns/sources/runtime/get | id(必填) | 运行状态 |
|
|
8
8
|
| source.observations.get | /openapi/v2/uns/sources/observations/get | items:id、expectedRevision | 指定源和修订的观测 |
|
|
@@ -7,13 +7,13 @@
|
|
|
7
7
|
| id | string | 是 | 已知的 Source ID |
|
|
8
8
|
| patch / document | object / CloudSourceDocument | 二选一 | patch 保留未提供的字段;document 完整替换 |
|
|
9
9
|
| expectedRevision | integer | 是 | 已读取或上次提交返回的 revision,原样传回 |
|
|
10
|
-
| confirmedImpactRevision | string |
|
|
10
|
+
| confirmedImpactRevision | string | 有引用的连接变更时 | 相同修改意图和修订的 impact 回执;仅改名称、描述无需提供 |
|
|
11
11
|
| dryRun | boolean | 否 | 默认 false 提交;true 仅预检 |
|
|
12
12
|
| idempotencyKey | string | 否 | 最多 128 字符 |
|
|
13
13
|
|
|
14
14
|
## 示例:一次请求修改名称
|
|
15
15
|
|
|
16
|
-
已有 ID 和 revision 时直接提交 patch
|
|
16
|
+
已有 ID 和 revision 时直接提交 patch。仅改名称、描述无需 impact;连接配置变更在没有任何草稿、活动或历史 Stream 引用时也无需 impact。服务端在同一事务和依赖锁下检查并修改。patch 中未提供的字段保留原值,config 如果提供则整体替换;完整 document 更新需先取得完整文档并保留未改配置和认证引用。
|
|
17
17
|
|
|
18
18
|
```ts
|
|
19
19
|
import { createClient, type Tier0Client, type UnsV2Request } from '@tier0/sdk-v2';
|
|
@@ -32,19 +32,19 @@ export async function renameSource(
|
|
|
32
32
|
|
|
33
33
|
提交直接返回完整 Source view 和新的 revision,无需额外 get。版本冲突时不会覆盖其他人的修改。返回 `SOURCE_IMPACT_CONFIRMATION_REQUIRED` 表示存在引用且未提交,进入下面的显式评估流程;不要自动获取并同意影响。
|
|
34
34
|
|
|
35
|
-
##
|
|
35
|
+
## 有引用的连接变更:评估后确认相同意图
|
|
36
36
|
|
|
37
37
|
prepare 收集全部影响分页。在已有授权范围内评估 counts、restrictedCount 和引用后,再调用 commit。不可见引用也要求确认。影响字段说明见[影响分析](changes.md)。
|
|
38
38
|
|
|
39
39
|
```ts
|
|
40
|
-
import { createClient, type Tier0Client, type UnsV2Request } from '@tier0/sdk-v2';
|
|
40
|
+
import { createClient, type Tier0Client, type UnsV2Request, type CloudSourceDocument } from '@tier0/sdk-v2';
|
|
41
41
|
|
|
42
|
-
export async function
|
|
42
|
+
export async function prepareSourceConfigUpdate(
|
|
43
43
|
id: string, expectedRevision: UnsV2Request<'sourceImpact'>['expectedRevision'],
|
|
44
|
-
|
|
44
|
+
config: CloudSourceDocument['config'], client: Tier0Client = createClient(),
|
|
45
45
|
) {
|
|
46
46
|
const intent: UnsV2Request<'sourceImpact'> = {
|
|
47
|
-
id, expectedRevision, action: 'update', patch: {
|
|
47
|
+
id, expectedRevision, action: 'update', patch: { config },
|
|
48
48
|
};
|
|
49
49
|
const impact = await client.source.impact({ ...intent, offset: 0, size: 200 });
|
|
50
50
|
const references = [...impact.data.references];
|
|
@@ -62,7 +62,7 @@ export async function prepareSourceRename(
|
|
|
62
62
|
}
|
|
63
63
|
return { intent, impact: { ...impact.data, references, nextOffset: offset } };
|
|
64
64
|
}
|
|
65
|
-
export async function
|
|
65
|
+
export async function commitSourceConfigUpdate(
|
|
66
66
|
plan: { intent: UnsV2Request<'sourceImpact'>; impact: { impactRevision: string } },
|
|
67
67
|
intentKey: string, client: Tier0Client = createClient(),
|
|
68
68
|
) {
|
|
@@ -37,7 +37,7 @@ description: 使用 @tier0/sdk-v2 的 client.stream 创建、查询、修改和
|
|
|
37
37
|
5. resources=[] 是空冻结资源集,不是通配符。写入本身由服务端校验,需要预览时单独 validate;当前云端程序 timeoutMs 上限 30000。
|
|
38
38
|
6. HTTPServer 响应时机在 Stream:普通返回等处理/提交完成;HTTP onRequest 内 await ctx.reply 可先回复一次再继续,仍受超时/并发/权限约束,断线不取消但 shutdown 可取消,不保证崩溃重放。
|
|
39
39
|
7. 仅执行用户要求的生命周期步骤;修改脚本不自动授予发布、停用或删除权限。
|
|
40
|
-
8. 用户要求采集落库时,先创建或复用 Collection 并确定字段映射与稳定 recordId
|
|
40
|
+
8. 用户要求采集落库时,先创建或复用 Collection 并确定字段映射与稳定 recordId,再绑定 Source 的当前修订;脚本显式 api.uns.use(...).write。部署后核对实际运行并读回记录,返回上游结果、保存草稿或样例预演均不代表已落库。
|
|
41
41
|
|
|
42
42
|
## 成功与异常
|
|
43
43
|
|
|
@@ -61,7 +61,7 @@ export async function createHttpStream(
|
|
|
61
61
|
|
|
62
62
|
先创建或复用真实 Collection,再传入 collectionId;此库存示例的 recordSchema 必须包含 sku(string)与 quantity(number),以业务 SKU 作为稳定 recordId。按真实上游合同修改字段映射、path="inventory"、query 和 60 秒周期;不能只返回 response.data 就声称已经落库。resources=[] 是空冻结资源集,不是通配授权。源地址和凭据留在 Source,不能拷进 acquisition 或脚本。
|
|
63
63
|
|
|
64
|
-
|
|
64
|
+
此函数保存草稿。完整采集使用 Source 创建回执中的 revision 绑定并部署 Stream,无需额外启用或读取;核对本次发布的运行成功,再通过 records:find 读回目标 Collection 的业务记录。acceptedRows 只表示处理器已接受写入,隔离 sampleTest 的 writePreview 不会落库;未完成真实运行和读回前,不得报告采集完成。
|
|
65
65
|
|
|
66
66
|
`upstream.request` 收到完整的 401/403/429/5xx 响应时仍返回 `{status,headers,data}`,不会仅因非 2xx 自动抛错;必须先检查状态,再校验业务数据,最后才缓冲 UNS 写入。此库存示例约定 200 和数组;其他接口按其合同调整,不能把错误响应当业务数据。脚本异常只使用固定文案,不拼接响应正文、认证头或 URL。
|
|
67
67
|
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
| dryRun | boolean | 是 | true 预检;false 提交 |
|
|
10
10
|
| idempotencyKey | string | 否 | 最多 128 字符 |
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
使用已确认目标的回执或查询快照,评估活跃发布/调用依赖。运行中的 Stream 可直接删除,删除会同时撤销后续执行准入;无需先停用。删除请求不自动授予对 Source/UNS 数据的清理权限。
|
|
13
13
|
|
|
14
14
|
```ts
|
|
15
15
|
import { createClient, type Tier0Client, type CloudStreamStopView } from '@tier0/sdk-v2';
|
|
@@ -7,7 +7,9 @@ description: 使用 @tier0/sdk-v2 的 client.uns 完成新架构集合、Schema
|
|
|
7
7
|
|
|
8
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
9
|
|
|
10
|
-
|
|
10
|
+
先按[命名空间指导](references/namespace.md)区分凭证:`namespace()` 走 OpenAPI,但当前不对 App Key 开放;App 查询可用集合走 `dataAppContext`,不能改用同样受限的 `appCollectionsList`。Agent / Personal Key 也须满足当前工作空间权限;不要从“使用 SDK / MCP”推定权限。
|
|
11
|
+
|
|
12
|
+
工业场景中,有管理权限的调用方创建目录或集合前,先查询并复用已有 ISA-95 目录,只创建业务需要且缺失的层级,用现有 `parentId` 挂接;App Key 根据上下文和已知业务范围声明集合的 `directoryPath`。不要要求 App Key 先调用管理查询,也不要按 App 重建一套工厂目录。
|
|
11
13
|
|
|
12
14
|
## 按任务加载参考
|
|
13
15
|
|
|
@@ -17,6 +19,7 @@ UNS 管数据命名空间、集合、Record 和 Sample;外部连接与脚本
|
|
|
17
19
|
| 查询集合、筛选/排序记录 | [查询](references/read.md) | dataAppContext、metadataFind/FindOne(非 App 凭证)、collectionDescribe、recordFind |
|
|
18
20
|
| 改记录、集合名称、扩展 Schema | [修改](references/update.md) | recordUpdateOne、metadataUpdateOne/Many |
|
|
19
21
|
| 删记录、软删除集合 | [删除](references/delete.md) | recordDeleteOne、metadataDeleteOne/Many |
|
|
22
|
+
| 永久删除回收站集合 | [删除](references/delete.md) | metadataPurge、jobs |
|
|
20
23
|
| 恢复集合或记录 | [恢复](references/restore.md) | metadataRestore、recordRestore |
|
|
21
24
|
| 分页、历史采样、事务、写入结果 | [数据工作流](references/data-workflows.md) | sampleFind、recordTransactionExecute |
|
|
22
25
|
| 目录、Topic 元数据及组合事务 | [命名空间](references/namespace.md) | metadata*、metadataBulkWrite、metadataTransact |
|
|
@@ -43,7 +46,7 @@ export async function readAppRecords(collectionId: string) {
|
|
|
43
46
|
1. 记录行是 id/data/revision;filter 与 update 的业务路径用 name、customer.name,不加 data.;系统路径用 _id、_meta.revision。
|
|
44
47
|
2. 改/删使用最近写回执或读取取得的 revision 作为 expectedRevision;创建时可用 returnDocument:'after' 取得快照。历史重放不是当前最新版本,冲突时显式处理;不同操作使用独立意图键,长度上限以当前操作契约为准。
|
|
45
48
|
3. metadataUpdateOne 的 $set.recordSchema 替换完整 Schema,不迁移已有记录;未更新的 sampleSchema/indexes 保留。先预检兼容性。
|
|
46
|
-
4.
|
|
49
|
+
4. metadataDeleteOne/Many 软删除集合并保留 Record/Sample;metadataPurge 永久清理明确 ID 的回收站集合。accepted 只表示任务已受理,须用 jobs 查询逐项结果。Record 删除会物理删除关联 Sample,恢复 Record 不会恢复这些 Sample。
|
|
47
50
|
5. recordFind 使用 pageInfo.hasMore/nextCursor,limit 1–200;sort 为 [['field',1]]。单页不是全量,跨页不是稳定快照。
|
|
48
51
|
6. Sample 的时间是 Unix 毫秒,查询范围 [from,to)。Record 代表业务实体,不用覆盖它来保存历史。
|
|
49
52
|
|
|
@@ -23,7 +23,7 @@ export async function deleteDevice(
|
|
|
23
23
|
}
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
元数据使用 metadataDeleteOne/Many,或 metadataDeleteOne + returnDocument:before 返回删除前对象。它们执行软删除,增加 revision
|
|
26
|
+
元数据使用 metadataDeleteOne/Many,或 metadataDeleteOne + returnDocument:before 返回删除前对象。它们执行软删除,增加 revision,不隐式级联目录后代、引用对象或集合数据。集合有 Record/Sample 或配置引用不阻止明确删除;引用方保留并得到目标已删除的结果。目录和 Topic 保留原有约束,不能自行扩展删除范围。已知 revision 可直接提交,不要求每次重新读取或先 dryRun。
|
|
27
27
|
|
|
28
28
|
```ts
|
|
29
29
|
import { createClient, type Tier0Client } from '@tier0/sdk-v2';
|
|
@@ -51,3 +51,29 @@ metadataDeleteMany 使用明确 filter 和 maxDocs。默认原子;超过上限
|
|
|
51
51
|
业务记录使用 recordDeleteOne/Many,传 collectionId 和明确 filter,编辑型流程同时传 expectedRevision。不要把元数据软删除解释成逐条删除该集合的 Record。Sample 的删除是独立的显式时间范围操作,具体影响遵循当前数据 API 契约。
|
|
52
52
|
|
|
53
53
|
恢复见[恢复](restore.md)。未知写入结果保留原意图键,先按对象身份核对,不自动换键重试。
|
|
54
|
+
|
|
55
|
+
## 永久删除回收站集合
|
|
56
|
+
|
|
57
|
+
Workspace 管理调用方使用 `metadataPurge`,传 `kind:'collection'` 和 1–200 个明确 `ids`。
|
|
58
|
+
只允许回收站集合;清空时先查全分页并固定这次选中的 ID,再按批调用同一个接口。
|
|
59
|
+
不另传 `force`、`cascade`、确认 token,也不要求先清空记录或执行 dryRun。
|
|
60
|
+
运行时 App 凭证不开放此操作;权限由后端统一校验。
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
import { createClient, type Tier0Client } from '@tier0/sdk-v2';
|
|
64
|
+
|
|
65
|
+
export async function purgeCollections(
|
|
66
|
+
ids: string[], intentKey: string, client: Tier0Client = createClient(),
|
|
67
|
+
) {
|
|
68
|
+
const receipt = await client.uns.metadataPurge({
|
|
69
|
+
kind: 'collection', ids, idempotencyKey: intentKey,
|
|
70
|
+
});
|
|
71
|
+
return { state: 'submitted' as const, receipt };
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`status:accepted` 和 `jobId` 表示已受理,不表示已永久删除。通过 `client.uns.jobs`
|
|
76
|
+
查询任务并检查终态和逐项结果;部分失败不能报告整批成功,中断后继续查询原任务或复用原意图键。
|
|
77
|
+
永久删除包括全部 Record、Sample、集合元数据、索引以及指向集合的 App 授权;不连带删除 Topic、Source、Stream。
|
|
78
|
+
清理开始后不能恢复。索引由后台回收,不能仅凭已受理或数据清理回执声称物理空间已全部释放。
|
|
79
|
+
可选 `dryRun:true` 只返回校验结果,不创建清理任务。
|
|
@@ -1,12 +1,28 @@
|
|
|
1
1
|
# 目录、Topic 与元数据事务
|
|
2
2
|
|
|
3
|
+
## 先确定凭证与查询目的
|
|
4
|
+
|
|
5
|
+
`client.uns.namespace()` 对应 `POST /openapi/v2/uns/namespace/query`,不是内部接口。SDK 保留该方法供有权限的工作空间调用方使用;SDK 方法目录不是当前凭证的授权清单。
|
|
6
|
+
|
|
7
|
+
| 凭证 | Namespace / metadata 查询 | 发现并读取集合 |
|
|
8
|
+
| --- | --- | --- |
|
|
9
|
+
| 未绑定 App 的 Agent / Personal Key | 有当前工作空间权限及 Key scope 时可用;Agent Key 还会复核真实负责人的状态和权限 | 按当前工作空间授权查询,再调用 `recordFind` |
|
|
10
|
+
| App Key | 当前不开放;`appCollectionsList` 也不能调用 | `dataAppContext({})` 返回自有及已授权集合、目录路径、结构和有效权限,再按集合 ID 调 `recordFind` |
|
|
11
|
+
| 平台系统 Service Key | 按实际系统凭证授权,不按“Agent 正在调用”推定权限 | 带 Builder 目标时,指定 App 操作可使用目标 App 视角;不代表所有查询都变成 App 视角 |
|
|
12
|
+
|
|
13
|
+
目录位置不产生数据授权。App 自有集合的正常创建流程提供相应授权,但读取仍受凭证权限、App 状态及数据策略约束;不能仅凭 `ownerAppId` 推定任何接口都可调用。SDK 和 MCP 使用相同凭证时仍受服务端授权约束。
|
|
14
|
+
|
|
15
|
+
收到 `403 FORBIDDEN` 且原因为 `operation is not available to App credentials` 时,先核对调用接口与服务端确认的凭证身份。这是操作准入拒绝,不能据此认定 Collection 不可读或没有数据。App 的正确流程见[读取示例](read.md);不要重试同一不允许的操作、改走内部接口或自动换成权限更大的 Key。记录 `requestId`;错误文案仅供诊断,不在程序中匹配文案作权限判断。
|
|
16
|
+
|
|
17
|
+
## 工作空间目录管理
|
|
18
|
+
|
|
3
19
|
目录、Topic、Collection 使用同一套 metadata* 方法,通过 kind 指定类型。Source/Stream 的启停、连接和发布仍使用各自专用生命周期接口;不允许把系统任务、凭据或发布历史当作普通元数据修改。
|
|
4
20
|
|
|
5
21
|
目录、Collection 和 Topic 直接以 name 展示业务名称,不提供 displayName。系统引用用 ID,App 程序标识用独立 alias;名称改动不影响它们。不要用随机串、时间戳或 ID 拼接名称,也不自动用无意义后缀解决重名。
|
|
6
22
|
|
|
7
23
|
工业目录优先按实际 ISA-95 设备层级组织:企业 → 工厂/站点 → 区域/车间 → 工作中心(产线等)→ 工作单元。创建前用 `metadataFind` 查询相关已有目录和集合,结合名称与 `parentId` 确认位置,有分页时继续读取。优先复用,只创建业务需要且缺失的层级,通过 `data.parentId` 挂接;不为凑齐层级编造工厂设备,也不按每个 App 重建一套目录。跨区域共享数据放到适合其业务范围的共同祖先;非工业业务按真实业务结构组织,不用 Level 0–4 控制功能层级替代设备层级。整理时不擅自移动已有数据或更改权限。
|
|
8
24
|
|
|
9
|
-
例如已有“上海工厂 / 装配车间”,新增一条产线的数据只补缺少的产线节点;车间共享集合可直接放在车间下。App 的 `defineCollection` / ensure
|
|
25
|
+
例如已有“上海工厂 / 装配车间”,新增一条产线的数据只补缺少的产线节点;车间共享集合可直接放在车间下。App 的 `defineCollection` / ensure 使用 `directoryPath` 声明从 Workspace 根开始的真实业务目录名称数组,逐级复用或创建目录;不接收 `parentId`,也不默认放在缺少业务范围的根级 App 目录。允许在已知业务范围下使用 App 或模块目录,例如“工厂 / 产线 / MES / 工单”中的 MES;App 目录不能替代已知的工厂、产线范围。其他元数据创建入口仍使用 `data.parentId`。已有集合可按 ID 引用,目录位置不决定归属或权限;声明路径变化不会自动移动已有集合。
|
|
10
26
|
|
|
11
27
|
| 场景 | 方法 |
|
|
12
28
|
| --- | --- |
|
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# 查询集合与记录
|
|
2
2
|
|
|
3
|
+
## App Key:从授权上下文读取
|
|
4
|
+
|
|
5
|
+
App Key 使用 `dataAppContext`,不调用 `namespace`、`metadataFind` 或 `appCollectionsList`。上下文包含可访问集合的名称、目录路径、Schema 和有效权限;按已配置 alias 精确定位,不能随意选择列表第一项。已知集合 ID 时可直接调用 `recordFind`,不要求每次预先加载上下文。
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { createClient, type Tier0Client } from '@tier0/sdk-v2';
|
|
9
|
+
|
|
10
|
+
// 在服务端使用 App Key;alias 是应用配置中的稳定集合别名。
|
|
11
|
+
export async function readAppRecords(alias: string, client: Tier0Client = createClient()) {
|
|
12
|
+
const context = await client.uns.dataAppContext({});
|
|
13
|
+
const collection = context.data.collections.find(item => item.alias === alias);
|
|
14
|
+
if (!collection || !collection.permissions.includes('record:read')) {
|
|
15
|
+
throw new Error('当前 App 上下文没有该别名的可读集合,请核对别名、授权和凭证');
|
|
16
|
+
}
|
|
17
|
+
return client.uns.recordFind({ collectionId: collection.collectionId, limit: 20 });
|
|
18
|
+
}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
未找到别名不等于集合不存在;共享授权可能没有 alias,应使用明确配置的集合 ID。上下文只是发现结果,后续请求仍由服务端重新检查权限。网络或权限错误应传播,不转换成“零条记录”。此例只读取第一页,后续按返回的 `pageInfo` 继续查询。
|
|
22
|
+
|
|
23
|
+
## 工作空间凭证:查询元数据与记录
|
|
24
|
+
|
|
3
25
|
| 方法 | 必要输入 | 返回 data |
|
|
4
26
|
| --- | --- | --- |
|
|
5
27
|
| metadataFind | kinds;可选 filter、sort、projection、page、state | items、pageInfo |
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
# 启用、停用 Source
|
|
2
|
-
|
|
3
|
-
方法:`source.state.update`;POST `/openapi/v2/uns/sources/state/update`。
|
|
4
|
-
|
|
5
|
-
| 字段 | 类型 | 必填 | 说明 |
|
|
6
|
-
| --- | --- | --- | --- |
|
|
7
|
-
| dryRun | boolean | 否 | 默认 false 提交;true 仅预检 |
|
|
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 检查。提供的回执始终严格校验,确认过期或依赖变化不能自动刷新同意。
|