@yss123/cage-kernel 0.1.0 → 0.3.0

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 (140) hide show
  1. package/README.md +71 -9
  2. package/dist/authorization.d.ts +34 -0
  3. package/dist/authorization.js +89 -0
  4. package/dist/authorization.js.map +1 -0
  5. package/dist/brands.d.ts +10 -0
  6. package/dist/brands.js +11 -1
  7. package/dist/brands.js.map +1 -1
  8. package/dist/db/connect.d.ts +14 -0
  9. package/dist/db/connect.js +18 -2
  10. package/dist/db/connect.js.map +1 -1
  11. package/dist/db/ddl.d.ts +40 -1
  12. package/dist/db/ddl.js +111 -7
  13. package/dist/db/ddl.js.map +1 -1
  14. package/dist/db/event-version-schema.d.ts +9 -0
  15. package/dist/db/event-version-schema.js +10 -0
  16. package/dist/db/event-version-schema.js.map +1 -0
  17. package/dist/db/index.d.ts +4 -0
  18. package/dist/db/index.js +4 -0
  19. package/dist/db/index.js.map +1 -1
  20. package/dist/db/migrate.d.ts +10 -0
  21. package/dist/db/migrate.js +9 -1
  22. package/dist/db/migrate.js.map +1 -1
  23. package/dist/db/rows.d.ts +2 -0
  24. package/dist/db/rows.js +6 -0
  25. package/dist/db/rows.js.map +1 -1
  26. package/dist/db/runtime-schema.d.ts +9 -0
  27. package/dist/db/runtime-schema.js +41 -0
  28. package/dist/db/runtime-schema.js.map +1 -0
  29. package/dist/db/session.d.ts +6 -0
  30. package/dist/db/session.js +6 -0
  31. package/dist/db/session.js.map +1 -1
  32. package/dist/errors.d.ts +12 -0
  33. package/dist/errors.js +14 -0
  34. package/dist/errors.js.map +1 -1
  35. package/dist/files/links.d.ts +10 -0
  36. package/dist/files/links.js +50 -0
  37. package/dist/files/links.js.map +1 -0
  38. package/dist/files/schema.d.ts +9 -0
  39. package/dist/files/schema.js +51 -0
  40. package/dist/files/schema.js.map +1 -0
  41. package/dist/index.d.ts +12 -2
  42. package/dist/index.js +12 -2
  43. package/dist/index.js.map +1 -1
  44. package/dist/kernel.d.ts +88 -0
  45. package/dist/kernel.js +82 -41
  46. package/dist/kernel.js.map +1 -1
  47. package/dist/mask.d.ts +7 -1
  48. package/dist/mask.js +11 -0
  49. package/dist/mask.js.map +1 -1
  50. package/dist/org.d.ts +7 -0
  51. package/dist/org.js +5 -0
  52. package/dist/org.js.map +1 -1
  53. package/dist/pipeline/draft.d.ts +16 -0
  54. package/dist/pipeline/draft.js +39 -0
  55. package/dist/pipeline/draft.js.map +1 -0
  56. package/dist/pipeline/idempotency.d.ts +6 -0
  57. package/dist/pipeline/idempotency.js +6 -0
  58. package/dist/pipeline/idempotency.js.map +1 -1
  59. package/dist/pipeline/invoker.d.ts +9 -0
  60. package/dist/pipeline/invoker.js +22 -0
  61. package/dist/pipeline/invoker.js.map +1 -0
  62. package/dist/pipeline/load.d.ts +13 -0
  63. package/dist/pipeline/load.js +22 -6
  64. package/dist/pipeline/load.js.map +1 -1
  65. package/dist/primitives/aggregate.d.ts +95 -2
  66. package/dist/primitives/aggregate.js +15 -1
  67. package/dist/primitives/aggregate.js.map +1 -1
  68. package/dist/primitives/business-process.d.ts +128 -0
  69. package/dist/primitives/business-process.js +62 -0
  70. package/dist/primitives/business-process.js.map +1 -0
  71. package/dist/primitives/collection.d.ts +65 -0
  72. package/dist/primitives/collection.js +79 -0
  73. package/dist/primitives/collection.js.map +1 -0
  74. package/dist/primitives/command.d.ts +110 -11
  75. package/dist/primitives/command.js +16 -0
  76. package/dist/primitives/command.js.map +1 -1
  77. package/dist/primitives/constraints.d.ts +30 -1
  78. package/dist/primitives/constraints.js +14 -0
  79. package/dist/primitives/constraints.js.map +1 -1
  80. package/dist/primitives/event.d.ts +36 -0
  81. package/dist/primitives/event.js +12 -1
  82. package/dist/primitives/event.js.map +1 -1
  83. package/dist/primitives/fields.d.ts +176 -36
  84. package/dist/primitives/fields.js +134 -14
  85. package/dist/primitives/fields.js.map +1 -1
  86. package/dist/primitives/flow.d.ts +173 -8
  87. package/dist/primitives/flow.js +29 -2
  88. package/dist/primitives/flow.js.map +1 -1
  89. package/dist/primitives/states.d.ts +34 -2
  90. package/dist/primitives/states.js +15 -0
  91. package/dist/primitives/states.js.map +1 -1
  92. package/dist/read.d.ts +39 -3
  93. package/dist/read.js +193 -48
  94. package/dist/read.js.map +1 -1
  95. package/dist/registry.d.ts +51 -1
  96. package/dist/registry.js +105 -5
  97. package/dist/registry.js.map +1 -1
  98. package/dist/relations.d.ts +21 -0
  99. package/dist/relations.js +65 -0
  100. package/dist/relations.js.map +1 -0
  101. package/dist/runtime.d.ts +35 -0
  102. package/dist/runtime.js +9 -0
  103. package/dist/runtime.js.map +1 -0
  104. package/dist/scope/index.d.ts +5 -0
  105. package/dist/scope/index.js +5 -0
  106. package/dist/scope/index.js.map +1 -1
  107. package/dist/telemetry.d.ts +8 -0
  108. package/dist/telemetry.js +28 -3
  109. package/dist/telemetry.js.map +1 -1
  110. package/dist/transfer/csv.d.ts +19 -0
  111. package/dist/transfer/csv.js +125 -0
  112. package/dist/transfer/csv.js.map +1 -0
  113. package/dist/transfer/index.d.ts +67 -0
  114. package/dist/transfer/index.js +271 -0
  115. package/dist/transfer/index.js.map +1 -0
  116. package/dist/transfer/schema.d.ts +9 -0
  117. package/dist/transfer/schema.js +36 -0
  118. package/dist/transfer/schema.js.map +1 -0
  119. package/dist/types.d.ts +38 -2
  120. package/dist/types.js +8 -0
  121. package/dist/types.js.map +1 -1
  122. package/dist/util/names.d.ts +6 -0
  123. package/dist/util/names.js +6 -0
  124. package/dist/util/names.js.map +1 -1
  125. package/dist/worker/consumers.d.ts +7 -0
  126. package/dist/worker/consumers.js +21 -9
  127. package/dist/worker/consumers.js.map +1 -1
  128. package/dist/worker/health.d.ts +38 -0
  129. package/dist/worker/health.js +20 -0
  130. package/dist/worker/health.js.map +1 -0
  131. package/dist/worker/index.d.ts +34 -1
  132. package/dist/worker/index.js +176 -37
  133. package/dist/worker/index.js.map +1 -1
  134. package/dist/worker/outbox.d.ts +25 -2
  135. package/dist/worker/outbox.js +40 -2
  136. package/dist/worker/outbox.js.map +1 -1
  137. package/dist/worker/scheduler.d.ts +10 -1
  138. package/dist/worker/scheduler.js +34 -4
  139. package/dist/worker/scheduler.js.map +1 -1
  140. package/package.json +1 -1
