@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.
- package/README.md +71 -9
- package/dist/authorization.d.ts +34 -0
- package/dist/authorization.js +89 -0
- package/dist/authorization.js.map +1 -0
- package/dist/brands.d.ts +10 -0
- package/dist/brands.js +11 -1
- package/dist/brands.js.map +1 -1
- package/dist/db/connect.d.ts +14 -0
- package/dist/db/connect.js +18 -2
- package/dist/db/connect.js.map +1 -1
- package/dist/db/ddl.d.ts +40 -1
- package/dist/db/ddl.js +111 -7
- package/dist/db/ddl.js.map +1 -1
- package/dist/db/event-version-schema.d.ts +9 -0
- package/dist/db/event-version-schema.js +10 -0
- package/dist/db/event-version-schema.js.map +1 -0
- package/dist/db/index.d.ts +4 -0
- package/dist/db/index.js +4 -0
- package/dist/db/index.js.map +1 -1
- package/dist/db/migrate.d.ts +10 -0
- package/dist/db/migrate.js +9 -1
- package/dist/db/migrate.js.map +1 -1
- package/dist/db/rows.d.ts +2 -0
- package/dist/db/rows.js +6 -0
- package/dist/db/rows.js.map +1 -1
- package/dist/db/runtime-schema.d.ts +9 -0
- package/dist/db/runtime-schema.js +41 -0
- package/dist/db/runtime-schema.js.map +1 -0
- package/dist/db/session.d.ts +6 -0
- package/dist/db/session.js +6 -0
- package/dist/db/session.js.map +1 -1
- package/dist/errors.d.ts +12 -0
- package/dist/errors.js +14 -0
- package/dist/errors.js.map +1 -1
- package/dist/files/links.d.ts +10 -0
- package/dist/files/links.js +50 -0
- package/dist/files/links.js.map +1 -0
- package/dist/files/schema.d.ts +9 -0
- package/dist/files/schema.js +51 -0
- package/dist/files/schema.js.map +1 -0
- package/dist/index.d.ts +12 -2
- package/dist/index.js +12 -2
- package/dist/index.js.map +1 -1
- package/dist/kernel.d.ts +88 -0
- package/dist/kernel.js +82 -41
- package/dist/kernel.js.map +1 -1
- package/dist/mask.d.ts +7 -1
- package/dist/mask.js +11 -0
- package/dist/mask.js.map +1 -1
- package/dist/org.d.ts +7 -0
- package/dist/org.js +5 -0
- package/dist/org.js.map +1 -1
- package/dist/pipeline/draft.d.ts +16 -0
- package/dist/pipeline/draft.js +39 -0
- package/dist/pipeline/draft.js.map +1 -0
- package/dist/pipeline/idempotency.d.ts +6 -0
- package/dist/pipeline/idempotency.js +6 -0
- package/dist/pipeline/idempotency.js.map +1 -1
- package/dist/pipeline/invoker.d.ts +9 -0
- package/dist/pipeline/invoker.js +22 -0
- package/dist/pipeline/invoker.js.map +1 -0
- package/dist/pipeline/load.d.ts +13 -0
- package/dist/pipeline/load.js +22 -6
- package/dist/pipeline/load.js.map +1 -1
- package/dist/primitives/aggregate.d.ts +95 -2
- package/dist/primitives/aggregate.js +15 -1
- package/dist/primitives/aggregate.js.map +1 -1
- package/dist/primitives/business-process.d.ts +128 -0
- package/dist/primitives/business-process.js +62 -0
- package/dist/primitives/business-process.js.map +1 -0
- package/dist/primitives/collection.d.ts +65 -0
- package/dist/primitives/collection.js +79 -0
- package/dist/primitives/collection.js.map +1 -0
- package/dist/primitives/command.d.ts +110 -11
- package/dist/primitives/command.js +16 -0
- package/dist/primitives/command.js.map +1 -1
- package/dist/primitives/constraints.d.ts +30 -1
- package/dist/primitives/constraints.js +14 -0
- package/dist/primitives/constraints.js.map +1 -1
- package/dist/primitives/event.d.ts +36 -0
- package/dist/primitives/event.js +12 -1
- package/dist/primitives/event.js.map +1 -1
- package/dist/primitives/fields.d.ts +176 -36
- package/dist/primitives/fields.js +134 -14
- package/dist/primitives/fields.js.map +1 -1
- package/dist/primitives/flow.d.ts +173 -8
- package/dist/primitives/flow.js +29 -2
- package/dist/primitives/flow.js.map +1 -1
- package/dist/primitives/states.d.ts +34 -2
- package/dist/primitives/states.js +15 -0
- package/dist/primitives/states.js.map +1 -1
- package/dist/read.d.ts +39 -3
- package/dist/read.js +193 -48
- package/dist/read.js.map +1 -1
- package/dist/registry.d.ts +51 -1
- package/dist/registry.js +105 -5
- package/dist/registry.js.map +1 -1
- package/dist/relations.d.ts +21 -0
- package/dist/relations.js +65 -0
- package/dist/relations.js.map +1 -0
- package/dist/runtime.d.ts +35 -0
- package/dist/runtime.js +9 -0
- package/dist/runtime.js.map +1 -0
- package/dist/scope/index.d.ts +5 -0
- package/dist/scope/index.js +5 -0
- package/dist/scope/index.js.map +1 -1
- package/dist/telemetry.d.ts +8 -0
- package/dist/telemetry.js +28 -3
- package/dist/telemetry.js.map +1 -1
- package/dist/transfer/csv.d.ts +19 -0
- package/dist/transfer/csv.js +125 -0
- package/dist/transfer/csv.js.map +1 -0
- package/dist/transfer/index.d.ts +67 -0
- package/dist/transfer/index.js +271 -0
- package/dist/transfer/index.js.map +1 -0
- package/dist/transfer/schema.d.ts +9 -0
- package/dist/transfer/schema.js +36 -0
- package/dist/transfer/schema.js.map +1 -0
- package/dist/types.d.ts +38 -2
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -1
- package/dist/util/names.d.ts +6 -0
- package/dist/util/names.js +6 -0
- package/dist/util/names.js.map +1 -1
- package/dist/worker/consumers.d.ts +7 -0
- package/dist/worker/consumers.js +21 -9
- package/dist/worker/consumers.js.map +1 -1
- package/dist/worker/health.d.ts +38 -0
- package/dist/worker/health.js +20 -0
- package/dist/worker/health.js.map +1 -0
- package/dist/worker/index.d.ts +34 -1
- package/dist/worker/index.js +176 -37
- package/dist/worker/index.js.map +1 -1
- package/dist/worker/outbox.d.ts +25 -2
- package/dist/worker/outbox.js +40 -2
- package/dist/worker/outbox.js.map +1 -1
- package/dist/worker/scheduler.d.ts +10 -1
- package/dist/worker/scheduler.js +34 -4
- package/dist/worker/scheduler.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { AnyCommand } from '../primitives/command.js';
|
|
2
|
+
import type { Ctx } from '../types.js';
|
|
3
|
+
/** 实际执行与权限解释共享调用身份判断,防止诊断给出另一套结论。 */
|
|
4
|
+
/**
|
|
5
|
+
* 要求调用者已经完成 actor 存在性检查。系统身份是框架特权通道;reaction 按 process 语义检查。
|
|
6
|
+
* 用户/匿名请求不能直接调用仅供流程、任务或外部回调的命令;不满足时抛 FORBIDDEN。
|
|
7
|
+
* 通过本检查仍必须继续能力和范围校验,不能把 invokedBy 当作角色授权。
|
|
8
|
+
*/
|
|
9
|
+
export declare function assertInvoker(cmd: AnyCommand, ctx: Ctx, capability: string): void;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** 调用身份是独立于能力点和数据范围的一道边界;实际执行与只读解释共用此实现。 */
|
|
2
|
+
import { cageError } from '../errors.js';
|
|
3
|
+
/** 实际执行与权限解释共享调用身份判断,防止诊断给出另一套结论。 */
|
|
4
|
+
/**
|
|
5
|
+
* 要求调用者已经完成 actor 存在性检查。系统身份是框架特权通道;reaction 按 process 语义检查。
|
|
6
|
+
* 用户/匿名请求不能直接调用仅供流程、任务或外部回调的命令;不满足时抛 FORBIDDEN。
|
|
7
|
+
* 通过本检查仍必须继续能力和范围校验,不能把 invokedBy 当作角色授权。
|
|
8
|
+
*/
|
|
9
|
+
export function assertInvoker(cmd, ctx, capability) {
|
|
10
|
+
const kind = ctx.actor.kind;
|
|
11
|
+
if (kind === 'system')
|
|
12
|
+
return;
|
|
13
|
+
if (kind === 'user' || kind === 'anonymous') {
|
|
14
|
+
if (!cmd.invokedBy.includes('user'))
|
|
15
|
+
throw cageError('FORBIDDEN', { capability, message: `该命令只接受 ${cmd.invokedBy.join('/')} 调用` });
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const as = kind === 'reaction' ? 'process' : kind;
|
|
19
|
+
if (!cmd.invokedBy.includes('user') && !cmd.invokedBy.includes(as))
|
|
20
|
+
throw cageError('FORBIDDEN', { capability, message: `${kind} 身份不能调用该命令` });
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=invoker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"invoker.js","sourceRoot":"","sources":["../../src/pipeline/invoker.ts"],"names":[],"mappings":"AAAA,4CAA4C;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAIzC,qCAAqC;AACrC;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,GAAe,EAAE,GAAQ,EAAE,UAAkB;IACzE,MAAM,IAAI,GAAG,GAAG,CAAC,KAAM,CAAC,IAAI,CAAC;IAC7B,IAAI,IAAI,KAAK,QAAQ;QAAE,OAAO;IAC9B,IAAI,IAAI,KAAK,MAAM,IAAI,IAAI,KAAK,WAAW,EAAE,CAAC;QAC5C,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC;YAAE,MAAM,SAAS,CAAC,WAAW,EAAE,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC;QACnI,OAAO;IACT,CAAC;IACD,MAAM,EAAE,GAAG,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC;IAClD,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAS,CAAC;QAAE,MAAM,SAAS,CAAC,WAAW,EAAE,EAAE,UAAU,EAAE,OAAO,EAAE,GAAG,IAAI,YAAY,EAAE,CAAC,CAAC;AACxJ,CAAC"}
|
package/dist/pipeline/load.d.ts
CHANGED
|
@@ -3,13 +3,26 @@ import type { AnyAggregate } from '../primitives/aggregate.js';
|
|
|
3
3
|
import type { FindOptions, ReadRepo, Reads } from '../primitives/command.js';
|
|
4
4
|
import type { Ctx } from '../types.js';
|
|
5
5
|
type Db = Transaction<any> | Kysely<any>;
|
|
6
|
+
/** 内核内部基表引用;业务公开读入口应使用 ReadSide 的 safe 视图,而非直接复用此函数。 */
|
|
6
7
|
export declare const tableRef: (agg: AnyAggregate, alias?: string) => string;
|
|
8
|
+
/**
|
|
9
|
+
* 在 SQL 中同时限定 ID、租户和有效范围,再加载内部关系 ID;范围外与不存在统一返回 null。
|
|
10
|
+
* 写流水线传 forUpdate=true 持有行锁直至事务结束;诊断/读依赖传 false,不借此获得写权限。
|
|
11
|
+
*/
|
|
7
12
|
export declare function loadScoped(tx: Db, agg: AnyAggregate, id: string, ctx: Ctx, forUpdate: boolean): Promise<Record<string, any> | null>;
|
|
8
13
|
/** 把等值条件翻译成列条件;只允许声明过的字段 */
|
|
9
14
|
export declare function whereColumns(agg: AnyAggregate, where: Record<string, unknown> | undefined): [string, unknown][];
|
|
15
|
+
/**
|
|
16
|
+
* 构造尚未执行的范围查询;仅接受声明列及系统列,null 转 IS NULL,值始终参数化。
|
|
17
|
+
* 默认按创建时间升序,默认最多 1000 条、上限 5000 条;JSON 明细和集合不是主表筛选列。
|
|
18
|
+
*/
|
|
10
19
|
export declare function scopedFind(tx: Db, agg: AnyAggregate, ctx: Ctx, where?: Record<string, unknown>, opts?: FindOptions): import("kysely").SelectQueryBuilder<any, string, {
|
|
11
20
|
[x: string]: any;
|
|
12
21
|
}>;
|
|
13
22
|
/** 第 7 关:只把 reads 里的聚合做成只读仓储,同样受范围约束 */
|
|
23
|
+
/**
|
|
24
|
+
* 仓储绑定当前事务与身份,handler 的读操作因此看到同一事务状态且不能写入。
|
|
25
|
+
* Proxy 将未声明别名转成明确 CAGE-READS-001;then/toJSON 保留为普通对象协议属性。
|
|
26
|
+
*/
|
|
14
27
|
export declare function bindReads<R extends Reads>(tx: Db, reads: R, ctx: Ctx): Record<string, ReadRepo<any>>;
|
|
15
28
|
export {};
|
package/dist/pipeline/load.js
CHANGED
|
@@ -4,7 +4,14 @@ import { cageError } from '../errors.js';
|
|
|
4
4
|
import { rowToSnapshot } from '../db/rows.js';
|
|
5
5
|
import { compileScope } from '../scope/index.js';
|
|
6
6
|
import { snake } from '../util/names.js';
|
|
7
|
+
import { loadRelationIds } from '../relations.js';
|
|
8
|
+
import { isRelation } from '../primitives/fields.js';
|
|
9
|
+
/** 内核内部基表引用;业务公开读入口应使用 ReadSide 的 safe 视图,而非直接复用此函数。 */
|
|
7
10
|
export const tableRef = (agg, alias = 't') => `${agg.table.schema}.${agg.table.name} as ${alias}`;
|
|
11
|
+
/**
|
|
12
|
+
* 在 SQL 中同时限定 ID、租户和有效范围,再加载内部关系 ID;范围外与不存在统一返回 null。
|
|
13
|
+
* 写流水线传 forUpdate=true 持有行锁直至事务结束;诊断/读依赖传 false,不借此获得写权限。
|
|
14
|
+
*/
|
|
8
15
|
export async function loadScoped(tx, agg, id, ctx, forUpdate) {
|
|
9
16
|
let q = tx
|
|
10
17
|
.selectFrom(tableRef(agg))
|
|
@@ -14,7 +21,7 @@ export async function loadScoped(tx, agg, id, ctx, forUpdate) {
|
|
|
14
21
|
if (forUpdate)
|
|
15
22
|
q = q.forUpdate();
|
|
16
23
|
const row = await q.executeTakeFirst();
|
|
17
|
-
return row ? rowToSnapshot(agg, row) : null;
|
|
24
|
+
return row ? (await loadRelationIds(tx, agg, [rowToSnapshot(agg, row)], ctx))[0] : null;
|
|
18
25
|
}
|
|
19
26
|
const SYSTEM_KEYS = {
|
|
20
27
|
id: 'id', status: 'status', version: 'version', createdAt: 'created_at', updatedAt: 'updated_at',
|
|
@@ -29,12 +36,16 @@ export function whereColumns(agg, where) {
|
|
|
29
36
|
if (!col)
|
|
30
37
|
throw cageError('INVALID_INPUT', { field: k, message: `${agg.name} 没有字段 ${k}` });
|
|
31
38
|
const f = agg.fields[k];
|
|
32
|
-
if (f
|
|
33
|
-
throw cageError('INVALID_INPUT', { field: k, message: '
|
|
39
|
+
if (f && (f.sql === 'jsonb' || isRelation(f)))
|
|
40
|
+
throw cageError('INVALID_INPUT', { field: k, message: '明细或集合关系不能作为主表查询条件' });
|
|
34
41
|
out.push([col, v]);
|
|
35
42
|
}
|
|
36
43
|
return out;
|
|
37
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* 构造尚未执行的范围查询;仅接受声明列及系统列,null 转 IS NULL,值始终参数化。
|
|
47
|
+
* 默认按创建时间升序,默认最多 1000 条、上限 5000 条;JSON 明细和集合不是主表筛选列。
|
|
48
|
+
*/
|
|
38
49
|
export function scopedFind(tx, agg, ctx, where, opts = {}) {
|
|
39
50
|
let q = tx.selectFrom(tableRef(agg)).selectAll().where(compileScope(agg.scope, agg.name, ctx, 't'));
|
|
40
51
|
for (const [col, v] of whereColumns(agg, where)) {
|
|
@@ -42,7 +53,8 @@ export function scopedFind(tx, agg, ctx, where, opts = {}) {
|
|
|
42
53
|
}
|
|
43
54
|
if (opts.orderBy) {
|
|
44
55
|
const col = SYSTEM_KEYS[opts.orderBy] ?? (opts.orderBy in agg.fields ? snake(opts.orderBy) : undefined);
|
|
45
|
-
|
|
56
|
+
const field = agg.fields[opts.orderBy];
|
|
57
|
+
if (!col || (field && isRelation(field)))
|
|
46
58
|
throw cageError('INVALID_INPUT', { field: opts.orderBy });
|
|
47
59
|
q = q.orderBy(sql.ref(`t.${col}`), opts.desc ? 'desc' : 'asc');
|
|
48
60
|
}
|
|
@@ -52,15 +64,19 @@ export function scopedFind(tx, agg, ctx, where, opts = {}) {
|
|
|
52
64
|
return q.limit(Math.min(opts.limit ?? 1000, 5000));
|
|
53
65
|
}
|
|
54
66
|
/** 第 7 关:只把 reads 里的聚合做成只读仓储,同样受范围约束 */
|
|
67
|
+
/**
|
|
68
|
+
* 仓储绑定当前事务与身份,handler 的读操作因此看到同一事务状态且不能写入。
|
|
69
|
+
* Proxy 将未声明别名转成明确 CAGE-READS-001;then/toJSON 保留为普通对象协议属性。
|
|
70
|
+
*/
|
|
55
71
|
export function bindReads(tx, reads, ctx) {
|
|
56
72
|
const deps = {};
|
|
57
73
|
for (const [alias, agg] of Object.entries(reads)) {
|
|
58
74
|
deps[alias] = Object.freeze({
|
|
59
75
|
get: async (id) => (typeof id === 'string' && /^[0-9a-f-]{36}$/i.test(id) ? loadScoped(tx, agg, id, ctx, false) : null),
|
|
60
|
-
find: async (where, opts) => (await scopedFind(tx, agg, ctx, where, opts).execute()).map((r) => rowToSnapshot(agg, r)),
|
|
76
|
+
find: async (where, opts) => loadRelationIds(tx, agg, (await scopedFind(tx, agg, ctx, where, opts).execute()).map((r) => rowToSnapshot(agg, r)), ctx),
|
|
61
77
|
findOne: async (where) => {
|
|
62
78
|
const rows = await scopedFind(tx, agg, ctx, where, { limit: 1 }).execute();
|
|
63
|
-
return rows[0] ? rowToSnapshot(agg, rows[0]) : null;
|
|
79
|
+
return rows[0] ? (await loadRelationIds(tx, agg, [rowToSnapshot(agg, rows[0])], ctx))[0] : null;
|
|
64
80
|
},
|
|
65
81
|
count: async (where) => {
|
|
66
82
|
let q = tx.selectFrom(tableRef(agg)).select(sql `count(*)::int`.as('n')).where(compileScope(agg.scope, agg.name, ctx, 't'));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"load.js","sourceRoot":"","sources":["../../src/pipeline/load.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAC9C,OAAO,EAAE,GAAG,EAAiC,MAAM,QAAQ,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAGzC,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"load.js","sourceRoot":"","sources":["../../src/pipeline/load.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,52 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 声明业务对象的字段、生命周期、范围元数据和最终不变量,并派生快照类型。
|
|
3
|
+
* 注册表、DDL、契约、安全读和写流水线共同消费同一规格;聚合描述不持有记录或连接,数据变化必须经所属命令完成。
|
|
4
|
+
*/
|
|
1
5
|
import { z } from 'zod';
|
|
2
6
|
import { AGGREGATE } from '../brands.js';
|
|
3
7
|
import type { ScopeSpec } from '../scope/index.js';
|
|
4
8
|
import { type AnyField, type FieldValues, type Fields } from './fields.js';
|
|
5
9
|
import { type StatesDef } from './states.js';
|
|
6
10
|
/** 每张表都有的系统列 */
|
|
11
|
+
/** 系统列由写流水线维护;时间对外统一为字符串,version 用于乐观并发检查。 */
|
|
7
12
|
export interface SystemCols<S extends string> {
|
|
13
|
+
/**
|
|
14
|
+
* 对象 UUID,由新建流水线生成或按受控规则采用输入,后续 handler 不能改写。
|
|
15
|
+
*/
|
|
8
16
|
id: string;
|
|
17
|
+
/**
|
|
18
|
+
* 持久记录的乐观并发版本;新记录提交后为 1,成功更新递增,客户端可提交 expectedVersion 检测冲突。
|
|
19
|
+
*/
|
|
9
20
|
version: number;
|
|
21
|
+
/**
|
|
22
|
+
* 当前生命周期状态,类型来自声明 states;由命令 to 确定,handler 不直接迁移。
|
|
23
|
+
*/
|
|
10
24
|
status: S;
|
|
25
|
+
/**
|
|
26
|
+
* 对象创建 UTC 时间字符串,更新时保持原创建时间。
|
|
27
|
+
*/
|
|
11
28
|
createdAt: string;
|
|
29
|
+
/**
|
|
30
|
+
* 最近成功写入时间字符串,同一命令使用固定时间。
|
|
31
|
+
*/
|
|
12
32
|
updatedAt: string;
|
|
13
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* 聚合字段值与系统列的组合类型;用于持久记录和业务规则输入。
|
|
36
|
+
* 不含租户内部列,hasMany 不在可写字段集合中;是否已做范围/掩码应以具体读入口为准,类型本身不证明授权。
|
|
37
|
+
*/
|
|
14
38
|
export type Snapshot<F extends Fields = Fields, S extends string = string> = FieldValues<F> & SystemCols<S>;
|
|
39
|
+
/**
|
|
40
|
+
* 由内核维护的字段名白名单,禁止重复声明业务列;status 可用专门 StatesDef 语法声明生命周期,但不能伪装普通字段。
|
|
41
|
+
*/
|
|
15
42
|
export declare const RESERVED_FIELDS: readonly ["id", "version", "status", "createdAt", "updatedAt", "tenantId"];
|
|
43
|
+
/** 注册、DDL、契约和读写流水线共享的对象规格;声明对象本身不持有数据或数据库连接。 */
|
|
16
44
|
export interface Aggregate<N extends string = string, F extends Fields = Fields, S extends string = string> {
|
|
45
|
+
/**
|
|
46
|
+
* 聚合品牌标记,供注册表与类型守卫识别,不触发注册副作用。
|
|
47
|
+
*/
|
|
17
48
|
readonly [AGGREGATE]: true;
|
|
18
49
|
/** 聚合名,同时是表名和能力点前缀 */
|
|
19
50
|
readonly name: N;
|
|
51
|
+
/**
|
|
52
|
+
* 冻结的业务字段定义映射,状态声明已移出,系统保留列由内核补充。
|
|
53
|
+
*/
|
|
20
54
|
readonly fields: F;
|
|
55
|
+
/**
|
|
56
|
+
* 规范化生命周期定义;未显式声明时为 active/deleted,不自动生成 CRUD 命令。
|
|
57
|
+
*/
|
|
21
58
|
readonly states: StatesDef<S>;
|
|
59
|
+
/**
|
|
60
|
+
* 部门/所有者等范围字段元数据;真实可见范围来自当前角色 grants,声明不授予权限。
|
|
61
|
+
*/
|
|
22
62
|
readonly scope: ScopeSpec;
|
|
63
|
+
/**
|
|
64
|
+
* 按名称定义的最终快照布尔检查,在字段合并与目标状态确定后逐项验证;任一 false 拒绝整笔命令事务。
|
|
65
|
+
*/
|
|
23
66
|
readonly invariants: Readonly<Record<string, (o: Snapshot<F, S>) => boolean>>;
|
|
24
67
|
/** 由 fields 拼出的整体校验 */
|
|
25
68
|
readonly schema: z.ZodObject<any>;
|
|
69
|
+
/**
|
|
70
|
+
* 物理表定位:schema 默认 core,name 默认聚合名 snake_case;sys 用于框架组织和运行聚合。
|
|
71
|
+
*/
|
|
26
72
|
readonly table: {
|
|
27
|
-
readonly schema: 'core' | 'sys';
|
|
73
|
+
readonly schema: 'core' | 'sys'; /** 已解析的物理表名,默认聚合名称 snake_case,不是显示标题。 */
|
|
28
74
|
readonly name: string;
|
|
29
75
|
};
|
|
76
|
+
/**
|
|
77
|
+
* 聚合业务含义说明,供本体和文档展示,不自动成为数据库约束。
|
|
78
|
+
*/
|
|
30
79
|
readonly description?: string;
|
|
31
80
|
/** 内部选项:授权类聚合写入后通知各实例清缓存 */
|
|
32
81
|
readonly options: {
|
|
33
82
|
readonly grants?: 'self' | 'all';
|
|
34
83
|
};
|
|
35
84
|
}
|
|
85
|
+
/**
|
|
86
|
+
* 注册表等异构容器中的聚合擦除类型,保留声明结构而不保留具体字段/状态推导;业务声明应保留 aggregate 返回的精确类型。
|
|
87
|
+
*/
|
|
36
88
|
export type AnyAggregate = Aggregate<string, any, any>;
|
|
89
|
+
/**
|
|
90
|
+
* 从聚合类型提取状态字符串联合;非聚合得到 never,不检查某个命令实际可达性。
|
|
91
|
+
*/
|
|
37
92
|
export type StateOf<A> = A extends Aggregate<any, any, infer S> ? S : never;
|
|
93
|
+
/**
|
|
94
|
+
* 提取聚合原始 Field 映射,用于路由字段、生成器与类型约束;不是数据库行值。
|
|
95
|
+
*/
|
|
38
96
|
export type FieldsOf<A> = A extends Aggregate<any, infer F, any> ? F : never;
|
|
97
|
+
/**
|
|
98
|
+
* 提取特定聚合字段值与系统列的完整快照类型,保留该聚合状态联合。
|
|
99
|
+
*/
|
|
39
100
|
export type SnapshotOf<A> = A extends Aggregate<any, infer F, infer S> ? Snapshot<F, S> : never;
|
|
101
|
+
/**
|
|
102
|
+
* 提取聚合可写业务字段值,不含内核系统列及只读 hasMany 反向集合。
|
|
103
|
+
*/
|
|
40
104
|
export type FieldValuesOf<A> = A extends Aggregate<any, infer F, any> ? FieldValues<F> : never;
|
|
105
|
+
/**
|
|
106
|
+
* aggregate 输入字段容器,允许普通字段和仅可放在 status 键上的状态声明;位置规则在构造时校验。
|
|
107
|
+
*/
|
|
41
108
|
type FieldInput = Record<string, AnyField | StatesDef<any>>;
|
|
109
|
+
/**
|
|
110
|
+
* 从输入字段类型移除 StatesDef,得到真正的业务 Field 映射;状态另放入系统列类型。
|
|
111
|
+
*/
|
|
42
112
|
type PlainFields<FI> = {
|
|
43
113
|
[K in keyof FI as FI[K] extends StatesDef<any> ? never : K]: FI[K] extends AnyField ? FI[K] : never;
|
|
44
114
|
};
|
|
115
|
+
/**
|
|
116
|
+
* 收集 fields 内 StatesDef 的状态联合,供与顶层 states 选项统一推导;运行时仍禁止重复声明状态集。
|
|
117
|
+
*/
|
|
45
118
|
type StatesIn<FI> = {
|
|
46
119
|
[K in keyof FI]: FI[K] extends StatesDef<infer S> ? S : never;
|
|
47
120
|
}[keyof FI];
|
|
121
|
+
/**
|
|
122
|
+
* 优先使用显式顶层状态类型,否则使用 fields.status,均未声明时推导 active/deleted 默认生命周期。
|
|
123
|
+
*/
|
|
48
124
|
type ResolveStates<FI, SS extends string> = [SS] extends [never] ? ([StatesIn<FI>] extends [never] ? 'active' | 'deleted' : StatesIn<FI>) : SS;
|
|
125
|
+
/**
|
|
126
|
+
* 应用提供的聚合声明输入。
|
|
127
|
+
* 字段、状态、范围与不变量统一在构造时验证和冻结;table/options 面向框架内部扩展,普通业务默认写入 core 下聚合对应表。
|
|
128
|
+
*/
|
|
49
129
|
export interface AggregateDef<FI extends FieldInput, SS extends string> {
|
|
130
|
+
/**
|
|
131
|
+
* 业务字段定义;可用 status: StatesDef 声明状态,但不能同时提供顶层 states,也不能重定义其他系统列。
|
|
132
|
+
*/
|
|
50
133
|
fields: FI;
|
|
51
134
|
/** 状态集;也可以写在 fields 里:status: POStatus */
|
|
52
135
|
states?: StatesDef<SS>;
|
|
@@ -54,10 +137,13 @@ export interface AggregateDef<FI extends FieldInput, SS extends string> {
|
|
|
54
137
|
scope?: ScopeSpec;
|
|
55
138
|
/** 不变量:普通函数,每次写入后逐条校验 */
|
|
56
139
|
invariants?: Record<string, (o: Snapshot<PlainFields<FI>, ResolveStates<FI, SS>>) => boolean>;
|
|
140
|
+
/**
|
|
141
|
+
* 可选中文对象职责说明,帮助目录与 agent 识别业务含义;不影响聚合标识。
|
|
142
|
+
*/
|
|
57
143
|
description?: string;
|
|
58
144
|
/** 框架内部使用:系统聚合放在 sys 模式 */
|
|
59
145
|
table?: {
|
|
60
|
-
schema?: 'core' | 'sys';
|
|
146
|
+
schema?: 'core' | 'sys'; /** 内部物理表名覆盖,省略时由聚合名转 snake_case。 */
|
|
61
147
|
name?: string;
|
|
62
148
|
};
|
|
63
149
|
/** 框架内部使用 */
|
|
@@ -65,8 +151,15 @@ export interface AggregateDef<FI extends FieldInput, SS extends string> {
|
|
|
65
151
|
grants?: 'self' | 'all';
|
|
66
152
|
};
|
|
67
153
|
}
|
|
154
|
+
/**
|
|
155
|
+
* 校验并冻结聚合规格,统一解析 states 与 fields.status 两种写法,拒绝二义性。
|
|
156
|
+
* 未指定状态时使用默认生命周期;createdBy 自动参与本人范围,其他范围字段必须真实存在。
|
|
157
|
+
* 声明错误立即抛出 CageDeclarationError;数据库建表和实际授权在后续阶段完成。
|
|
158
|
+
*/
|
|
68
159
|
export declare function aggregate<const N extends string, FI extends FieldInput, SS extends string = never>(name: N, def: AggregateDef<FI, SS>): Aggregate<N, PlainFields<FI>, ResolveStates<FI, SS>>;
|
|
160
|
+
/** 按品牌标记识别声明;不依赖 instanceof,因此支持模块副本和 re-export。 */
|
|
69
161
|
export declare function isAggregate(v: unknown): v is AnyAggregate;
|
|
162
|
+
/** 检查全部候选状态是否已声明;不判断迁移可达性或当前记录能否执行某个命令。 */
|
|
70
163
|
export declare function hasStates(agg: AnyAggregate, s: string | readonly string[]): boolean;
|
|
71
164
|
/** 聚合中不对外暴露(hidden)的字段名 */
|
|
72
165
|
export declare function hiddenFields(agg: AnyAggregate): string[];
|
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
import { AGGREGATE } from '../brands.js';
|
|
2
2
|
import { declError } from '../errors.js';
|
|
3
3
|
import { snake } from '../util/names.js';
|
|
4
|
-
import { isField, objectSchema } from './fields.js';
|
|
4
|
+
import { isField, isRelation, isFile, objectSchema } from './fields.js';
|
|
5
5
|
import { DEFAULT_STATES, isStates } from './states.js';
|
|
6
|
+
/**
|
|
7
|
+
* 由内核维护的字段名白名单,禁止重复声明业务列;status 可用专门 StatesDef 语法声明生命周期,但不能伪装普通字段。
|
|
8
|
+
*/
|
|
6
9
|
export const RESERVED_FIELDS = ['id', 'version', 'status', 'createdAt', 'updatedAt', 'tenantId'];
|
|
10
|
+
/**
|
|
11
|
+
* 校验并冻结聚合规格,统一解析 states 与 fields.status 两种写法,拒绝二义性。
|
|
12
|
+
* 未指定状态时使用默认生命周期;createdBy 自动参与本人范围,其他范围字段必须真实存在。
|
|
13
|
+
* 声明错误立即抛出 CageDeclarationError;数据库建表和实际授权在后续阶段完成。
|
|
14
|
+
*/
|
|
7
15
|
export function aggregate(name, def) {
|
|
8
16
|
if (!/^[A-Z][A-Za-z0-9]*$/.test(name)) {
|
|
9
17
|
throw declError('CAGE-NAME-001', `聚合名 ${name} 必须是大驼峰`);
|
|
@@ -35,6 +43,10 @@ export function aggregate(name, def) {
|
|
|
35
43
|
for (const f of [sc.deptField, ownerField]) {
|
|
36
44
|
if (f && f !== 'id' && !(f in fields))
|
|
37
45
|
throw declError('CAGE-SCOPE-002', `${name} 的范围字段 ${f} 不存在`);
|
|
46
|
+
if (f && f !== 'id' && isRelation(fields[f]))
|
|
47
|
+
throw declError('CAGE-REL-001', `${name} 的范围字段必须是主表列,不能使用集合关系 ${f}`);
|
|
48
|
+
if (f && f !== 'id' && isFile(fields[f]))
|
|
49
|
+
throw declError('CAGE-FILE-001', `${name} 的范围字段不能使用文件 ${f}`);
|
|
38
50
|
}
|
|
39
51
|
const scope = Object.freeze({ ...sc, ownerField });
|
|
40
52
|
return Object.freeze({
|
|
@@ -50,9 +62,11 @@ export function aggregate(name, def) {
|
|
|
50
62
|
options: Object.freeze({ ...(def.options ?? {}) }),
|
|
51
63
|
});
|
|
52
64
|
}
|
|
65
|
+
/** 按品牌标记识别声明;不依赖 instanceof,因此支持模块副本和 re-export。 */
|
|
53
66
|
export function isAggregate(v) {
|
|
54
67
|
return !!v && typeof v === 'object' && v[AGGREGATE] === true;
|
|
55
68
|
}
|
|
69
|
+
/** 检查全部候选状态是否已声明;不判断迁移可达性或当前记录能否执行某个命令。 */
|
|
56
70
|
export function hasStates(agg, s) {
|
|
57
71
|
return [s].flat().every((x) => agg.states.values.includes(x));
|
|
58
72
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aggregate.js","sourceRoot":"","sources":["../../src/primitives/aggregate.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"aggregate.js","sourceRoot":"","sources":["../../src/primitives/aggregate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 定义面向业务人员与 agent 阅读的流程板块、阶段和连线,并引用真实可执行声明作为证据。
|
|
3
|
+
* 注册表和 CLI 检查说明与实现关系,图工作台据此展示中文流程;该阅读规格不调度命令、创建审批人、授予权限或自动产生事件关系。
|
|
4
|
+
*/
|
|
5
|
+
import { BUSINESS_PROCESS, INBOUND, OUTBOUND } from '../brands.js';
|
|
6
|
+
import type { AnyAggregate } from './aggregate.js';
|
|
7
|
+
import type { AnyCommand } from './command.js';
|
|
8
|
+
import type { AnyEvent } from './event.js';
|
|
9
|
+
import type { AnyQuery, AnyReaction, Approval, Job } from './flow.js';
|
|
10
|
+
/** 使用对象引用,让注册表能发现未导出的实现,避免字符串随重命名悄悄失效。 */
|
|
11
|
+
export type BusinessAction = AnyCommand | AnyEvent | AnyReaction | AnyQuery | Approval | Job | {
|
|
12
|
+
readonly [INBOUND]: true;
|
|
13
|
+
} | {
|
|
14
|
+
readonly [OUTBOUND]: true;
|
|
15
|
+
};
|
|
16
|
+
/** 面向业务阅读的一个阶段;action 是主证据,details 和 related 补充实现与对象联系。 */
|
|
17
|
+
export interface BusinessStage {
|
|
18
|
+
/**
|
|
19
|
+
* 阶段中文名称,必须非空,用于流程阅读卡片。
|
|
20
|
+
*/
|
|
21
|
+
readonly title: string;
|
|
22
|
+
/**
|
|
23
|
+
* 阶段具体业务职责及边界说明,必须非空;不能只靠标题替代。
|
|
24
|
+
*/
|
|
25
|
+
readonly description: string;
|
|
26
|
+
/** 业务职责说明;不替代角色授权或审批选人。 */
|
|
27
|
+
readonly actor?: string;
|
|
28
|
+
/**
|
|
29
|
+
* 可选显示/结构种类:action 操作、decision 决策、end 结束;end 禁止后续连线。
|
|
30
|
+
*/
|
|
31
|
+
readonly kind?: 'action' | 'decision' | 'end';
|
|
32
|
+
/**
|
|
33
|
+
* 该阶段的主要真实声明引用,必须属于允许的命令/事件/反应/查询/审批/任务/连接器类型。
|
|
34
|
+
*/
|
|
35
|
+
readonly action: BusinessAction;
|
|
36
|
+
/**
|
|
37
|
+
* 可选补充实现声明列表,构造后规范化为空或冻结列表;不是按列表自动调度的步骤。
|
|
38
|
+
*/
|
|
39
|
+
readonly details?: readonly BusinessAction[];
|
|
40
|
+
/**
|
|
41
|
+
* 可选关联业务对象及标签/via 证据,说明阶段与其他聚合的联系;CLI 继续核对真实声明关系。
|
|
42
|
+
*/
|
|
43
|
+
readonly related?: readonly {
|
|
44
|
+
/** 关联的实际聚合声明,不接受仅有表名的字符串。 */
|
|
45
|
+
readonly object: AnyAggregate;
|
|
46
|
+
/** 该关联在此阶段的业务意义,必须非空。 */
|
|
47
|
+
readonly label: string;
|
|
48
|
+
/** 这份实际声明负责关联;CLI 继续核对它与阶段、对象之间的关系。 */
|
|
49
|
+
readonly via: BusinessAction;
|
|
50
|
+
}[];
|
|
51
|
+
}
|
|
52
|
+
/** 可视化阶段连线;mode 描述业务推进方式,不会创建运行时自动触发器。 */
|
|
53
|
+
export interface BusinessTransition<S extends string = string> {
|
|
54
|
+
/**
|
|
55
|
+
* 起点阶段 key,必须存在于 stages,不允许从 end 节点继续。
|
|
56
|
+
*/
|
|
57
|
+
readonly from: S;
|
|
58
|
+
/**
|
|
59
|
+
* 终点阶段 key,必须存在于 stages。
|
|
60
|
+
*/
|
|
61
|
+
readonly to: S;
|
|
62
|
+
/**
|
|
63
|
+
* 业务推进方式的阅读说明:人工触发、自动推进或等待;不创建运行时触发器。
|
|
64
|
+
*/
|
|
65
|
+
readonly mode: 'manual' | 'automatic' | 'wait';
|
|
66
|
+
/**
|
|
67
|
+
* 连线的业务条件/动作说明,必须非空;不能代替实际命令约束。
|
|
68
|
+
*/
|
|
69
|
+
readonly label: string;
|
|
70
|
+
/** 次要路径按需展开;审批通过/驳回等主要分支应保持可见。 */
|
|
71
|
+
readonly branch?: boolean;
|
|
72
|
+
}
|
|
73
|
+
/** 将已有可执行声明组织成中文业务流程;保持对象引用以便检查代码与说明的一致性。 */
|
|
74
|
+
export interface BusinessProcess {
|
|
75
|
+
/**
|
|
76
|
+
* 业务流程阅读规格品牌标记,供注册表和图谱识别。
|
|
77
|
+
*/
|
|
78
|
+
readonly [BUSINESS_PROCESS]: true;
|
|
79
|
+
/**
|
|
80
|
+
* 稳定流程标识,使用小写短横线格式;区别于中文标题。
|
|
81
|
+
*/
|
|
82
|
+
readonly name: string;
|
|
83
|
+
/**
|
|
84
|
+
* 流程中文名称,必须非空。
|
|
85
|
+
*/
|
|
86
|
+
readonly title: string;
|
|
87
|
+
/**
|
|
88
|
+
* 完整流程的职责及边界说明,必须非空。
|
|
89
|
+
*/
|
|
90
|
+
readonly description: string;
|
|
91
|
+
/**
|
|
92
|
+
* 所属业务板块元数据:id 为稳定小写短横线标识,title/description 为非空中文名称与说明。
|
|
93
|
+
*/
|
|
94
|
+
readonly area: {
|
|
95
|
+
readonly id: string; /** 业务板块中文名称,必须非空。 */
|
|
96
|
+
readonly title: string; /** 板块职责与边界说明,必须非空。 */
|
|
97
|
+
readonly description: string;
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* 主业务对象的实际聚合声明引用,不接受仅有名称的自造对象。
|
|
101
|
+
*/
|
|
102
|
+
readonly subject: AnyAggregate;
|
|
103
|
+
/**
|
|
104
|
+
* 入口阶段 key,必须存在于 stages,不自动执行其 action。
|
|
105
|
+
*/
|
|
106
|
+
readonly entry: string;
|
|
107
|
+
/**
|
|
108
|
+
* 阶段 key 到阅读规格的冻结映射,每个阶段必须有有效名称、说明和真实声明证据。
|
|
109
|
+
*/
|
|
110
|
+
readonly stages: Readonly<Record<string, BusinessStage>>;
|
|
111
|
+
/**
|
|
112
|
+
* 阶段连线冻结列表,重复边和不存在阶段会在构造时拒绝;实际可执行依赖由 CLI 继续验证。
|
|
113
|
+
*/
|
|
114
|
+
readonly transitions: readonly BusinessTransition[];
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* 校验流程阅读规格的标识、说明、阶段引用、连线和结束节点,再冻结所有容器。
|
|
118
|
+
* 这里只证明规格内部引用成立;阶段间真实事件/命令关系由 CLI 图规则继续验证。
|
|
119
|
+
* 不执行 action,也不因 actor 文案自动授予权限或选择审批人。
|
|
120
|
+
*/
|
|
121
|
+
export declare function businessProcess<const S extends Record<string, BusinessStage>>(name: string, def: Omit<BusinessProcess, typeof BUSINESS_PROCESS | 'name' | 'stages' | 'entry' | 'transitions'> & {
|
|
122
|
+
/** 以稳定阶段 key 定义具体阶段,泛型据此约束入口和连线。 */
|
|
123
|
+
stages: S;
|
|
124
|
+
/** 入口必须是 stages 的已有 key,构造时再次校验实际存在。 */
|
|
125
|
+
entry: keyof S & string;
|
|
126
|
+
/** 连接已有阶段的阅读规格,起终点类型受 stages 限定,不自动生成运行时事件关系。 */
|
|
127
|
+
transitions: readonly BusinessTransition<keyof S & string>[];
|
|
128
|
+
}): BusinessProcess;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 定义面向业务人员与 agent 阅读的流程板块、阶段和连线,并引用真实可执行声明作为证据。
|
|
3
|
+
* 注册表和 CLI 检查说明与实现关系,图工作台据此展示中文流程;该阅读规格不调度命令、创建审批人、授予权限或自动产生事件关系。
|
|
4
|
+
*/
|
|
5
|
+
// 业务流程是可检查的阅读规格。它引用已有声明,不调度命令,也不授予执行权限。
|
|
6
|
+
import { BUSINESS_PROCESS, INBOUND, OUTBOUND, brandOf } from '../brands.js';
|
|
7
|
+
import { declError } from '../errors.js';
|
|
8
|
+
/**
|
|
9
|
+
* 校验流程阅读规格的标识、说明、阶段引用、连线和结束节点,再冻结所有容器。
|
|
10
|
+
* 这里只证明规格内部引用成立;阶段间真实事件/命令关系由 CLI 图规则继续验证。
|
|
11
|
+
* 不执行 action,也不因 actor 文案自动授予权限或选择审批人。
|
|
12
|
+
*/
|
|
13
|
+
export function businessProcess(name, def) {
|
|
14
|
+
const invalid = (reason) => { throw declError('CAGE-BIZ-001', `${name}:${reason}`); };
|
|
15
|
+
const identifier = (value) => /^[a-z][a-z0-9-]*$/.test(value);
|
|
16
|
+
const text = (value) => typeof value === 'string' && value.trim().length > 0;
|
|
17
|
+
if (!identifier(name) || !identifier(def.area.id))
|
|
18
|
+
invalid('流程与板块标识须使用小写短横线格式');
|
|
19
|
+
if (brandOf(def.subject) !== 'AGGREGATE')
|
|
20
|
+
invalid('主业务对象必须引用实际聚合声明');
|
|
21
|
+
if (![def.title, def.description, def.area.title, def.area.description].every(text))
|
|
22
|
+
invalid('请填写流程及业务板块的名称和说明');
|
|
23
|
+
if (!Object.hasOwn(def.stages, def.entry))
|
|
24
|
+
invalid('入口阶段不存在');
|
|
25
|
+
const stages = {};
|
|
26
|
+
const actionKinds = new Set(['COMMAND', 'EVENT', 'REACTION', 'QUERY', 'APPROVAL', 'JOB', 'INBOUND', 'OUTBOUND']);
|
|
27
|
+
for (const [id, stage] of Object.entries(def.stages)) {
|
|
28
|
+
if (!identifier(id) || !text(stage.title) || !text(stage.description))
|
|
29
|
+
invalid(`阶段 ${id} 需要有效标识、名称和说明`);
|
|
30
|
+
if (stage.kind && !['action', 'decision', 'end'].includes(stage.kind))
|
|
31
|
+
invalid(`阶段 ${id} 的类型无效`);
|
|
32
|
+
for (const action of [stage.action, ...(stage.details ?? []), ...(stage.related ?? []).map(r => r.via)]) {
|
|
33
|
+
if (!actionKinds.has(brandOf(action) ?? ''))
|
|
34
|
+
invalid(`阶段 ${id} 必须引用实际业务操作、审批或自动处理声明`);
|
|
35
|
+
}
|
|
36
|
+
for (const relation of stage.related ?? []) {
|
|
37
|
+
if (brandOf(relation.object) !== 'AGGREGATE' || !text(relation.label))
|
|
38
|
+
invalid(`阶段 ${id} 的关联需要业务对象和说明`);
|
|
39
|
+
}
|
|
40
|
+
stages[id] = Object.freeze({ ...stage,
|
|
41
|
+
details: Object.freeze([...(stage.details ?? [])]),
|
|
42
|
+
related: Object.freeze((stage.related ?? []).map(r => Object.freeze({ ...r }))),
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
const seen = new Set();
|
|
46
|
+
for (const edge of def.transitions) {
|
|
47
|
+
if (!Object.hasOwn(stages, edge.from) || !Object.hasOwn(stages, edge.to))
|
|
48
|
+
invalid(`连线 ${edge.from} → ${edge.to} 引用了不存在的阶段`);
|
|
49
|
+
if (!['manual', 'automatic', 'wait'].includes(edge.mode) || !text(edge.label))
|
|
50
|
+
invalid('每条连线需要执行方式和业务说明');
|
|
51
|
+
const key = `${edge.from}>${edge.to}:${edge.mode}:${edge.label}`;
|
|
52
|
+
if (seen.has(key))
|
|
53
|
+
invalid(`重复连线:${edge.from} → ${edge.to}`);
|
|
54
|
+
seen.add(key);
|
|
55
|
+
if (stages[edge.from].kind === 'end')
|
|
56
|
+
invalid(`结束阶段 ${edge.from} 不能有后续连线`);
|
|
57
|
+
}
|
|
58
|
+
return Object.freeze({ ...def, [BUSINESS_PROCESS]: true, name,
|
|
59
|
+
area: Object.freeze({ ...def.area }), stages: Object.freeze(stages),
|
|
60
|
+
transitions: Object.freeze(def.transitions.map(edge => Object.freeze({ ...edge }))), });
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=business-process.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"business-process.js","sourceRoot":"","sources":["../../src/primitives/business-process.ts"],"names":[],"mappings":"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"}
|