@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.
Files changed (170) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +13 -11
  3. package/dist/cjs/app-identity-contract.generated.js.map +1 -1
  4. package/dist/cjs/capabilities.js.map +1 -1
  5. package/dist/cjs/cloud-server.js.map +1 -1
  6. package/dist/cjs/cloud-transport.d.ts +21 -0
  7. package/dist/cjs/cloud-transport.js +81 -1
  8. package/dist/cjs/cloud-transport.js.map +1 -1
  9. package/dist/cjs/controls.js.map +1 -1
  10. package/dist/cjs/data/bootstrap.d.ts +4 -0
  11. package/dist/cjs/data/bootstrap.js +15 -6
  12. package/dist/cjs/data/bootstrap.js.map +1 -1
  13. package/dist/cjs/data/call.d.ts +26 -9
  14. package/dist/cjs/data/call.js +179 -14
  15. package/dist/cjs/data/call.js.map +1 -1
  16. package/dist/cjs/data/client.d.ts +44 -2
  17. package/dist/cjs/data/client.js +87 -4
  18. package/dist/cjs/data/client.js.map +1 -1
  19. package/dist/cjs/data/collection.d.ts +151 -14
  20. package/dist/cjs/data/collection.js +213 -50
  21. package/dist/cjs/data/collection.js.map +1 -1
  22. package/dist/cjs/data/context.d.ts +4 -2
  23. package/dist/cjs/data/context.js +14 -3
  24. package/dist/cjs/data/context.js.map +1 -1
  25. package/dist/cjs/data/define.d.ts +9 -1
  26. package/dist/cjs/data/define.js.map +1 -1
  27. package/dist/cjs/data/error.d.ts +30 -12
  28. package/dist/cjs/data/error.js +61 -63
  29. package/dist/cjs/data/error.js.map +1 -1
  30. package/dist/cjs/data/index.d.ts +6 -3
  31. package/dist/cjs/data/index.js +7 -1
  32. package/dist/cjs/data/index.js.map +1 -1
  33. package/dist/cjs/data/schema.d.ts +7 -1
  34. package/dist/cjs/data/schema.js +54 -4
  35. package/dist/cjs/data/schema.js.map +1 -1
  36. package/dist/cjs/data/subscribe.d.ts +108 -0
  37. package/dist/cjs/data/subscribe.js +270 -0
  38. package/dist/cjs/data/subscribe.js.map +1 -0
  39. package/dist/cjs/mq/broker.js.map +1 -1
  40. package/dist/cjs/mq/client.js.map +1 -1
  41. package/dist/cjs/mq/index.js.map +1 -1
  42. package/dist/cjs/mq/types.js.map +1 -1
  43. package/dist/cjs/runtime-context.js.map +1 -1
  44. package/dist/cjs/runtime-env.js.map +1 -1
  45. package/dist/cjs/uns-v2/client.d.ts +90 -12
  46. package/dist/cjs/uns-v2/client.js +238 -63
  47. package/dist/cjs/uns-v2/client.js.map +1 -1
  48. package/dist/cjs/uns-v2/codec.js.map +1 -1
  49. package/dist/cjs/uns-v2/generated.d.ts +9619 -6923
  50. package/dist/cjs/uns-v2/generated.js +124 -119
  51. package/dist/cjs/uns-v2/generated.js.map +1 -1
  52. package/dist/cjs/uns-v2/index.js.map +1 -1
  53. package/dist/cjs/uns-v2/sse.d.ts +20 -0
  54. package/dist/cjs/uns-v2/sse.js +65 -0
  55. package/dist/cjs/uns-v2/sse.js.map +1 -0
  56. package/dist/esm/app-identity-contract.generated.js.map +1 -1
  57. package/dist/esm/capabilities.js.map +1 -1
  58. package/dist/esm/cloud-server.js.map +1 -1
  59. package/dist/esm/cloud-transport.d.ts +21 -0
  60. package/dist/esm/cloud-transport.js +81 -1
  61. package/dist/esm/cloud-transport.js.map +1 -1
  62. package/dist/esm/controls.js.map +1 -1
  63. package/dist/esm/data/bootstrap.d.ts +4 -0
  64. package/dist/esm/data/bootstrap.js +14 -5
  65. package/dist/esm/data/bootstrap.js.map +1 -1
  66. package/dist/esm/data/call.d.ts +26 -9
  67. package/dist/esm/data/call.js +177 -13
  68. package/dist/esm/data/call.js.map +1 -1
  69. package/dist/esm/data/client.d.ts +44 -2
  70. package/dist/esm/data/client.js +89 -7
  71. package/dist/esm/data/client.js.map +1 -1
  72. package/dist/esm/data/collection.d.ts +151 -14
  73. package/dist/esm/data/collection.js +212 -49
  74. package/dist/esm/data/collection.js.map +1 -1
  75. package/dist/esm/data/context.d.ts +4 -2
  76. package/dist/esm/data/context.js +13 -3
  77. package/dist/esm/data/context.js.map +1 -1
  78. package/dist/esm/data/define.d.ts +9 -1
  79. package/dist/esm/data/define.js.map +1 -1
  80. package/dist/esm/data/error.d.ts +30 -12
  81. package/dist/esm/data/error.js +61 -63
  82. package/dist/esm/data/error.js.map +1 -1
  83. package/dist/esm/data/index.d.ts +6 -3
  84. package/dist/esm/data/index.js +3 -1
  85. package/dist/esm/data/index.js.map +1 -1
  86. package/dist/esm/data/schema.d.ts +7 -1
  87. package/dist/esm/data/schema.js +54 -4
  88. package/dist/esm/data/schema.js.map +1 -1
  89. package/dist/esm/data/subscribe.d.ts +108 -0
  90. package/dist/esm/data/subscribe.js +265 -0
  91. package/dist/esm/data/subscribe.js.map +1 -0
  92. package/dist/esm/mq/broker.js.map +1 -1
  93. package/dist/esm/mq/client.js.map +1 -1
  94. package/dist/esm/mq/index.js.map +1 -1
  95. package/dist/esm/mq/types.js.map +1 -1
  96. package/dist/esm/runtime-context.js.map +1 -1
  97. package/dist/esm/runtime-env.js.map +1 -1
  98. package/dist/esm/uns-v2/client.d.ts +90 -12
  99. package/dist/esm/uns-v2/client.js +236 -63
  100. package/dist/esm/uns-v2/client.js.map +1 -1
  101. package/dist/esm/uns-v2/codec.js.map +1 -1
  102. package/dist/esm/uns-v2/generated.d.ts +9619 -6923
  103. package/dist/esm/uns-v2/generated.js +123 -118
  104. package/dist/esm/uns-v2/generated.js.map +1 -1
  105. package/dist/esm/uns-v2/index.js.map +1 -1
  106. package/dist/esm/uns-v2/sse.d.ts +20 -0
  107. package/dist/esm/uns-v2/sse.js +60 -0
  108. package/dist/esm/uns-v2/sse.js.map +1 -0
  109. package/package.json +29 -16
  110. package/skills/tier0-sdk-v2/SKILL.md +14 -14
  111. package/skills/{tier0-sdk-v2-data → tier0-sdk-v2/tier0-sdk-v2-data}/SKILL.md +3 -2
  112. package/skills/{tier0-sdk-v2-data → tier0-sdk-v2/tier0-sdk-v2-data}/references/consistency.md +3 -3
  113. package/skills/{tier0-sdk-v2-data → tier0-sdk-v2/tier0-sdk-v2-data}/references/errors.md +23 -10
  114. package/skills/{tier0-sdk-v2-data → tier0-sdk-v2/tier0-sdk-v2-data}/references/modeling.md +36 -0
  115. package/skills/tier0-sdk-v2/tier0-sdk-v2-data/references/read-write.md +172 -0
  116. package/skills/{tier0-sdk-v2-mq → tier0-sdk-v2/tier0-sdk-v2-mq}/references/quickstart.md +1 -1
  117. package/skills/{tier0-sdk-v2-source → tier0-sdk-v2/tier0-sdk-v2-source}/SKILL.md +3 -3
  118. package/skills/{tier0-sdk-v2-source → tier0-sdk-v2/tier0-sdk-v2-source}/references/create.md +3 -6
  119. package/skills/tier0-sdk-v2/tier0-sdk-v2-source/references/delete.md +39 -0
  120. package/skills/tier0-sdk-v2/tier0-sdk-v2-source/references/state.md +36 -0
  121. package/skills/tier0-sdk-v2/tier0-sdk-v2-source/references/update.md +80 -0
  122. package/skills/{tier0-sdk-v2-stream → tier0-sdk-v2/tier0-sdk-v2-stream}/SKILL.md +6 -6
  123. package/skills/{tier0-sdk-v2-stream → tier0-sdk-v2/tier0-sdk-v2-stream}/references/create.md +15 -14
  124. package/skills/{tier0-sdk-v2-stream → tier0-sdk-v2/tier0-sdk-v2-stream}/references/deploy.md +11 -9
  125. package/skills/{tier0-sdk-v2-stream → tier0-sdk-v2/tier0-sdk-v2-stream}/references/lifecycle.md +2 -1
  126. package/skills/{tier0-sdk-v2-stream → tier0-sdk-v2/tier0-sdk-v2-stream}/references/update.md +5 -6
  127. package/skills/tier0-sdk-v2/tier0-sdk-v2-uns/SKILL.md +55 -0
  128. package/skills/tier0-sdk-v2/tier0-sdk-v2-uns/references/create.md +57 -0
  129. package/skills/{tier0-sdk-v2-uns → tier0-sdk-v2/tier0-sdk-v2-uns}/references/data-workflows.md +3 -7
  130. package/skills/tier0-sdk-v2/tier0-sdk-v2-uns/references/delete.md +53 -0
  131. package/skills/tier0-sdk-v2/tier0-sdk-v2-uns/references/namespace.md +38 -0
  132. package/skills/tier0-sdk-v2/tier0-sdk-v2-uns/references/read.md +34 -0
  133. package/skills/tier0-sdk-v2/tier0-sdk-v2-uns/references/restore.md +39 -0
  134. package/skills/tier0-sdk-v2/tier0-sdk-v2-uns/references/update.md +62 -0
  135. package/skills/tier0-sdk-v2-data/references/read-write.md +0 -72
  136. package/skills/tier0-sdk-v2-source/references/delete.md +0 -47
  137. package/skills/tier0-sdk-v2-source/references/state.md +0 -37
  138. package/skills/tier0-sdk-v2-source/references/update.md +0 -61
  139. package/skills/tier0-sdk-v2-uns/SKILL.md +0 -53
  140. package/skills/tier0-sdk-v2-uns/references/create.md +0 -85
  141. package/skills/tier0-sdk-v2-uns/references/delete.md +0 -69
  142. package/skills/tier0-sdk-v2-uns/references/namespace.md +0 -75
  143. package/skills/tier0-sdk-v2-uns/references/read.md +0 -32
  144. package/skills/tier0-sdk-v2-uns/references/restore.md +0 -41
  145. package/skills/tier0-sdk-v2-uns/references/update.md +0 -86
  146. /package/skills/{tier0-sdk-v2-agent → tier0-sdk-v2/tier0-sdk-v2-agent}/SKILL.md +0 -0
  147. /package/skills/{tier0-sdk-v2-agent → tier0-sdk-v2/tier0-sdk-v2-agent}/references/results.md +0 -0
  148. /package/skills/{tier0-sdk-v2-controls → tier0-sdk-v2/tier0-sdk-v2-controls}/SKILL.md +0 -0
  149. /package/skills/{tier0-sdk-v2-data → tier0-sdk-v2/tier0-sdk-v2-data}/references/schema-evolution.md +0 -0
  150. /package/skills/{tier0-sdk-v2-files → tier0-sdk-v2/tier0-sdk-v2-files}/SKILL.md +0 -0
  151. /package/skills/{tier0-sdk-v2-files → tier0-sdk-v2/tier0-sdk-v2-files}/references/delete.md +0 -0
  152. /package/skills/{tier0-sdk-v2-files → tier0-sdk-v2/tier0-sdk-v2-files}/references/download.md +0 -0
  153. /package/skills/{tier0-sdk-v2-files → tier0-sdk-v2/tier0-sdk-v2-files}/references/multipart.md +0 -0
  154. /package/skills/{tier0-sdk-v2-files → tier0-sdk-v2/tier0-sdk-v2-files}/references/upload.md +0 -0
  155. /package/skills/{tier0-sdk-v2-files → tier0-sdk-v2/tier0-sdk-v2-files}/references/url.md +0 -0
  156. /package/skills/{tier0-sdk-v2-members → tier0-sdk-v2/tier0-sdk-v2-members}/SKILL.md +0 -0
  157. /package/skills/{tier0-sdk-v2-members → tier0-sdk-v2/tier0-sdk-v2-members}/references/app-members.md +0 -0
  158. /package/skills/{tier0-sdk-v2-members → tier0-sdk-v2/tier0-sdk-v2-members}/references/platform-members.md +0 -0
  159. /package/skills/{tier0-sdk-v2-mq → tier0-sdk-v2/tier0-sdk-v2-mq}/SKILL.md +0 -0
  160. /package/skills/{tier0-sdk-v2-notifications → tier0-sdk-v2/tier0-sdk-v2-notifications}/SKILL.md +0 -0
  161. /package/skills/{tier0-sdk-v2-notifications → tier0-sdk-v2/tier0-sdk-v2-notifications}/references/get.md +0 -0
  162. /package/skills/{tier0-sdk-v2-notifications → tier0-sdk-v2/tier0-sdk-v2-notifications}/references/send.md +0 -0
  163. /package/skills/{tier0-sdk-v2-source → tier0-sdk-v2/tier0-sdk-v2-source}/references/changes.md +0 -0
  164. /package/skills/{tier0-sdk-v2-source → tier0-sdk-v2/tier0-sdk-v2-source}/references/protocols.md +0 -0
  165. /package/skills/{tier0-sdk-v2-source → tier0-sdk-v2/tier0-sdk-v2-source}/references/read.md +0 -0
  166. /package/skills/{tier0-sdk-v2-stream → tier0-sdk-v2/tier0-sdk-v2-stream}/references/delete.md +0 -0
  167. /package/skills/{tier0-sdk-v2-stream → tier0-sdk-v2/tier0-sdk-v2-stream}/references/read.md +0 -0
  168. /package/skills/{tier0-sdk-v2-system → tier0-sdk-v2/tier0-sdk-v2-system}/SKILL.md +0 -0
  169. /package/skills/{tier0-sdk-v2-system → tier0-sdk-v2/tier0-sdk-v2-system}/references/info.md +0 -0
  170. /package/skills/{tier0-sdk-v2-system → tier0-sdk-v2/tier0-sdk-v2-system}/references/whoami.md +0 -0