@@ -1,41 +1,100 @@
1
+ /**
2
+ * 声明事件反应、只读查询、审批绑定、定时任务与显式逃生口。
3
+ * 这些规格由注册表、CLI 和对应运行服务消费:写回必须调用命令,读取遵守声明依赖,审批选人不授予能力,资源标签只供静态检查,不自动产生锁或释放动作。
4
+ */
1
5
  import type { Kysely } from 'kysely';
2
6
  import { z } from 'zod';
3
7
  import { APPROVAL, JOB, QUERY, REACTION } from '../brands.js';
4
8
  import type { AnyAggregate, FieldsOf, SnapshotOf } from './aggregate.js';
5
9
  import type { AnyCommand } from './command.js';
6
10
  import type { AnyEvent, PayloadOf } from './event.js';
11
+ /** same 与触发命令共同提交/回滚;outbox 在提交后由 worker 另开事务,可能重试或进入死信。 */
7
12
  export type TxMode = 'outbox' | 'same';
13
+ /**
14
+ * 事件到目标命令的冻结连接规格,普通反应输出单个输入,集合反应可展开多个输入。
15
+ * map 只做纯映射,null 跳过;真实执行仍走目标命令权限/状态/版本与审计,事务模式决定同提交还是异步恢复。
16
+ */
8
17
  export interface Reaction<E extends AnyEvent = AnyEvent, C extends AnyCommand = AnyCommand> {
18
+ /**
19
+ * 反应品牌标记,用于注册和静态抽取。
20
+ */
9
21
  readonly [REACTION]: true;
22
+ /**
23
+ * 触发源事件声明,载荷由对应事件 schema 校验。
24
+ */
10
25
  readonly event: E;
26
+ /**
27
+ * 每次反应实际调用的目标命令,不能用标签替代其业务逻辑。
28
+ */
11
29
  readonly command: C;
12
30
  /** 作用的聚合(等于 command.agg,写出来便于阅读) */
13
31
  readonly target: AnyAggregate;
14
32
  /** 把事件载荷映射成命令输入(纯函数);返回 null 表示这次不处理 */
15
- readonly map: (e: PayloadOf<E>) => z.input<C['input']> | null;
33
+ readonly map: (e: PayloadOf<E>) => z.input<C['input']> | readonly z.input<C['input']>[] | null;
34
+ /** 同一事件、同一事务模式中的显式步骤序号;启用后各步骤必须完整、唯一地声明。 */
35
+ readonly order?: number;
36
+ /** 有界集合的可检查元数据;集合仍逐项经过命令流水线。 */
37
+ readonly collection?: {
38
+ readonly maxItems: number; /** 稳定排序字段,所有项需同类型唯一键。 */
39
+ readonly orderBy: string; /** 集合选择器源码快照,供静态检查和图谱证据使用,不在运行时 eval。 */
40
+ readonly itemsSource: string; /** 逐项输入映射源码快照,供静态解释,不代替真实回调执行。 */
41
+ readonly mapSource: string;
42
+ };
16
43
  /** 事务语义:outbox 异步(默认,见 D3);same 与触发命令同一事务 */
17
44
  readonly tx: TxMode;
18
45
  /** 获得的资源,供资源配对检查使用 */
19
46
  readonly acquires?: string;
20
47
  /** 释放的资源 */
21
48
  readonly releases?: string;
49
+ /**
50
+ * 可选反应标识;省略时注册/抽取层按导出声明识别,不是资源名。
51
+ */
22
52
  readonly name?: string;
23
53
  }
54
+ /**
55
+ * 容纳不同事件载荷和命令输入的反应擦除类型,供注册表和消费者遍历;声明时仍保留精确泛型。
56
+ */
24
57
  export type AnyReaction = Reaction<AnyEvent, AnyCommand>;
58
+ /**
59
+ * 声明事件到命令的映射,默认使用 outbox 异步执行。target 若显式提供必须与命令所属聚合一致。
60
+ * order 是同一事件步骤的显式顺序;acquires/releases 仅用于静态资源配对,实际动作由命令实现。
61
+ */
25
62
  export declare function reaction<E extends AnyEvent, C extends AnyCommand>(ev: E, def: {
63
+ /** 事件发生后调用的目标命令,其聚合就是实际 target。 */
26
64
  command: C;
65
+ /** 从已校验事件载荷同步生成命令输入的纯映射,返回 null 表示该事件不执行此反应。 */
27
66
  map: (e: PayloadOf<E>) => z.input<C['input']> | null;
67
+ /** 可选显式目标聚合,提供时必须与 command.agg 为同一声明对象;省略自动取命令聚合。 */
28
68
  target?: AnyAggregate;
69
+ /** 省略时 outbox,由 worker 提交后异步处理;same 表示与触发命令同事务回滚。 */
29
70
  tx?: TxMode;
71
+ /** 可选取得资源标签,供静态配对检查,不自动创建占用。 */
30
72
  acquires?: string;
73
+ /** 可选释放资源标签,供静态配对检查,真实释放由目标命令实现。 */
31
74
  releases?: string;
75
+ /** 可选反应名,用于注册、图谱和诊断识别。 */
32
76
  name?: string;
77
+ /** 同一事件/事务组的非负步骤序号;组内使用时需完整且不重复,不依赖导出排序。 */
78
+ order?: number;
33
79
  }): Reaction<E, C>;
80
+ /**
81
+ * 已声明的命名查询规格,input 解析参数,reads 明确所有读取聚合及对应 read 权限。
82
+ * sql/build 恰选一个;安全只读账号、事务会话和审计由读侧执行层提供,说明文字不能替代真实查询边界。
83
+ */
34
84
  export interface Query<I extends z.ZodType = z.ZodType> {
85
+ /**
86
+ * 命名查询品牌标记,不表示已经执行或授权。
87
+ */
35
88
  readonly [QUERY]: true;
89
+ /**
90
+ * 稳定小驼峰查询名,形成客户端查询入口和本体 key。
91
+ */
36
92
  readonly name: string;
37
93
  /** 查询读了哪些聚合:只能引用它们的安全视图;调用者需要每个聚合的 read 能力 */
38
94
  readonly reads: readonly AnyAggregate[];
95
+ /**
96
+ * 输入 Zod schema;省略声明时规范化为空对象,运行时使用解析后的输出值绑定参数。
97
+ */
39
98
  readonly input: I;
40
99
  /** 手写 SQL:只能引用 safe.* 视图,参数用 :name 占位 */
41
100
  readonly sql?: string;
@@ -43,20 +102,53 @@ export interface Query<I extends z.ZodType = z.ZodType> {
43
102
  readonly build?: (db: Kysely<any>, input: z.output<I>) => {
44
103
  execute(): Promise<unknown[]>;
45
104
  };
105
+ /**
106
+ * 查询口径与用途说明,供本体和契约展示;统计完整性仍由查询实现与分页边界决定。
107
+ */
46
108
  readonly description?: string;
109
+ /** 用途级可靠审计;敏感明文查询另会自动审计所涉及聚合。 */
110
+ readonly audit?: {
111
+ readonly purpose: string;
112
+ };
47
113
  }
114
+ /**
115
+ * 异构查询容器类型,保留公共规格但擦除具体输入 schema,生成契约仍使用原声明推导参数。
116
+ */
48
117
  export type AnyQuery = Query<z.ZodType>;
118
+ /**
119
+ * 声明只读查询,SQL 和构建器必须恰选一种。SQL 仅允许安全视图,reads 决定授权和审计范围。
120
+ * 构造时检查明显基表引用和空审计用途;实际运行还要验证输入、read 能力并注入事务会话变量。
121
+ */
49
122
  export declare function query<I extends z.ZodType = z.ZodObject<{}>>(name: string, def: {
123
+ /** 所有访问聚合的声明白名单,包括 join 对象;调用者需全部 read 能力。 */
50
124
  reads: AnyAggregate[];
125
+ /** 可选参数 schema,省略默认空对象;build 接收解析后的 z.output。 */
51
126
  input?: I;
127
+ /** 与 build 二选一的参数化 SQL,使用 safe 视图及 :name 参数,禁止拼接用户值。 */
52
128
  sql?: string;
129
+ /** 与 sql 二选一的查询构建回调,接收只读 Kysely 和已解析输入,返回可 execute 的行查询。 */
53
130
  build?: (db: Kysely<any>, input: z.output<I>) => {
54
131
  execute(): Promise<unknown[]>;
55
132
  };
133
+ /** 查询业务口径说明,供本体、文档和模型工具理解。 */
56
134
  description?: string;
135
+ /** 可选用途级可靠审计声明;审计失败时读侧不交付结果,敏感读取另有保守审计。 */
136
+ audit?: {
137
+ purpose: string;
138
+ };
57
139
  }): Query<I>;
140
+ /**
141
+ * 聚合事件与审批流程的绑定规格,不含流程选人或当前待办状态。
142
+ * start/cancelOn 驱动实例生命周期,结论通过所属聚合 process 命令落实;自动审核身份从 automatedBy 当前角色授权解析。
143
+ */
58
144
  export interface Approval<A extends AnyAggregate = AnyAggregate> {
145
+ /**
146
+ * 审批绑定品牌标记,不创建待办或流程实例。
147
+ */
59
148
  readonly [APPROVAL]: true;
149
+ /**
150
+ * 审批业务主体聚合,所有通过/驳回/退回结论命令必须属于它。
151
+ */
60
152
  readonly agg: A;
61
153
  /** 流程名,例如 po-approval */
62
154
  readonly process: string;
@@ -68,59 +160,132 @@ export interface Approval<A extends AnyAggregate = AnyAggregate> {
68
160
  readonly routable: readonly (keyof FieldsOf<A> & string)[];
69
161
  /** 通过与驳回分别落到哪个命令(必须 invokedBy: process) */
70
162
  readonly onApproved: AnyCommand;
163
+ /**
164
+ * 最终驳回对应的 process 命令,必须与主体同聚合;不同于可修改后重提的 onReturned。
165
+ */
71
166
  readonly onRejected: AnyCommand;
167
+ /** 退回修改独立于最终驳回;重新提交会创建新审批轮次。 */
168
+ readonly onReturned?: AnyCommand;
169
+ /** 自动节点使用的角色 code;权限与范围从当前角色授权解析。 */
170
+ readonly automatedBy?: string;
72
171
  }
172
+ /**
173
+ * 将聚合事件绑定到审批流程及结论命令;此处只验证字段和结论归属,不创建待办。
174
+ * routable 限制传给流程的字段,结论命令必须接受 process 身份;选人和最终命令授权仍独立判断。
175
+ * onReturned 开启修改后再提交的轮次语义,automatedBy 指向运行时解析授权的角色 code。
176
+ */
73
177
  export declare function approval<A extends AnyAggregate>(agg: A, def: {
178
+ /** 已声明路由/适配器的流程标识,与审批实例关联,不是角色名。 */
74
179
  process: string;
180
+ /** 启动审批的领域事件,需由真实命令发出并经桥接处理。 */
75
181
  start: AnyEvent;
182
+ /** 取消相关审批轮次的事件列表,省略为空;不自动推导所有撤回路径。 */
76
183
  cancelOn?: AnyEvent[];
184
+ /** 允许交给流程引擎的业务字段名白名单,字段必须属于主体聚合。 */
77
185
  routable: (keyof FieldsOf<A> & string)[];
186
+ /** 通过后执行的主体聚合命令,必须允许 process 身份,仍执行能力/范围/约束校验。 */
78
187
  onApproved: AnyCommand;
188
+ /** 最终驳回命令,与主体同聚合且允许 process;不同于退回修改。 */
79
189
  onRejected: AnyCommand;
190
+ /** 可选退回修改命令,同样要求主体聚合/process 身份;重新提交另建审批轮次。 */
191
+ onReturned?: AnyCommand;
192
+ /** 可选自动审批执行角色 code,运行时读取其当前授权,不因配置存在就自动授予业务能力。 */
193
+ automatedBy?: string;
80
194
  }): Approval<A>;
195
+ /**
196
+ * 调度器注入的受限读入口,只允许 job.reads 中的聚合,始终限定当前租户和运行身份范围。
197
+ * 查询返回内部业务快照而非浏览器展示 DTO,不把此接口暴露成任意客户端查询。
198
+ */
81
199
  export interface JobQueryApi {
82
200
  /** 只读查询:等值条件,另支持 createdBefore / createdAfter / updatedBefore */
83
201
  list<A extends AnyAggregate>(agg: A, where?: Partial<SnapshotOf<A>> & {
84
- createdBefore?: Date | string;
85
- createdAfter?: Date | string;
202
+ createdBefore?: Date | string; /** 创建时间严格晚于此时间,用于受限增量范围。 */
203
+ createdAfter?: Date | string; /** 更新时间严格早于此时间,适合按任务固定 now 查找过期记录。 */
86
204
  updatedBefore?: Date | string;
87
205
  }, opts?: {
88
206
  limit?: number;
89
207
  }): Promise<SnapshotOf<A>[]>;
90
208
  }
209
+ /** 调度器为单租户单次任务注入的受限能力;不要缓存到下一轮或用于绕过 reads/calls。 */
91
210
  export interface JobApi {
211
+ /**
212
+ * 只提供读取已声明聚合的查询方法,不暴露数据库连接或写仓储。
213
+ */
92
214
  query: JobQueryApi;
93
215
  /** 只能调用 calls 里声明过的命令;每次调用都走完整流水线,审计 source = job */
94
216
  call<C extends AnyCommand>(cmd: C, input: z.input<C['input']>): Promise<SnapshotOf<C['agg']>>;
217
+ /**
218
+ * 本次调度所处理的租户 ID,调用方不能跨租户复用该 api。
219
+ */
95
220
  tenantId: string;
221
+ /**
222
+ * 本次任务固定参考时间,由调度器传入;可用于同轮过期判断,不能假定等于每个内部命令的写入时间。
223
+ */
96
224
  now: Date;
225
+ /**
226
+ * 结构化任务日志入口,自动附任务名;extra 不应写入凭据或业务敏感明文。
227
+ */
97
228
  log: (msg: string, extra?: Record<string, unknown>) => void;
98
229
  }
230
+ /**
231
+ * 定时任务的冻结规格,调度器按 cron 和逐租户生命周期执行 run。
232
+ * reads/calls 是实际白名单和图谱依赖,任务不能绕过命令直接写表;run 完成不代表多次独立命令自动成为同一事务。
233
+ */
99
234
  export interface Job {
235
+ /**
236
+ * 定时任务品牌标记,供注册与调度发现。
237
+ */
100
238
  readonly [JOB]: true;
239
+ /**
240
+ * 小驼峰任务名称,形成执行身份、日志和调度记录标识。
241
+ */
101
242
  readonly name: string;
102
243
  /** cron 表达式,例如 0 2 * * * */
103
244
  readonly cron: string;
245
+ /**
246
+ * 允许查询的冻结聚合列表,省略声明时为空;未声明的读取在运行时拒绝。
247
+ */
104
248
  readonly reads: readonly AnyAggregate[];
249
+ /**
250
+ * 允许调用的冻结命令列表,省略声明时为空;列入仍走命令完整流水线。
251
+ */
105
252
  readonly calls: readonly AnyCommand[];
253
+ /**
254
+ * 调度器为当前租户注入 JobApi 后调用的异步任务体;失败抛出由调度器记录,外部 IO 需走连接器。
255
+ */
106
256
  readonly run: (api: JobApi) => Promise<void>;
257
+ /**
258
+ * 任务业务目的说明,帮助图谱与诊断定位,不影响 cron 解释。
259
+ */
107
260
  readonly description?: string;
108
261
  }
262
+ /**
263
+ * 声明定时任务的时间表和读写依赖,不立即启动定时器。调度器负责跨实例互斥和逐租户执行。
264
+ * reads/calls 是运行时白名单也是图谱证据;cron 在这里做基本形状校验,具体解释交给调度器。
265
+ */
109
266
  export declare function job(name: string, def: {
110
- cron: string;
111
- reads?: AnyAggregate[];
112
- calls?: AnyCommand[];
113
- run: (api: JobApi) => Promise<void>;
267
+ cron: string; /** 可查询聚合白名单,省略为空;运行时访问未声明聚合会拒绝。 */
268
+ reads?: AnyAggregate[]; /** 可调用命令白名单,省略为空;内部命令还须允许对应身份。 */
269
+ calls?: AnyCommand[]; /** 当前租户任务体,只通过注入 query/call/log 等能力工作;失败抛出由调度器记录,不自动包成跨命令总事务。 */
270
+ run: (api: JobApi) => Promise<void>; /** 可选任务目的和边界说明,供图谱及诊断展示。 */
114
271
  description?: string;
115
272
  }): Job;
116
- /** 前 N 天 */
273
+ /** 返回参考时间之前 n×24 小时的 Date,from 缺省当前时间;按毫秒减法而非日历时区日计算,不修改传入 Date。 */
117
274
  export declare function daysAgo(n: number, from?: Date): Date;
275
+ /** 逃生口的责任记录;reason 说明现有原语为何不足,owner 指向后续审查与收敛负责人。 */
118
276
  export interface UnsafeTag {
277
+ /**
278
+ * 说明现有原语不能表达的具体原因及特殊边界,不能为空。
279
+ */
119
280
  readonly reason: string;
281
+ /**
282
+ * 负责审查和后续收敛的责任人标识,不能为空;不作为系统授权身份。
283
+ */
120
284
  readonly owner: string;
121
285
  }
122
286
  /**
123
287
  * 逃生口:声明表达不了时使用,必须写明原因和负责人。lint 检查参数,计数进 PR 摘要。
288
+ * 校验标签后立即调用 fn 并原样返回结果/抛出异常;不会授予数据库、网络或用户权限,也不会关闭依赖与 lint 门禁。
124
289
  * const v = unsafe({ reason: '…', owner: 'zhangsan' }, () => …)
125
290
  */
126
291
  export declare function unsafe<T>(tag: UnsafeTag, fn: () => T): T;
@@ -1,10 +1,17 @@
1
1
  import { z } from 'zod';
2
2
  import { APPROVAL, JOB, QUERY, REACTION, UNSAFE } from '../brands.js';
3
3
  import { declError } from '../errors.js';
4
+ /**
5
+ * 声明事件到命令的映射,默认使用 outbox 异步执行。target 若显式提供必须与命令所属聚合一致。
6
+ * order 是同一事件步骤的显式顺序;acquires/releases 仅用于静态资源配对,实际动作由命令实现。
7
+ */
4
8
  export function reaction(ev, def) {
5
9
  if (def.target && def.target !== def.command.agg) {
6
10
  throw declError('CAGE-REACT-001', `反应的 target 与 command 所属聚合不一致`);
7
11
  }
12
+ if (def.order !== undefined && (!Number.isSafeInteger(def.order) || def.order < 0)) {
13
+ throw declError('CAGE-REACT-003', '反应 order 必须为非负安全整数');
14
+ }
8
15
  return Object.freeze({
9
16
  [REACTION]: true,
10
17
  event: ev,
@@ -15,8 +22,13 @@ export function reaction(ev, def) {
15
22
  acquires: def.acquires,
16
23
  releases: def.releases,
17
24
  name: def.name,
25
+ order: def.order,
18
26
  });
19
27
  }
28
+ /**
29
+ * 声明只读查询,SQL 和构建器必须恰选一种。SQL 仅允许安全视图,reads 决定授权和审计范围。
30
+ * 构造时检查明显基表引用和空审计用途;实际运行还要验证输入、read 能力并注入事务会话变量。
31
+ */
20
32
  export function query(name, def) {
21
33
  if (!/^[a-z][A-Za-z0-9]*$/.test(name))
22
34
  throw declError('CAGE-NAME-005', `查询名 ${name} 必须是小驼峰`);
@@ -25,6 +37,8 @@ export function query(name, def) {
25
37
  if (def.sql && /\bcore\.|\bsys\./i.test(def.sql)) {
26
38
  throw declError('CAGE-QUERY-002', `查询 ${name} 引用了基表,只能引用 safe.* 安全视图`);
27
39
  }
40
+ if (def.audit && !def.audit.purpose.trim())
41
+ throw declError('CAGE-QUERY-003', `查询 ${name} 的审计 purpose 不能为空`);
28
42
  return Object.freeze({
29
43
  [QUERY]: true,
30
44
  name,
@@ -33,14 +47,20 @@ export function query(name, def) {
33
47
  sql: def.sql,
34
48
  build: def.build,
35
49
  description: def.description,
50
+ audit: def.audit ? Object.freeze({ ...def.audit }) : undefined,
36
51
  });
37
52
  }
53
+ /**
54
+ * 将聚合事件绑定到审批流程及结论命令;此处只验证字段和结论归属,不创建待办。
55
+ * routable 限制传给流程的字段,结论命令必须接受 process 身份;选人和最终命令授权仍独立判断。
56
+ * onReturned 开启修改后再提交的轮次语义,automatedBy 指向运行时解析授权的角色 code。
57
+ */
38
58
  export function approval(agg, def) {
39
59
  for (const f of def.routable) {
40
60
  if (!(f in agg.fields))
41
61
  throw declError('CAGE-APPR-001', `可路由字段 ${agg.name}.${f} 不存在`);
42
62
  }
43
- for (const c of [def.onApproved, def.onRejected]) {
63
+ for (const c of [def.onApproved, def.onRejected, ...(def.onReturned ? [def.onReturned] : [])]) {
44
64
  if (c.agg !== agg)
45
65
  throw declError('CAGE-APPR-002', `审批结论命令必须属于 ${agg.name}`);
46
66
  if (!c.invokedBy.includes('process')) {
@@ -56,8 +76,14 @@ export function approval(agg, def) {
56
76
  routable: Object.freeze([...def.routable]),
57
77
  onApproved: def.onApproved,
58
78
  onRejected: def.onRejected,
79
+ onReturned: def.onReturned,
80
+ automatedBy: def.automatedBy,
59
81
  });
60
82
  }
83
+ /**
84
+ * 声明定时任务的时间表和读写依赖,不立即启动定时器。调度器负责跨实例互斥和逐租户执行。
85
+ * reads/calls 是运行时白名单也是图谱证据;cron 在这里做基本形状校验,具体解释交给调度器。
86
+ */
61
87
  export function job(name, def) {
62
88
  if (!/^[a-z][A-Za-z0-9]*$/.test(name))
63
89
  throw declError('CAGE-NAME-006', `任务名 ${name} 必须是小驼峰`);
@@ -73,12 +99,13 @@ export function job(name, def) {
73
99
  description: def.description,
74
100
  });
75
101
  }
76
- /** 前 N 天 */
102
+ /** 返回参考时间之前 n×24 小时的 Date,from 缺省当前时间;按毫秒减法而非日历时区日计算,不修改传入 Date。 */
77
103
  export function daysAgo(n, from = new Date()) {
78
104
  return new Date(from.getTime() - n * 24 * 3600 * 1000);
79
105
  }
80
106
  /**
81
107
  * 逃生口:声明表达不了时使用,必须写明原因和负责人。lint 检查参数,计数进 PR 摘要。
108
+ * 校验标签后立即调用 fn 并原样返回结果/抛出异常;不会授予数据库、网络或用户权限,也不会关闭依赖与 lint 门禁。
82
109
  * const v = unsafe({ reason: '…', owner: 'zhangsan' }, () => …)
83
110
  */
84
111
  export function unsafe(tag, fn) {
@@ -1 +1 @@
1
- {"version":3,"file":"flow.js","sourceRoot":"","sources":["../../src/primitives/flow.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"flow.js","sourceRoot":"","sources":["../../src/primitives/flow.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtE,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAoDzC;;;GAGG;AACH,MAAM,UAAU,QAAQ,CACtB,EAAK,EACL,GAiBC;IAED,IAAI,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;QACjD,MAAM,SAAS,CAAC,gBAAgB,EAAE,8BAA8B,CAAC,CAAC;IACpE,CAAC;IACD,IAAI,GAAG,CAAC,KAAK,KAAK,SAAS,IAAI,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC,EAAE,CAAC;QACnF,MAAM,SAAS,CAAC,gBAAgB,EAAE,oBAAoB,CAAC,CAAC;IAC1D,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC;QACnB,CAAC,QAAQ,CAAC,EAAE,IAAa;QACzB,KAAK,EAAE,EAAE;QACT,OAAO,EAAE,GAAG,CAAC,OAAO;QACpB,MAAM,EAAE,GAAG,CAAC,OAAO,CAAC,GAAG;QACvB,GAAG,EAAE,GAAG,CAAC,GAAG;QACZ,EAAE,EAAE,GAAG,CAAC,EAAE,IAAI,QAAQ;QACtB,QAAQ,EAAE,GAAG,CAAC,QAAQ;QACtB,QAAQ,EAAE,GAAG,CAAC,QAAQ;QACtB,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,KAAK,EAAE,GAAG,CAAC,KAAK;KACjB,CAAC,CAAC;AACL,CAAC;AAwCD;;;GAGG;AACH,MAAM,UAAU,KAAK,CACnB,IAAY,EACZ,GAaC;IAED,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,MAAM,SAAS,CAAC,eAAe,EAAE,OAAO,IAAI,SAAS,CAAC,CAAC;IAC9F,IAAI,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,KAAK;QAAE,MAAM,SAAS,CAAC,gBAAgB,EAAE,MAAM,IAAI,oBAAoB,CAAC,CAAC;IAC/F,IAAI,GAAG,CAAC,GAAG,IAAI,mBAAmB,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;QACjD,MAAM,SAAS,CAAC,gBAAgB,EAAE,MAAM,IAAI,yBAAyB,CAAC,CAAC;IACzE,CAAC;IACD,IAAI,GAAG,CAAC,KAAK,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE;QAAE,MAAM,SAAS,CAAC,gBAAgB,EAAE,MAAM,IAAI,mBAAmB,CAAC,CAAC;IAC7G,OAAO,MAAM,CAAC,MAAM,CAAC;QACnB,CAAC,KAAK,CAAC,EAAE,IAAa;QACtB,IAAI;QACJ,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC;QACpC,KAAK,EAAE,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAM;QACvC,GAAG,EAAE,GAAG,CAAC,GAAG;QACZ,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,WAAW,EAAE,GAAG,CAAC,WAAW;QAC5B,KAAK,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS;KAC/D,CAAC,CAAC;AACL,CAAC;AAqCD;;;;GAIG;AACH,MAAM,UAAU,QAAQ,CACtB,GAAM,EACN,GAiBC;IAED,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,QAAQ,EAAE,CAAC;QAC7B,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,MAAM,CAAC;YAAE,MAAM,SAAS,CAAC,eAAe,EAAE,SAAS,GAAG,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC;IACzF,CAAC;IACD,KAAK,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;QAC9F,IAAI,CAAC,CAAC,GAAG,KAAK,GAAG;YAAE,MAAM,SAAS,CAAC,eAAe,EAAE,cAAc,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;QAC9E,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;YACrC,MAAM,SAAS,CAAC,eAAe,EAAE,iCAAiC,CAAC,CAAC;QACtE,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC;QACnB,CAAC,QAAQ,CAAC,EAAE,IAAa;QACzB,GAAG;QACH,OAAO,EAAE,GAAG,CAAC,OAAO;QACpB,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,CAAC;QAClD,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC1C,UAAU,EAAE,GAAG,CAAC,UAAU;QAC1B,UAAU,EAAE,GAAG,CAAC,UAAU;QAC1B,UAAU,EAAE,GAAG,CAAC,UAAU;QAC1B,WAAW,EAAE,GAAG,CAAC,WAAW;KAC7B,CAAC,CAAC;AACL,CAAC;AAwED;;;GAGG;AACH,MAAM,UAAU,GAAG,CACjB,IAAY,EACZ,GAAyV;IAEzV,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,MAAM,SAAS,CAAC,eAAe,EAAE,OAAO,IAAI,SAAS,CAAC,CAAC;IAC9F,IAAI,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC;QAAE,MAAM,SAAS,CAAC,cAAc,EAAE,MAAM,IAAI,gBAAgB,CAAC,CAAC;IACzG,OAAO,MAAM,CAAC,MAAM,CAAC;QACnB,CAAC,GAAG,CAAC,EAAE,IAAa;QACpB,IAAI;QACJ,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC;QAC5C,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC;QAC5C,GAAG,EAAE,GAAG,CAAC,GAAG;QACZ,WAAW,EAAE,GAAG,CAAC,WAAW;KAC7B,CAAC,CAAC;AACL,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,OAAO,CAAC,CAAS,EAAE,IAAI,GAAG,IAAI,IAAI,EAAE;IAClD,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC;AACzD,CAAC;AAgBD;;;;GAIG;AACH,MAAM,UAAU,MAAM,CAAI,GAAc,EAAE,EAAW;IACnD,IAAI,CAAC,GAAG,EAAE,MAAM,IAAI,CAAC,GAAG,EAAE,KAAK;QAAE,MAAM,SAAS,CAAC,iBAAiB,EAAE,4BAA4B,CAAC,CAAC;IAClG,OAAO,EAAE,EAAE,CAAC;AACd,CAAC;AACA,MAAc,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC"}
@@ -1,18 +1,50 @@
1
+ /**
2
+ * 定义聚合有限生命周期及展示标签,供命令状态约束、草稿初始值和图规则共同使用。
3
+ * states 只返回冻结描述,不创建迁移或命令;terminal 是声明元数据,实际是否允许离开仍取决于命令 from/to。
4
+ */
1
5
  import { STATES } from '../brands.js';
6
+ /** 状态标识用于持久化与命令约束,labels 只用于显示,不改变实际状态字符串。 */
2
7
  export interface StatesDef<S extends string = string> {
8
+ /**
9
+ * 状态集品牌标记,用于识别 fields.status 与普通业务字段。
10
+ */
3
11
  readonly [STATES]: true;
12
+ /**
13
+ * 按声明顺序保存的完整状态标识集合,不能为空或重复,标识使用小写下划线。
14
+ */
4
15
  readonly values: readonly S[];
16
+ /**
17
+ * 显式声明的终态集合,默认空;供检查和展示,不自动禁止终态命令。
18
+ */
5
19
  readonly terminal: readonly S[];
20
+ /**
21
+ * 新建草稿的初始状态,显式 initial 优先,否则取 values 第一项;最终提交状态由新建命令 to 决定。
22
+ */
6
23
  readonly initial: S;
24
+ /**
25
+ * 状态到显示名的部分映射,缺省为空,未配置标签仍使用状态标识。
26
+ */
27
+ readonly labels: Readonly<Partial<Record<S, string>>>;
7
28
  }
29
+ /**
30
+ * 状态声明的可选配置,所有引用必须属于前面的状态字符串集合。
31
+ * terminal 缺省为空,initial 缺省第一项,labels 只覆盖显示名且不能空白,不改变数据库持久标识。
32
+ */
8
33
  type StatesOpts<S extends string> = {
9
- terminal?: NoInfer<S>[];
10
- initial?: NoInfer<S>;
34
+ terminal?: NoInfer<S>[]; /** 新建草稿的初始状态,省略时取第一个状态;必须属于已声明集合。 */
35
+ initial?: NoInfer<S>; /** 可选状态显示名映射,键必须已声明、值不能为空白;持久化仍使用原状态标识。 */
36
+ labels?: Partial<Record<NoInfer<S>, string>>;
11
37
  };
12
38
  /**
13
39
  * states('draft', 'pending_approval', 'approved', { terminal: ['approved'] })
14
40
  */
41
+ /**
42
+ * 最后一个对象参数为选项,字符串参数构成完整状态集;显式 initial 优先,否则取首项。
43
+ * 终态、初始态和中文标签都必须引用已声明状态,错误在加载 domain 时即可暴露。
44
+ * terminal 是图检查及界面元数据,是否允许后续动作仍由具体命令的 from/to 决定。
45
+ */
15
46
  export declare function states<const S extends string>(...args: [...S[]] | [...S[], StatesOpts<S>]): StatesDef<S>;
47
+ /** 聚合解析 fields.status 时使用,只检查标记,不重新运行状态校验。 */
16
48
  export declare function isStates(v: unknown): v is StatesDef;
17
49
  /** 没有声明状态的聚合使用默认生命周期:active → deleted(业务数据不物理删除) */
18
50
  export declare const DEFAULT_STATES: StatesDef<string>;
@@ -1,9 +1,18 @@
1
+ /**
2
+ * 定义聚合有限生命周期及展示标签,供命令状态约束、草稿初始值和图规则共同使用。
3
+ * states 只返回冻结描述,不创建迁移或命令;terminal 是声明元数据,实际是否允许离开仍取决于命令 from/to。
4
+ */
1
5
  // states 原语:有限状态集,可标记终态。第一个状态是新建命令的默认初始状态。
2
6
  import { STATES } from '../brands.js';
3
7
  import { declError } from '../errors.js';
4
8
  /**
5
9
  * states('draft', 'pending_approval', 'approved', { terminal: ['approved'] })
6
10
  */
11
+ /**
12
+ * 最后一个对象参数为选项,字符串参数构成完整状态集;显式 initial 优先,否则取首项。
13
+ * 终态、初始态和中文标签都必须引用已声明状态,错误在加载 domain 时即可暴露。
14
+ * terminal 是图检查及界面元数据,是否允许后续动作仍由具体命令的 from/to 决定。
15
+ */
7
16
  export function states(...args) {
8
17
  const all = args;
9
18
  const last = all[all.length - 1];
@@ -22,13 +31,19 @@ export function states(...args) {
22
31
  if (!values.includes(t))
23
32
  throw declError('CAGE-STATE-001', `状态 ${t} 未在 states 中声明`);
24
33
  }
34
+ for (const [state, label] of Object.entries(opts.labels ?? {})) {
35
+ if (!values.includes(state) || typeof label !== 'string' || !label.trim())
36
+ throw declError('CAGE-STATE-005', `状态名称 ${state} 必须对应已声明的状态且不能为空`);
37
+ }
25
38
  return Object.freeze({
26
39
  [STATES]: true,
27
40
  values: Object.freeze([...values]),
28
41
  terminal: Object.freeze([...terminal]),
29
42
  initial: (opts.initial ?? values[0]),
43
+ labels: Object.freeze({ ...opts.labels }),
30
44
  });
31
45
  }
46
+ /** 聚合解析 fields.status 时使用,只检查标记,不重新运行状态校验。 */
32
47
  export function isStates(v) {
33
48
  return !!v && typeof v === 'object' && v[STATES] === true;
34
49
  }
@@ -1 +1 @@
1
- {"version":3,"file":"states.js","sourceRoot":"","sources":["../../src/primitives/states.ts"],"names":[],"mappings":"AAAA,2CAA2C;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAWzC;;GAEG;AACH,MAAM,UAAU,MAAM,CAAyB,GAAG,IAAwC;IACxF,MAAM,GAAG,GAAG,IAAiB,CAAC;IAC9B,MAAM,IAAI,GAAG,GAAG,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACjC,MAAM,IAAI,GAAG,CAAC,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAoC,CAAC;IACxG,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAU,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC;IAChE,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,SAAS,CAAC,gBAAgB,EAAE,mBAAmB,CAAC,CAAC;IAChF,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC,CAAC;YAAE,MAAM,SAAS,CAAC,gBAAgB,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;IAC3F,CAAC;IACD,IAAI,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,MAAM;QAAE,MAAM,SAAS,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC;IACvF,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC;IACrC,KAAK,MAAM,CAAC,IAAI,CAAC,GAAG,QAAQ,EAAE,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;QACvE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;YAAE,MAAM,SAAS,CAAC,gBAAgB,EAAE,MAAM,CAAC,gBAAgB,CAAC,CAAC;IACtF,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC;QACnB,CAAC,MAAM,CAAC,EAAE,IAAa;QACvB,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC;QAClC,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC;QACtC,OAAO,EAAE,CAAC,IAAI,CAAC,OAAO,IAAI,MAAM,CAAC,CAAC,CAAC,CAAM;KAC1C,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,CAAU;IACjC,OAAO,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAK,CAAS,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC;AACrE,CAAC;AAED,oDAAoD;AACpD,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC,QAAQ,EAAE,SAAS,EAAE,EAAE,QAAQ,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC"}
1
+ {"version":3,"file":"states.js","sourceRoot":"","sources":["../../src/primitives/states.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,2CAA2C;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAgCzC;;GAEG;AACH;;;;GAIG;AACH,MAAM,UAAU,MAAM,CAAyB,GAAG,IAAwC;IACxF,MAAM,GAAG,GAAG,IAAiB,CAAC;IAC9B,MAAM,IAAI,GAAG,GAAG,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACjC,MAAM,IAAI,GAAG,CAAC,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAkB,CAAC;IACtF,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAU,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC;IAChE,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,SAAS,CAAC,gBAAgB,EAAE,mBAAmB,CAAC,CAAC;IAChF,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC,CAAC;YAAE,MAAM,SAAS,CAAC,gBAAgB,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;IAC3F,CAAC;IACD,IAAI,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,MAAM;QAAE,MAAM,SAAS,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC;IACvF,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC;IACrC,KAAK,MAAM,CAAC,IAAI,CAAC,GAAG,QAAQ,EAAE,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;QACvE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;YAAE,MAAM,SAAS,CAAC,gBAAgB,EAAE,MAAM,CAAC,gBAAgB,CAAC,CAAC;IACtF,CAAC;IACD,KAAK,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC,EAAE,CAAC;QAC/D,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAU,CAAC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE;YAAE,MAAM,SAAS,CAAC,gBAAgB,EAAE,QAAQ,KAAK,kBAAkB,CAAC,CAAC;IACrJ,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC;QACnB,CAAC,MAAM,CAAC,EAAE,IAAa;QACvB,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC;QAClC,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC;QACtC,OAAO,EAAE,CAAC,IAAI,CAAC,OAAO,IAAI,MAAM,CAAC,CAAC,CAAC,CAAM;QACzC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;KAC1C,CAAC,CAAC;AACL,CAAC;AAED,8CAA8C;AAC9C,MAAM,UAAU,QAAQ,CAAC,CAAU;IACjC,OAAO,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAK,CAAS,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC;AACrE,CAAC;AAED,oDAAoD;AACpD,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC,QAAQ,EAAE,SAAS,EAAE,EAAE,QAAQ,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC"}
package/dist/read.d.ts CHANGED
@@ -4,44 +4,80 @@ import type { Kernel } from './kernel.js';
4
4
  import { type AnyAggregate } from './primitives/aggregate.js';
5
5
  import type { AnyQuery } from './primitives/flow.js';
6
6
  import type { Ctx } from './types.js';
7
+ /** HTTP 查询参数与服务端查询共用;页码从 1 开始,字符串值在执行前按字段类型严格转换。 */
7
8
  export interface ListParams {
9
+ /** 从 1 开始的页码,省略默认 1;接受正安全整数或纯数字字符串,非法值明确拒绝。 */
8
10
  page?: number | string;
11
+ /** 请求每页条数,省略默认 20;普通列表/关联读取最多 200,导出内部另设分批上限。 */
9
12
  pageSize?: number | string;
10
13
  /** 排序:字段名,前面加 - 表示倒序,例如 -createdAt */
11
14
  sort?: string;
15
+ /** 创建时间下界(包含边界),接受合法日期或带时区 ISO 时间;省略不限制下界。 */
12
16
  createdAfter?: string;
17
+ /** 创建时间上界(不含边界),格式同 createdAfter,必须晚于已指定的下界。 */
13
18
  createdBefore?: string;
14
19
  /** 其他键:字段等值过滤;status 可以逗号分隔多个 */
15
20
  [field: string]: unknown;
16
21
  }
22
+ /** total 是当前权限/筛选条件内的总数,items 仅是当前页,不能据此推断完整关联集合。 */
17
23
  export interface Page<T> {
24
+ /** 当前页可见记录,已经安全投影;空数组只说明本页无结果。 */
18
25
  items: T[];
26
+ /** 同权限与筛选条件的总条数,不是数据库全局行数。 */
19
27
  total: number;
28
+ /** 实际使用的一基页码。 */
20
29
  page: number;
30
+ /** 实际采用的分页大小,可能受服务端上限限制。 */
21
31
  pageSize: number;
22
32
  }
33
+ /**
34
+ * 面向用户的统一只读服务。能力检查在 TypeScript 层,行级范围与敏感掩码由 safe 视图执行。
35
+ * 有明文交付或导出时先可靠写审计;审计失败不会返回已经查询到的数据,避免先泄露后补日志。
36
+ */
23
37
  export declare class ReadSide {
24
38
  private readonly k;
25
39
  constructor(k: Kernel);
26
40
  /** 读侧账号上的只读事务,已写入会话变量 */
27
- tx<T>(ctx: Ctx, fn: (tx: Transaction<any>) => Promise<T>): Promise<T>;
41
+ /** 所有查询共享此边界;repeatable read 用于多次读取必须看到同一快照的关联/导出场景。 */
42
+ tx<T>(ctx: Ctx, fn: (tx: Transaction<any>) => Promise<T>, isolation?: 'read committed' | 'repeatable read'): Promise<T>;
43
+ /** 解析注册聚合,未知名称统一 NOT_FOUND,不暴露内部注册细节。 */
28
44
  private agg;
45
+ /** read/export 能力与数据范围分开检查;通过此函数不意味着某条记录可见。 */
29
46
  private need;
30
47
  /** 明文读取敏感字段要写读审计 */
31
48
  private auditUnmask;
49
+ /** 查询单条安全视图记录;非法 ID、范围外和不存在都按 NOT_FOUND 处理,明文审计后才交付。 */
32
50
  get(a: AnyAggregate | string, id: string, ctx: Ctx): Promise<Record<string, any>>;
51
+ /** 普通页面最多每页 200 条;输出由安全视图已掩码,关联集合需另调 related。 */
33
52
  list(a: AnyAggregate | string, params: ListParams, ctx: Ctx): Promise<Page<Record<string, any>>>;
53
+ /** 关联按需分页:先确认所属对象可见,再从目标安全视图读取;两侧能力、范围和掩码都生效。 */
54
+ related(a: AnyAggregate | string, id: string, fieldName: string, params: ListParams, ctx: Ctx): Promise<Page<Record<string, any>>>;
55
+ /**
56
+ * 构造安全视图分页和同口径计数,restrict 让关联读取复用相同过滤逻辑。
57
+ * 隐藏、敏感、JSON 和集合字段不能被普通等值筛选旁路探测;默认排除 deleted。
58
+ * 排序追加 id 作为稳定次序;时间窗口为 createdAfter 含边界、createdBefore 不含边界。
59
+ */
34
60
  private listIn;
35
- /** 导出:就是读,走安全视图,自然带范围和脱敏;需要 聚合.export 能力,并写读审计 */
36
- export(a: AnyAggregate | string, params: ListParams, ctx: Ctx): Promise<{
61
+ /**
62
+ * worker 使用的导出构建器:同一只读快照内分批读取,避免翻页期间数据变化导致漏行或重复。
63
+ * 上限用于约束单任务内存与数据库文件存储;超限明确失败,绝不能静默截断成“成功文件”。
64
+ * HTTP 只提交任务,保留本方法供受控的服务端调用。
65
+ */
66
+ export(a: AnyAggregate | string, params: ListParams, ctx: Ctx, auditTransaction?: Transaction<any>): Promise<{
37
67
  filename: string;
38
68
  contentType: string;
39
69
  body: string;
40
70
  rows: number;
41
71
  }>;
42
72
  /** 执行 query 声明:需要 reads 里每个聚合的 read 能力;手写 SQL 用 :name 占位,全部参数化 */
73
+ /**
74
+ * SQL/build 在只读事务中执行;任意报表无法反推出全部对象 ID,因此按声明 reads 保守记录明文审计。
75
+ * audit.purpose 另形成用途审计,任一可靠审计失败都会阻止交付,不能以空结果冒充成功。
76
+ */
43
77
  run(q: AnyQuery | string, raw: unknown, ctx: Ctx): Promise<Record<string, any>[]>;
44
78
  }
45
79
  /** :name 占位 → 参数化 SQL(:: 类型转换不受影响) */
80
+ /** SQL 模板必须来自可信声明,用户只能提供占位参数;缺少命名参数立即报 INVALID_INPUT。 */
46
81
  export declare function compileNamedSql(text: string, input: Record<string, unknown>): import("kysely").RawBuilder<Record<string, unknown>>;
82
+ /** 外层接收字段名动态的查询参数;具体白名单、类型、分页边界在 listIn 中结合聚合校验。 */
47
83
  export declare const listParamsSchema: z.ZodRecord<z.ZodString, z.ZodUnknown>;