@@ -0,0 +1,38 @@
1
+ # 目录、Topic 与元数据事务
2
+
3
+ 目录、Topic、Collection 使用同一套 metadata* 方法,通过 kind 指定类型。Source/Stream 的启停、连接和发布仍使用各自专用生命周期接口;不允许把系统任务、凭据或发布历史当作普通元数据修改。
4
+
5
+ | 场景 | 方法 |
6
+ | --- | --- |
7
+ | 单条/批量新增 | metadataInsertOne / metadataInsertMany |
8
+ | 单条/批量条件更新 | metadataUpdateOne / metadataUpdateMany |
9
+ | 更新并读取原子结果 | metadataFindOneAndUpdate / metadataFindOneAndReplace |
10
+ | 同 kind 混合写入 | metadataBulkWrite |
11
+ | 跨 kind、有依赖的组合操作 | metadataTransact |
12
+ | 软删除并返回原对象 | metadataFindOneAndDelete |
13
+ | 按明确 ID 批量恢复 | metadataRestore |
14
+
15
+ 普通写操作只指定一个 kind。insertMany/updateMany/deleteMany/restore 固定原子执行;bulkWrite 默认 atomic=true,原子批次不接受 ordered=false。bulkWrite 显式 atomic=false 时 ordered=true 遇错停止,false 继续,并检查 results/errors。非原子执行有任一项已提交后失败时,`execution` 为 committed、`retry.safe` 为 false,错误的 data 是累计结果:序号小于 `nextIndex` 且不在 errors 里的操作已提交;有 `unknownUntil` 时 `[nextIndex, unknownUntil)` 结局未知,用同一个幂等键重试(不带键时只有按 id 的操作能先读回再续做,所以非原子批次始终带幂等键);没有 `unknownUntil` 时从 `nextIndex` 续做。第一块就结局未知时 `execution` 为 unknown。批次最多 200 项。不要给所有对象套一个相同 expectedRevision:需要逐项版本检查时在 bulkWrite 中分别提供。
16
+
17
+ ```ts
18
+ import { createClient, type Tier0Client } from '@tier0/sdk-v2';
19
+
20
+ export async function createDirectoryAndTopic(
21
+ directoryId: string, topicId: string, collectionId: string, intentKey: string,
22
+ client: Tier0Client = createClient(),
23
+ ) {
24
+ return client.uns.metadataTransact({
25
+ idempotencyKey: intentKey,
26
+ operations: [
27
+ { opId: 'directory', action: 'insertOne', kind: 'directory', id: directoryId,
28
+ data: { name: '设备目录', parentId: null } },
29
+ { opId: 'topic', action: 'insertOne', kind: 'topic', id: topicId, dependsOn: ['directory'],
30
+ data: { name: '设备视图', parentId: { ref: 'directory' }, viewMode: 'records', collectionId } },
31
+ ],
32
+ });
33
+ }
34
+ ```
35
+
36
+ transact 始终原子,结果通过 opId 对应输入;dryRun=true 校验,不预留 ID 或名称。正式执行仍重新检查。创建结果引用仅用于契约支持的关系字段,不能把任意字符串或脚本当引用。
37
+
38
+ 名称、父目录、Schema、引用与权限由服务端校验。移动不改变对象 ID;删除目录不隐式清空后代;删除 Topic 不删除所引用的 Collection。引用查询仍使用 references 专用方法。
@@ -0,0 +1,34 @@
1
+ # 查询集合与记录
2
+
3
+ | 方法 | 必要输入 | 返回 data |
4
+ | --- | --- | --- |
5
+ | metadataFind | kinds;可选 filter、sort、projection、page、state | items、pageInfo |
6
+ | metadataFindOne | kind、filter;可选 state | value,未找到为 null |
7
+ | metadataCount / metadataDistinct | kind/kinds 与查询条件;distinct 还指定 field | count / values、pageInfo |
8
+ | collectionDescribe | collectionId | 集合结构和数据能力 |
9
+ | recordFind | collectionId;可选 filter、sort、limit、cursor | items、pageInfo |
10
+
11
+ 元数据查询配置字段写 name、parentId;系统字段写 _id、_meta.kind、_meta.revision。返回资源的配置在 value.data 或 items[].data 中。state 为 active/deleted/all,默认 active。对象身份始终是 kind + id。
12
+
13
+ ```ts
14
+ import { createClient, type Tier0Client } from '@tier0/sdk-v2';
15
+
16
+ export async function readDevices(collectionId: string, client: Tier0Client = createClient()) {
17
+ const collection = await client.uns.metadataFindOne({ kind: 'collection', filter: { _id: collectionId } });
18
+ if (!collection.data.value) throw new Error('集合不存在或不可见');
19
+ const records = await client.uns.recordFind({
20
+ collectionId, filter: { enabled: true }, sort: [['name', 1]], limit: 20,
21
+ });
22
+ return { definition: collection.data.value.data, items: records.data.items, pageInfo: records.data.pageInfo };
23
+ }
24
+
25
+ export async function listCollections(page = 1, client: Tier0Client = createClient()) {
26
+ return client.uns.metadataFind({
27
+ kinds: ['collection'], state: 'active', sort: [['_id', 1]], page: { mode: 'offset', page, size: 20 },
28
+ });
29
+ }
30
+ ```
31
+
32
+ 按 pageInfo.hasMore/nextPage 继续元数据分页;不要把第一页当全量。recordFind 的游标循环见[数据工作流](data-workflows.md)。sort 是数组元组。
33
+
34
+ 以上 metadataFind/FindOne 示例用于 Workspace 集成(会话、personal / agent Key)。App 凭证不能调用 metadataFind/FindOne:用 dataAppContext 精确选择授权 alias,集合的 revision、indexStatus、ttl 也在其中。数据授权不等于元数据管理授权。网络/权限错误必须传播,不能用空数组掩盖错误。
@@ -0,0 +1,39 @@
1
+ # 恢复元数据与记录
2
+
3
+ metadataRestore 接收 kind、items[{id,expectedRevision?}],支持一个或多个明确 ID。revision 应从 state=deleted 的当前对象读取。恢复重新校验名称、父目录、Schema 与引用;不会恢复其他对象,冲突时原子批次回滚。
4
+
5
+ ```ts
6
+ import { createClient, type Tier0Client } from '@tier0/sdk-v2';
7
+
8
+ export async function restoreCollection(
9
+ id: string, intentKey: string, client: Tier0Client = createClient(),
10
+ ) {
11
+ const deleted = (await client.uns.metadataFindOne({ kind: 'collection', filter: { _id: id }, state: 'deleted' })).data.value;
12
+ if (!deleted) throw new Error('未找到待恢复集合');
13
+ const receipt = await client.uns.metadataRestore({
14
+ kind: 'collection', items: [{ id, expectedRevision: deleted.revision }], idempotencyKey: intentKey,
15
+ });
16
+ if (receipt.data.status !== 'committed' || String(receipt.data.restoredCount) !== '1') {
17
+ return { state: 'unconfirmed' as const, receipt };
18
+ }
19
+ const current = (await client.uns.metadataFindOne({ kind: 'collection', filter: { _id: id } })).data.value;
20
+ if (!current) throw new Error('恢复有回执,读回未确认');
21
+ return { state: 'committed' as const, receipt, current };
22
+ }
23
+
24
+ export async function restoreDevice(
25
+ collectionId: string, id: string, intentKey: string, client: Tier0Client = createClient(),
26
+ ) {
27
+ const receipt = await client.uns.recordRestore({
28
+ collectionId, ids: [id], dryRun: false, idempotencyKey: intentKey,
29
+ });
30
+ if (receipt.data.status !== 'committed' || !receipt.data.restoredIds.includes(id)) {
31
+ return { state: 'unconfirmed' as const, receipt };
32
+ }
33
+ const found = await client.uns.recordFind({ collectionId, filter: { _id: id }, limit: 1 });
34
+ if (!found.data.items[0]) throw new Error('恢复有回执,读回未确认');
35
+ return { state: 'committed' as const, receipt, current: found.data.items[0] };
36
+ }
37
+ ```
38
+
39
+ Record 恢复使用 recordRestore 的 ids(最多 100 条),重新校验当前 Schema 和有效唯一索引,冲突整批回滚。新增同 ID 或 upsert 不会隐式恢复对象。幂等键每个新意图独立。
@@ -0,0 +1,62 @@
1
+ # 修改记录、元数据和 Schema
2
+
3
+ 元数据单条使用 metadataUpdateOne 或 metadataFindOneAndUpdate;后者原子返回 value,returnDocument 可选 before/after。批量相同更新使用 metadataUpdateMany;不同更新使用 metadataBulkWrite。完整替换使用 metadataReplaceOne/metadataFindOneAndReplace,replacement 是完整可写配置。
4
+
5
+ update 首版支持 $set/$unset/$setOnInsert。字段使用裸配置路径;_id/_meta 为只读。写前读取 revision,编辑页面携带 expectedRevision,冲突后重新比较。单条 upsert 需明确 _id 等值条件,新增内容满足完整创建约束;同 ID 的软删除对象必须显式恢复。
6
+
7
+ ```ts
8
+ import { createClient, type Tier0Client } from '@tier0/sdk-v2';
9
+
10
+ export async function renameCollection(
11
+ id: string, name: string, intentKey: string, client: Tier0Client = createClient(),
12
+ ) {
13
+ const current = (await client.uns.metadataFindOne({ kind: 'collection', filter: { _id: id } })).data.value;
14
+ if (!current) throw new Error('集合不存在或不可见');
15
+ return client.uns.metadataFindOneAndUpdate({
16
+ kind: 'collection', filter: { _id: id }, expectedRevision: current.revision,
17
+ update: { $set: { name } }, returnDocument: 'after', upsert: false, idempotencyKey: intentKey,
18
+ });
19
+ }
20
+ ```
21
+
22
+ 修改 Schema 时先构造完整 recordSchema,再用 $set 更新该字段。它不迁移已有记录,必须兼容保留数据与引用。没有修改的 sampleSchema/indexes 保持原值。先 dryRun,再用独立正式意图键提交。
23
+
24
+ ```ts
25
+ import { createClient, type Tier0Client, type Schema } from '@tier0/sdk-v2';
26
+
27
+ export async function replaceRecordSchema(
28
+ id: string, recordSchema: Schema, intentKey: string, client: Tier0Client = createClient(),
29
+ ) {
30
+ const current = (await client.uns.metadataFindOne({ kind: 'collection', filter: { _id: id } })).data.value;
31
+ if (!current) throw new Error('集合不存在或不可见');
32
+ const request = { kind: 'collection' as const, filter: { _id: id },
33
+ expectedRevision: current.revision, update: { $set: { recordSchema } } };
34
+ const preview = await client.uns.metadataUpdateOne({ ...request, dryRun: true });
35
+ if (preview.data.status !== 'validated') throw new Error('Schema 预检未通过');
36
+ return client.uns.metadataUpdateOne({ ...request, dryRun: false, idempotencyKey: intentKey });
37
+ }
38
+ ```
39
+
40
+ 业务记录仍使用 recordUpdateOne:传 collectionId/filter/update,按需携带 expectedRevision。metadataUpdateOne 不能更新 Record 的业务内容。matchedCount 和 modifiedCount 不等价;值未变化时 modifiedCount 可为 0。
41
+
42
+ ```ts
43
+ import { createClient, type Tier0Client } from '@tier0/sdk-v2';
44
+
45
+ export async function renameDevice(
46
+ collectionId: string, id: string, name: string, intentKey: string,
47
+ client: Tier0Client = createClient(),
48
+ ) {
49
+ const current = (await client.uns.recordFind({ collectionId, filter: { _id: id }, limit: 1 })).data.items[0];
50
+ if (!current) throw new Error('记录不存在或不可见');
51
+ const receipt = await client.uns.recordUpdateOne({
52
+ collectionId, filter: { _id: id }, expectedRevision: current.revision,
53
+ update: { $set: { name } }, upsert: false, dryRun: false, idempotencyKey: intentKey,
54
+ });
55
+ if (receipt.data.status !== 'committed' || String(receipt.data.matchedCount) !== '1') {
56
+ return { state: 'unconfirmed' as const, receipt };
57
+ }
58
+ const after = await client.uns.recordFind({ collectionId, filter: { _id: id }, limit: 1 });
59
+ if (after.data.items[0]?.data.name !== name) throw new Error('读回名称与目标不一致');
60
+ return { state: 'committed' as const, receipt, current: after.data.items[0] };
61
+ }
62
+ ```
@@ -1,72 +0,0 @@
1
- # 读写
2
-
3
- `data.collection(declaration)` 返回按声明推断类型的句柄(也可用 alias 字符串,类型由调用方指定)。读到的记录是扁平文档 `{ _id, ...字段, _meta }`;过滤、排序、投影都用同样的路径(`status`、`customer.id`、`_id`、`_meta.updatedAt`),不加 `data.` 前缀。
4
-
5
- ```ts
6
- import { z } from 'zod';
7
- import { createDataClient, defineCollection } from '@tier0/sdk-v2/data';
8
-
9
- const workOrders = defineCollection({
10
- alias: 'work_orders',
11
- schema: z.object({
12
- code: z.string(),
13
- status: z.enum(['DRAFT', 'RELEASED', 'DONE']),
14
- line: z.object({ id: z.string(), name: z.string() }),
15
- targetQty: z.number().int().min(1),
16
- notes: z.string().optional(),
17
- }),
18
- indexes: [{ path: 'code', unique: true }, { path: 'status' }],
19
- });
20
- const data = createDataClient({ collections: [workOrders] });
21
- const orders = data.collection(workOrders);
22
-
23
- export async function dashboard(page: number) {
24
- const recent = await orders.find({ status: { $in: ['RELEASED', 'DONE'] } }, { sort: { '_meta.createdAt': -1 }, limit: 50 }).toArray();
25
- const listPage = await orders.findPage({ status: 'RELEASED' }, { sort: { code: 1 }, page, pageSize: 20 });
26
- const released = await orders.countDocuments({ status: 'RELEASED' });
27
- const totals = await orders
28
- .aggregate<{ _id: string; total: number }>([{ $match: { status: 'DONE' } }, { $group: { _id: '$line.id', total: { $sum: '$targetQty' } } }])
29
- .toArray();
30
- return { recent, listPage, released, totals };
31
- }
32
-
33
- export async function addNote(id: string, notes: string, revision: number) {
34
- return orders.updateOne({ _id: id }, { $set: { notes } }, { expectedRevision: revision });
35
- }
36
- ```
37
-
38
- ## 方法
39
-
40
- | 读取 | 说明 |
41
- | --- | --- |
42
- | `find(filter, { sort, limit, projection })` | 游标,每页最多 200 条;列表读取始终给 `limit` |
43
- | `findOne(filter)` | 无匹配返回 `null` |
44
- | `findPage(filter, { sort, page, pageSize })` | 带总数的页码分页 `{ items, total, page, pageSize }`,pageSize 最多 200 |
45
- | `countDocuments(filter)`、`distinct(field, filter)` | `distinct` 会取完所有页 |
46
- | `aggregate(pipeline)` | `$match` `$sort` `$group` `$project` `$set` `$unwind` `$skip` `$limit` `$count` `$lookup`;`$lookup.from` 写声明或 alias,数据层换成集合 ID。`$exists` / `$distinct` / `$frequencies` 阶段不执行,改用 `countDocuments`、`distinct` 或 `$group` |
47
-
48
- | 写入 | 说明 |
49
- | --- | --- |
50
- | `insertOne(doc)`、`insertMany(docs)` | 可带 `_id`;`insertMany` 最多 200 条、原子 |
51
- | `updateOne` / `updateMany` / `replaceOne` | 更新用操作符(`$set` `$inc` `$push` `$unset`…);`replaceOne` 会丢掉其他代码版本写入的字段 |
52
- | `deleteOne` / `deleteMany` | 软删除,App Key 需要 `uns.delete` |
53
- | `findOneAndUpdate` / `findOneAndReplace` / `findOneAndDelete` | 原子读改写,默认返回更新后的文档 |
54
- | `bulkWrite(ops)` | `insertOne` / `updateOne` / `replaceOne` / `deleteOne` 混合,最多 200 项、原子 |
55
- | `data.transact(steps)` | 跨集合原子写,见 [一致性](consistency.md) |
56
-
57
- ## 批量与原子性
58
-
59
- | 调用 | 上限 | 原子 |
60
- | --- | --- | --- |
61
- | `insertMany`、`bulkWrite`、`data.transact` | 每次 ≤200 项,超过在发送前抛 `BATCH_LIMIT_EXCEEDED` | 是,全部或全不 |
62
- | `updateMany`、`deleteMany` | 无上限,翻页处理所有匹配 | 否,逐条提交;失败抛 `WRITE_ERRORS` 并报告已提交的数量 |
63
-
64
- 超过 200 条按 200 分块写入(循环分块即可),每块用固定 `_id` 或独立 `idempotencyKey`,重试不会重复。多条文档必须一起成功或失败时,用一次 `data.transact`(≤200 步),不要用 `updateMany`。不要在循环里逐条调用数据层:读用一次 `$in`,写用 `insertMany` / `updateMany` / `bulkWrite` 或 `data.transact`。
65
-
66
- ## 过滤
67
-
68
- 支持 `$eq $ne $gt $gte $lt $lte $in $nin $exists $all $size $elemMatch $not $and $or $nor` 和 `$contains`。`$contains` 是子串匹配(1–256 字节,无通配符和正则),只对 ASCII 字母忽略大小写;缺失、null、非字符串值不匹配;只能用于声明为字符串的字段,用在数组或数字字段上返回 `INVALID_REQUEST`。它不能使用索引,大集合上可能触发 15 秒语句超时(`QUERY_TIMEOUT`),要和有索引的条件一起用。`$in` 最多 200 个值,排序最多 4 个字段(`toSortPairs` 也导出给自定义查询转换使用)。
69
-
70
- ## 校验
71
-
72
- 写入前按声明严格校验:未知字段、类型错误、未声明的更新路径抛 `ZodError`(HTTP 400 语义)。读取不严格解析:记录可能含其他代码版本写入的字段、新的枚举值,或缺少后来新增的字段。枚举渲染要有兜底标签,后加的字段按可能缺失处理。
@@ -1,47 +0,0 @@
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 | 是 | get 返回的修订 |
11
- | items[].confirmedImpactRevision | string | 是 | action=delete 的影响回执 |
12
- | mode | auto/sync | 否 | 不支持 async |
13
- | allowPartial | false | 否 | 不支持部分提交 |
14
- | idempotencyKey | string | 否 | 最多 128 字符 |
15
-
16
- 先 get → impact(action=delete) → [遍历并评估影响](changes.md)。存在 Stream 依赖时遵守服务端保护,不自动删除依赖流。
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
- confirmedImpactRevision: string, intentKey: string, client: Tier0Client = createClient(),
24
- ) {
25
- const receipt = await client.source.delete({
26
- dryRun: false, mode: 'sync', allowPartial: false, idempotencyKey: intentKey,
27
- items: [{ id, expectedRevision, confirmedImpactRevision }],
28
- });
29
- if (receipt.data.status !== 'committed' || receipt.data.results?.some(item => item.error)) {
30
- return { state: 'unconfirmed' as const, receipt };
31
- }
32
- let page = 1;
33
- while (true) {
34
- const { data } = await client.source.documents.list({
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 };
44
- }
45
- ```
46
-
47
- 返回 Outcome,HTTP 200/validated/accepted 不等于完成。归档列表找到 ID 是额外核验;读回不可见时保留提交回执,报告核验未完成。不能把任意 get 异常当成成功。
@@ -1,37 +0,0 @@
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
- 先 get,以相同修订执行 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', confirmedImpactRevision: string,
22
- intentKey: string, client: Tier0Client = createClient(),
23
- ) {
24
- const receipt = await client.source.state.update({
25
- dryRun: false, mode: 'sync', allowPartial: false, idempotencyKey: intentKey,
26
- items: [{ id, expectedRevision, desiredState, confirmedImpactRevision }],
27
- });
28
- if (receipt.data.status !== 'committed' || receipt.data.results?.some(item => item.error)) {
29
- return { state: 'unconfirmed' as const, receipt };
30
- }
31
- const current = await client.source.documents.get({ id });
32
- if (current.data.desiredState !== desiredState) throw new Error('读回状态与目标不一致');
33
- return { state: 'committed' as const, receipt, current: current.data };
34
- }
35
- ```
36
-
37
- 响应为 Outcome;accepted/jobId 仅表示受理,继续查询作业。enabled 是期望状态;健康与连通性另用 observations/runtime 检查。
@@ -1,61 +0,0 @@
1
- # 修改 Source
2
-
3
- 方法:`source.documents.update`;POST `/openapi/v2/uns/sources/documents/update`。
4
-
5
- | 字段 | 类型 | 必填 | 说明 |
6
- | --- | --- | --- | --- |
7
- | id | string | 是 | get 返回的 ID |
8
- | document | CloudSourceDocument | 是 | 完整新文档,保留未改字段和认证引用 |
9
- | expectedRevision | integer | 是 | 读取时的 revision,原样传回 |
10
- | confirmedImpactRevision | string | 是 | 相同文档和修订的 impact 回执 |
11
- | dryRun | boolean | 是 | true 预检;false 提交 |
12
- | idempotencyKey | string | 否 | 最多 128 字符 |
13
-
14
- ## 示例:修改名称并保留连接
15
-
16
- prepare 读取文档、形成新文档并收集全部影响分页。在已有授权范围内评估 counts、restrictedCount 和引用后调用 commit,不自动串联未经授权的操作。影响字段说明见[影响分析](changes.md)。
17
-
18
- ```ts
19
- import { createClient, type Tier0Client, type CloudSourceDocument, type UnsV2Request } from '@tier0/sdk-v2';
20
-
21
- export async function prepareSourceRename(id: string, name: string, client: Tier0Client = createClient()) {
22
- const { data: current } = await client.source.documents.get({ id });
23
- const document: CloudSourceDocument = { ...current.document, name };
24
- const intent: UnsV2Request<'sourceImpact'> = {
25
- id, expectedRevision: current.revision, action: 'update', document,
26
- };
27
- const impact = await client.source.impact({ ...intent, offset: 0, size: 200 });
28
- const references = [...impact.data.references];
29
- let offset = impact.data.nextOffset;
30
- const seen = new Set<string>();
31
- while (String(offset) !== '0') {
32
- if (seen.has(String(offset))) throw new Error('影响分页未前进');
33
- seen.add(String(offset));
34
- const next = await client.source.impact({
35
- ...intent, offset, size: 200, impactRevision: impact.data.impactRevision,
36
- });
37
- if (next.data.impactRevision !== impact.data.impactRevision) throw new Error('影响确认值发生变化');
38
- references.push(...next.data.references);
39
- offset = next.data.nextOffset;
40
- }
41
- return { intent, impact: { ...impact.data, references, nextOffset: offset } };
42
- }
43
- export async function commitSourceRename(
44
- plan: { intent: UnsV2Request<'sourceImpact'>; impact: { impactRevision: string } },
45
- intentKey: string, client: Tier0Client = createClient(),
46
- ) {
47
- const { id, expectedRevision, document, action } = plan.intent;
48
- if (action !== 'update' || !document) throw new Error('需要完整更新意图');
49
- const request = { id, expectedRevision, document,
50
- confirmedImpactRevision: plan.impact.impactRevision };
51
- const preview = await client.source.documents.update({ ...request, dryRun: true });
52
- if (!('validated' in preview.data) || !preview.data.validated) throw new Error('预检未通过');
53
- const saved = await client.source.documents.update({
54
- ...request, dryRun: false, idempotencyKey: intentKey,
55
- });
56
- if (!('id' in saved.data)) throw new Error('未收到更新提交结果');
57
- return { receipt: saved, current: (await client.source.documents.get({ id })).data };
58
- }
59
- ```
60
-
61
- 提交返回完整 Source view。核对读回名称、配置和提交修订;若读回又被别人修改,报告并发变化。影响评估后的 plan 保持不变。遇到 VERSION_CONFLICT、确认过期/不匹配,重新读取并评估,不静默刷新 token 重放。
@@ -1,53 +0,0 @@
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) | collectionCreate、recordInsertOne |
15
- | 查询集合、筛选/排序记录 | [查询](references/read.md) | collectionGet/List/Describe、recordFind |
16
- | 改记录、集合名称、扩展 Schema | [修改](references/update.md) | recordUpdateOne、collectionUpdate/SchemaUpdate |
17
- | 删记录、归档集合 | [删除](references/delete.md) | recordDeleteOne、collectionArchive |
18
- | 恢复集合或记录 | [恢复](references/restore.md) | collectionRestore、recordRestore |
19
- | 分页、历史采样、事务、变更轮询 | [数据工作流](references/data-workflows.md) | sampleFind、recordTransactionExecute、recordChangesPoll |
20
- | 目录与 Topic 元数据 | [命名空间](references/namespace.md) | directory*、topic* |
21
-
22
- ## 找到正确数据上下文
23
-
24
- 托管 App 使用 dataAppContext,按业务 alias 精确选择授权集合;Workspace 集成用 dataDiscover/collectionList,不强制 App 上下文。
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
- ## 核心约束
41
-
42
- 1. 记录行是 id/data/revision;filter 与 update 的业务路径用 name、customer.name,不加 data.;系统路径用 _id、_meta.revision。
43
- 2. 改/删前读取 expectedRevision;普通 Record 意图键上限 96 字符,集合管理为 128。不同操作使用独立键。
44
- 3. collectionSchemaUpdate 替换完整 Schema,不迁移已有记录;sampleSchema/indexes 省略保留。先预检兼容性。
45
- 4. 集合无硬删除入口,用 archive;Record 删除会物理删除关联 Sample,恢复 Record 不会恢复这些 Sample。
46
- 5. recordFind 使用 pageInfo.hasMore/nextCursor,limit 1–200;sort 为 [['field',1]]。单页不是全量,跨页不是稳定快照。
47
- 6. Sample 的时间是 Unix 毫秒,查询范围 [from,to)。Record 代表业务实体,不用覆盖它来保存历史。
48
-
49
- ## 结果与异常
50
-
51
- 响应为 code/msg/requestId/data;集合写入 Outcome 与记录写入计数回执不同。检查 committed、每项错误和实际计数;accepted/jobId 先对账,不当成完成。allowPartial 不等于事务,recordBulkWrite 不保证跨集合事务。
52
-
53
- 大整数与 JSON 精确数字可能为 bigint/UnsV2RawNumber;保留读取到的 revision 类型,不用 Number 或普通 JSON.stringify 做无损往返。UnsV2OutcomeUnknownError 先读回核对内容或查询 jobs;取消请求不撤销写入,SDK 不自动重试。
@@ -1,85 +0,0 @@
1
- # 创建集合与记录
2
-
3
- | 方法 | POST 路径 | 用途 |
4
- | --- | --- | --- |
5
- | uns.collectionCreate | /openapi/v2/uns/collections/create | 创建集合及 Schema |
6
- | uns.recordInsertOne | /openapi/v2/uns/collections/{collectionId}/records | 新增业务记录 |
7
-
8
- ## 集合参数
9
-
10
- | 字段 | 类型 | 必填 | 说明 |
11
- | --- | --- | --- | --- |
12
- | dryRun | boolean | 是 | true 预检;false 提交 |
13
- | items[].id | string | 否 | 可指定稳定业务 ID,不能与已有集合冲突 |
14
- | items[].name | string | 是 | 1–63 字符 |
15
- | items[].recordSchema | Schema | 是 | 至少 type;对象可含 properties/required |
16
- | items[].sampleSchema | Schema | 否 | 需要采样时配置 |
17
- | items[].parentId | string/null | 否 | 目录 ID;null 或创建时省略表示根 |
18
- | items[].indexes | IndexSpec[] | 否 | 索引定义,不能把索引创建当成已 ready |
19
- | mode / allowPartial | string / boolean | 否 | 示例 sync / false |
20
- | idempotencyKey | string | 否 | 集合管理最多 128 字符 |
21
-
22
- ## 完整示例:设备集合
23
-
24
- ```ts
25
- import { createClient, type Tier0Client, type UnsV2Request } from '@tier0/sdk-v2';
26
-
27
- export async function createDeviceCollection(
28
- collectionId: string, name: string, intentKey: string, client: Tier0Client = createClient(),
29
- ) {
30
- const item: UnsV2Request<'collectionCreate'>['items'][number] = {
31
- id: collectionId, name, parentId: null,
32
- recordSchema: {
33
- type: 'object', additionalProperties: false, required: ['name', 'enabled'],
34
- properties: { name: { type: 'string' }, enabled: { type: 'boolean' } },
35
- },
36
- sampleSchema: {
37
- type: 'object', additionalProperties: false, required: ['temperature'],
38
- properties: { temperature: { type: 'number', unit: '°C' } },
39
- },
40
- };
41
- const preview = await client.uns.collectionCreate({ dryRun: true, mode: 'sync', items: [item] });
42
- if (preview.data.status !== 'validated') throw new Error('集合预检未通过');
43
- const receipt = await client.uns.collectionCreate({
44
- dryRun: false, mode: 'sync', allowPartial: false, items: [item], idempotencyKey: intentKey,
45
- });
46
- if (receipt.data.status !== 'committed' || receipt.data.results?.some(item => item.error)) {
47
- return { state: 'unconfirmed' as const, receipt };
48
- }
49
- return { state: 'committed' as const, receipt,
50
- current: (await client.uns.collectionGet({ id: collectionId })).data };
51
- }
52
- ```
53
-
54
- 返回 Outcome:status、results、replayed,可含 jobId/summary。accepted 需要作业对账;validated 不表示已创建。省略自定义 ID 时从成功 results[].id 取得实际 ID,不能用集合名称代替。
55
-
56
- ## 新增设备记录
57
-
58
- | 字段 | 类型 | 必填 | 说明 |
59
- | --- | --- | --- | --- |
60
- | collectionId | string | 是 | 已存在且有权限的集合 |
61
- | id | string | 否 | 可指定稳定记录 ID |
62
- | data | object | 是 | 符合 recordSchema 的业务字段 |
63
- | dryRun | boolean | 否 | 默认 false,示例显式给出 |
64
- | idempotencyKey | string | 否 | 记录写入最多 96 字符 |
65
-
66
- ```ts
67
- import { createClient, type Tier0Client } from '@tier0/sdk-v2';
68
-
69
- export async function insertDevice(
70
- collectionId: string, id: string, name: string, intentKey: string,
71
- client: Tier0Client = createClient(),
72
- ) {
73
- const receipt = await client.uns.recordInsertOne({
74
- collectionId, id, data: { name, enabled: true }, dryRun: false, idempotencyKey: intentKey,
75
- });
76
- if (receipt.data.status !== 'committed') return { state: 'unconfirmed' as const, receipt };
77
- const found = await client.uns.recordFind({
78
- collectionId, filter: { _id: receipt.data.insertedId }, limit: 1,
79
- });
80
- if (!found.data.items[0]) throw new Error('新增有回执,但读回未找到记录');
81
- return { state: 'committed' as const, receipt, current: found.data.items[0] };
82
- }
83
- ```
84
-
85
- 成功 data:insertedId、status、replayed;不能把 replayed 误解为本次重复插入。记录创建与集合创建用不同意图键。未知结果先按 ID 核对业务内容,不仅检查“存在”。
@@ -1,69 +0,0 @@
1
- # 删除记录与归档集合
2
-
3
- 集合没有 collectionDelete;使用 collectionArchive。记录使用 recordDeleteOne/Many。两种操作的影响不可互换。
4
-
5
- ## 删除一个明确 ID 的记录
6
-
7
- | 字段 | 类型 | 必填 | 说明 |
8
- | --- | --- | --- | --- |
9
- | collectionId | string | 是 | 所属集合 |
10
- | filter | object | 是 | 示例用 _id 精确等值 |
11
- | expectedRevision | integer | 建议提供 | 刚读取的记录修订 |
12
- | dryRun | boolean | 否 | 默认 false,示例显式指定 |
13
- | idempotencyKey | string | 否 | 最多 96 字符 |
14
-
15
- ```ts
16
- import { createClient, type Tier0Client } from '@tier0/sdk-v2';
17
-
18
- export async function deleteDevice(
19
- collectionId: string, id: string, intentKey: string, client: Tier0Client = createClient(),
20
- ) {
21
- const found = await client.uns.recordFind({ collectionId, filter: { _id: id }, limit: 1 });
22
- const current = found.data.items[0];
23
- if (!current) throw new Error('记录不存在或不可见,未执行删除');
24
- const receipt = await client.uns.recordDeleteOne({
25
- collectionId, filter: { _id: id }, expectedRevision: current.revision,
26
- dryRun: false, idempotencyKey: intentKey,
27
- });
28
- if (receipt.data.status !== 'committed' || String(receipt.data.deletedCount) !== '1') {
29
- return { state: 'unconfirmed' as const, receipt };
30
- }
31
- const after = await client.uns.recordFind({ collectionId, filter: { _id: id }, limit: 1 });
32
- if (after.data.items.length) throw new Error('删除有回执,但记录仍可见或已被恢复');
33
- return { state: 'committed' as const, receipt };
34
- }
35
- ```
36
-
37
- 删除 Record 时关联 Sample 被物理删除;恢复记录不会重建这些 Sample。批量删除先明确筛选范围,不能用空 filter 代替未知 ID。删除回执含 deletedCount/status/replayed。
38
-
39
- ## 归档集合
40
-
41
- 输入 dryRun、items[{id,expectedRevision}],可选 mode/allowPartial/idempotencyKey(最多128字符)。归档不等于逐条删除 Record,也不承诺物理擦除。
42
-
43
- ```ts
44
- import { createClient, type Tier0Client } from '@tier0/sdk-v2';
45
-
46
- export async function archiveCollection(
47
- id: string, intentKey: string, client: Tier0Client = createClient(),
48
- ) {
49
- const { data: current } = await client.uns.collectionGet({ id });
50
- const receipt = await client.uns.collectionArchive({
51
- dryRun: false, mode: 'sync', allowPartial: false, idempotencyKey: intentKey,
52
- items: [{ id, expectedRevision: current.revision }],
53
- });
54
- if (receipt.data.status !== 'committed' || receipt.data.results?.some(item => item.error)) {
55
- return { state: 'unconfirmed' as const, receipt };
56
- }
57
- let page = 1;
58
- while (true) {
59
- const { data } = await client.uns.collectionList({ archived: true, page, size: 200 });
60
- const archived = data.list.find(item => item.id === id);
61
- if (archived) return { state: 'archived' as const, receipt, archived };
62
- if (!data.hasMore) return { state: 'committed-readback-unconfirmed' as const, receipt };
63
- if (typeof data.nextPage !== 'number' || data.nextPage <= page) throw new Error('无有效下一页');
64
- page = data.nextPage;
65
- }
66
- }
67
- ```
68
-
69
- 返回 Outcome,accepted 需要 jobs 对账;不能提前宣称完成。引用约束或版本冲突应停止并评估,不自动连带删除 Topic/Source/Stream。恢复见[恢复](restore.md)。