@yss123/cage-approval 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +18 -2
- package/dist/bridge.d.ts +6 -0
- package/dist/bridge.js +20 -0
- package/dist/bridge.js.map +1 -0
- package/dist/engine.d.ts +45 -0
- package/dist/engine.js +150 -0
- package/dist/engine.js.map +1 -0
- package/dist/http.d.ts +8 -0
- package/dist/http.js +33 -0
- package/dist/http.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/route.d.ts +74 -0
- package/dist/route.js +116 -0
- package/dist/route.js.map +1 -0
- package/dist/types.d.ts +15 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +41 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 shunshunyue and Cage contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @yss123/cage-approval
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
审批适配接口、桥接与代码内封闭路由(可选)
|
|
4
|
+
|
|
5
|
+
Cage 是通过 TypeScript 声明业务、由框架统一执行权限和数据规则的中后台框架。此包是框架的一部分。
|
|
6
|
+
|
|
7
|
+
## 使用
|
|
8
|
+
|
|
9
|
+
```powershell
|
|
10
|
+
pnpm add @yss123/cage-approval
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
首次使用建议通过脚手架创建完整项目,所需依赖由模板统一配置。需要 Node.js 24 或更新版本。
|
|
14
|
+
|
|
15
|
+
- [完整使用说明](https://github.com/shunshunyue/cage/blob/main/docs/使用说明.md)
|
|
16
|
+
- [框架源码与范例](https://github.com/shunshunyue/cage)
|
|
17
|
+
- [问题反馈](https://github.com/shunshunyue/cage/issues)
|
|
18
|
+
|
|
19
|
+
许可证:[MIT](https://github.com/shunshunyue/cage/blob/main/LICENSE)。
|
package/dist/bridge.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Kernel } from '@yss123/cage-kernel';
|
|
2
|
+
import type { Transaction } from 'kysely';
|
|
3
|
+
import type { ApprovalAdapter, ApprovalOutcome } from './types.js';
|
|
4
|
+
export declare function applyOutcome(k: Kernel, r: ApprovalOutcome, tx?: Transaction<any>): Promise<Record<string, any>>;
|
|
5
|
+
/** 外部引擎(例如独立部署的 Warm-Flow、Flowable 服务):它的结论通过回调进入这里 */
|
|
6
|
+
export declare function bindApproval(adapter: ApprovalAdapter, k: Kernel): void;
|
package/dist/bridge.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export async function applyOutcome(k, r, tx) {
|
|
2
|
+
const binding = k.registry.approvalByProcess(r.process);
|
|
3
|
+
if (!binding)
|
|
4
|
+
throw new Error(`[CAGE-APPR-004] 没有流程 ${r.process} 的审批绑定`);
|
|
5
|
+
const cmd = r.outcome === 'approved' ? binding.a.onApproved : binding.a.onRejected;
|
|
6
|
+
const ctx = k.processCtx({
|
|
7
|
+
process: r.process,
|
|
8
|
+
tenantId: r.tenantId,
|
|
9
|
+
onBehalfOf: r.lastApprover,
|
|
10
|
+
capabilities: [k.registry.capabilityOf(binding.a.onApproved), k.registry.capabilityOf(binding.a.onRejected)],
|
|
11
|
+
});
|
|
12
|
+
return tx ? k.executeIn(tx, cmd, { id: r.businessKey }, ctx, 0) : k.execute(cmd, { id: r.businessKey }, ctx);
|
|
13
|
+
}
|
|
14
|
+
/** 外部引擎(例如独立部署的 Warm-Flow、Flowable 服务):它的结论通过回调进入这里 */
|
|
15
|
+
export function bindApproval(adapter, k) {
|
|
16
|
+
adapter.onCompleted(async (r) => {
|
|
17
|
+
await applyOutcome(k, r);
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=bridge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge.js","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAKA,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,CAAS,EAAE,CAAkB,EAAE,EAAqB;IACrF,MAAM,OAAO,GAAG,CAAC,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IACxD,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC,OAAO,QAAQ,CAAC,CAAC;IACzE,MAAM,GAAG,GAAG,CAAC,CAAC,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC;IACnF,MAAM,GAAG,GAAG,CAAC,CAAC,UAAU,CAAC;QACvB,OAAO,EAAE,CAAC,CAAC,OAAO;QAClB,QAAQ,EAAE,CAAC,CAAC,QAAQ;QACpB,UAAU,EAAE,CAAC,CAAC,YAAY;QAC1B,YAAY,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;KAC7G,CAAC,CAAC;IACH,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,WAAW,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,WAAW,EAAE,EAAE,GAAG,CAAC,CAAC;AAC/G,CAAC;AAED,uDAAuD;AACvD,MAAM,UAAU,YAAY,CAAC,OAAwB,EAAE,CAAS;IAC9D,OAAO,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE;QAC9B,MAAM,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC3B,CAAC,CAAC,CAAC;AACL,CAAC"}
|
package/dist/engine.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { type Kernel } from '@yss123/cage-kernel';
|
|
2
|
+
import { type ApprovalEngine } from '@yss123/cage-kernel/worker';
|
|
3
|
+
import { type Transaction } from 'kysely';
|
|
4
|
+
import { type Route } from './route.js';
|
|
5
|
+
export interface TaskView {
|
|
6
|
+
id: string;
|
|
7
|
+
instanceId: string;
|
|
8
|
+
process: string;
|
|
9
|
+
aggregate: string;
|
|
10
|
+
businessKey: string;
|
|
11
|
+
step: number;
|
|
12
|
+
title: string;
|
|
13
|
+
status: string;
|
|
14
|
+
vars: Record<string, unknown>;
|
|
15
|
+
createdAt: string;
|
|
16
|
+
}
|
|
17
|
+
export declare class InCodeEngine implements ApprovalEngine {
|
|
18
|
+
private readonly k;
|
|
19
|
+
readonly route: Route;
|
|
20
|
+
constructor(k: Kernel, route: Route);
|
|
21
|
+
private approversOf;
|
|
22
|
+
private openStep;
|
|
23
|
+
start(input: {
|
|
24
|
+
process: string;
|
|
25
|
+
businessKey: string;
|
|
26
|
+
tenantId: string;
|
|
27
|
+
vars: Record<string, unknown>;
|
|
28
|
+
tx: Transaction<any>;
|
|
29
|
+
}): Promise<{
|
|
30
|
+
instanceId: string;
|
|
31
|
+
}>;
|
|
32
|
+
cancel(input: {
|
|
33
|
+
process: string;
|
|
34
|
+
businessKey: string;
|
|
35
|
+
tenantId: string;
|
|
36
|
+
tx: Transaction<any>;
|
|
37
|
+
}): Promise<void>;
|
|
38
|
+
/** 我的待办 */
|
|
39
|
+
tasksOf(userId: string, tenantId: string, status?: string): Promise<TaskView[]>;
|
|
40
|
+
/** 审批人做出决定;流程结束时在同一事务里执行结论命令 */
|
|
41
|
+
decide(taskId: string, userId: string, tenantId: string, outcome: 'approved' | 'rejected', comment?: string): Promise<{
|
|
42
|
+
finished: boolean;
|
|
43
|
+
outcome?: string;
|
|
44
|
+
}>;
|
|
45
|
+
}
|
package/dist/engine.js
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
// 代码内封闭路由的引擎:流程实例和待办存在 flow 模式;结论通过桥接以流程身份调用命令。
|
|
2
|
+
// 审批人做出最后一个决定时,决定和结论命令在同一个事务里提交:命令被拒(例如超出额度)时决定也不生效。
|
|
3
|
+
import { cageError } from '@yss123/cage-kernel';
|
|
4
|
+
import { sql } from 'kysely';
|
|
5
|
+
import { applyOutcome } from './bridge.js';
|
|
6
|
+
import { resolveSteps } from './route.js';
|
|
7
|
+
export class InCodeEngine {
|
|
8
|
+
k;
|
|
9
|
+
route;
|
|
10
|
+
constructor(k, route) {
|
|
11
|
+
this.k = k;
|
|
12
|
+
this.route = route;
|
|
13
|
+
}
|
|
14
|
+
async approversOf(tx, a, vars, tenantId) {
|
|
15
|
+
switch (a.kind) {
|
|
16
|
+
case 'user':
|
|
17
|
+
return [a.userId];
|
|
18
|
+
case 'deptLeader': {
|
|
19
|
+
const deptId = vars[a.field];
|
|
20
|
+
if (typeof deptId !== 'string')
|
|
21
|
+
return [];
|
|
22
|
+
// 本部门没有负责人时沿祖先路径向上找
|
|
23
|
+
const d = await tx.selectFrom('sys.dept').select(['leader_id', 'ancestors']).where('id', '=', deptId).where('tenant_id', '=', tenantId).executeTakeFirst();
|
|
24
|
+
if (!d)
|
|
25
|
+
return [];
|
|
26
|
+
if (d.leader_id)
|
|
27
|
+
return [d.leader_id];
|
|
28
|
+
const ids = String(d.ancestors).split('/').filter(Boolean).reverse();
|
|
29
|
+
for (const id of ids) {
|
|
30
|
+
const p = await tx.selectFrom('sys.dept').select('leader_id').where('id', '=', id).executeTakeFirst();
|
|
31
|
+
if (p?.leader_id)
|
|
32
|
+
return [p.leader_id];
|
|
33
|
+
}
|
|
34
|
+
return [];
|
|
35
|
+
}
|
|
36
|
+
case 'role': {
|
|
37
|
+
const role = await tx.selectFrom('sys.role').select('id').where('code', '=', a.code).where('tenant_id', '=', tenantId).where('status', '=', 'active').executeTakeFirst();
|
|
38
|
+
if (!role)
|
|
39
|
+
return [];
|
|
40
|
+
const users = await tx
|
|
41
|
+
.selectFrom('sys.user')
|
|
42
|
+
.select('id')
|
|
43
|
+
.where('tenant_id', '=', tenantId)
|
|
44
|
+
.where('status', '=', 'active')
|
|
45
|
+
.where(sql `role_ids @> ${JSON.stringify([role.id])}::jsonb`)
|
|
46
|
+
.execute();
|
|
47
|
+
return users.map((u) => u.id);
|
|
48
|
+
}
|
|
49
|
+
default:
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
async openStep(tx, instanceId, tenantId, steps, index, vars) {
|
|
54
|
+
const s = steps[index];
|
|
55
|
+
const who = await this.approversOf(tx, s.approver, vars, tenantId);
|
|
56
|
+
if (who.length === 0) {
|
|
57
|
+
throw new Error(`[CAGE-APPR-007] 流程 ${this.route.process} 第 ${index + 1} 步「${s.title}」找不到审批人,请在 /admin 配置部门负责人或角色`);
|
|
58
|
+
}
|
|
59
|
+
for (const assignee of new Set(who)) {
|
|
60
|
+
await tx.insertInto('flow.approval_task').values({ instance_id: instanceId, tenant_id: tenantId, step: index, title: s.title, assignee }).execute();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
async start(input) {
|
|
64
|
+
const { tx } = input;
|
|
65
|
+
const binding = this.k.registry.approvalByProcess(input.process);
|
|
66
|
+
const steps = resolveSteps(this.route.nodes, input.vars);
|
|
67
|
+
// 同一张单重新提交时,关闭旧的流程实例
|
|
68
|
+
await tx.updateTable('flow.approval_instance').set({ status: 'superseded', updated_at: sql `now()` })
|
|
69
|
+
.where('tenant_id', '=', input.tenantId).where('business_key', '=', input.businessKey).where('status', '=', 'running').execute();
|
|
70
|
+
const inst = await tx
|
|
71
|
+
.insertInto('flow.approval_instance')
|
|
72
|
+
.values({
|
|
73
|
+
tenant_id: input.tenantId, process: input.process, aggregate: binding?.a.agg.name ?? '', business_key: input.businessKey,
|
|
74
|
+
vars: JSON.stringify(input.vars), steps: JSON.stringify(steps),
|
|
75
|
+
})
|
|
76
|
+
.returning('id')
|
|
77
|
+
.executeTakeFirstOrThrow();
|
|
78
|
+
await this.openStep(tx, inst.id, input.tenantId, steps, 0, input.vars);
|
|
79
|
+
return { instanceId: inst.id };
|
|
80
|
+
}
|
|
81
|
+
async cancel(input) {
|
|
82
|
+
const insts = await input.tx.selectFrom('flow.approval_instance').select('id')
|
|
83
|
+
.where('tenant_id', '=', input.tenantId).where('business_key', '=', input.businessKey).where('status', '=', 'running').execute();
|
|
84
|
+
for (const i of insts) {
|
|
85
|
+
await input.tx.updateTable('flow.approval_instance').set({ status: 'cancelled', updated_at: sql `now()` }).where('id', '=', i.id).execute();
|
|
86
|
+
await input.tx.updateTable('flow.approval_task').set({ status: 'cancelled' }).where('instance_id', '=', i.id).where('status', '=', 'pending').execute();
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/** 我的待办 */
|
|
90
|
+
async tasksOf(userId, tenantId, status = 'pending') {
|
|
91
|
+
const rows = await this.k.app
|
|
92
|
+
.selectFrom('flow.approval_task as t')
|
|
93
|
+
.innerJoin('flow.approval_instance as i', 'i.id', 't.instance_id')
|
|
94
|
+
.select(['t.id', 't.instance_id', 'i.process', 'i.aggregate', 'i.business_key', 't.step', 't.title', 't.status', 'i.vars', 't.created_at'])
|
|
95
|
+
.where('t.assignee', '=', userId)
|
|
96
|
+
.where('t.tenant_id', '=', tenantId)
|
|
97
|
+
.where('t.status', '=', status)
|
|
98
|
+
.where('i.process', '=', this.route.process)
|
|
99
|
+
.orderBy('t.created_at', 'desc')
|
|
100
|
+
.limit(200)
|
|
101
|
+
.execute();
|
|
102
|
+
return rows.map((r) => ({
|
|
103
|
+
id: r.id, instanceId: r.instance_id, process: r.process, aggregate: r.aggregate, businessKey: r.business_key,
|
|
104
|
+
step: r.step, title: r.title, status: r.status, vars: r.vars, createdAt: new Date(r.created_at).toISOString(),
|
|
105
|
+
}));
|
|
106
|
+
}
|
|
107
|
+
/** 审批人做出决定;流程结束时在同一事务里执行结论命令 */
|
|
108
|
+
async decide(taskId, userId, tenantId, outcome, comment) {
|
|
109
|
+
return this.k.app.transaction().execute(async (tx) => {
|
|
110
|
+
const task = await tx.selectFrom('flow.approval_task').selectAll()
|
|
111
|
+
.where('id', '=', taskId).where('tenant_id', '=', tenantId).forUpdate().executeTakeFirst();
|
|
112
|
+
// 不是自己的待办一律 404,不泄露存在性
|
|
113
|
+
if (!task || task.assignee !== userId)
|
|
114
|
+
throw cageError('NOT_FOUND');
|
|
115
|
+
if (task.status !== 'pending')
|
|
116
|
+
throw cageError('INVALID_TRANSITION', { status: task.status });
|
|
117
|
+
const inst = await tx.selectFrom('flow.approval_instance').selectAll().where('id', '=', task.instance_id).forUpdate().executeTakeFirstOrThrow();
|
|
118
|
+
if (inst.status !== 'running')
|
|
119
|
+
throw cageError('INVALID_TRANSITION', { status: inst.status });
|
|
120
|
+
await tx.updateTable('flow.approval_task').set({ status: outcome, comment: comment ?? null, decided_at: sql `now()` }).where('id', '=', taskId).execute();
|
|
121
|
+
const steps = inst.steps;
|
|
122
|
+
const finish = async (result) => {
|
|
123
|
+
await tx.updateTable('flow.approval_task').set({ status: 'skipped' }).where('instance_id', '=', inst.id).where('status', '=', 'pending').execute();
|
|
124
|
+
await tx.updateTable('flow.approval_instance').set({ status: result, last_approver: userId, updated_at: sql `now()` }).where('id', '=', inst.id).execute();
|
|
125
|
+
await applyOutcome(this.k, { process: inst.process, tenantId, businessKey: inst.business_key, outcome: result, lastApprover: userId }, tx);
|
|
126
|
+
return { finished: true, outcome: result };
|
|
127
|
+
};
|
|
128
|
+
if (outcome === 'rejected')
|
|
129
|
+
return finish('rejected');
|
|
130
|
+
const s = steps[task.step];
|
|
131
|
+
if (s.mode === 'all') {
|
|
132
|
+
const left = await tx.selectFrom('flow.approval_task').select('id')
|
|
133
|
+
.where('instance_id', '=', inst.id).where('step', '=', task.step).where('status', '=', 'pending').execute();
|
|
134
|
+
if (left.length > 0)
|
|
135
|
+
return { finished: false };
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
await tx.updateTable('flow.approval_task').set({ status: 'skipped' })
|
|
139
|
+
.where('instance_id', '=', inst.id).where('step', '=', task.step).where('status', '=', 'pending').execute();
|
|
140
|
+
}
|
|
141
|
+
const nextIndex = task.step + 1;
|
|
142
|
+
if (nextIndex >= steps.length)
|
|
143
|
+
return finish('approved');
|
|
144
|
+
await tx.updateTable('flow.approval_instance').set({ current_step: nextIndex, last_approver: userId, updated_at: sql `now()` }).where('id', '=', inst.id).execute();
|
|
145
|
+
await this.openStep(tx, inst.id, tenantId, steps, nextIndex, inst.vars);
|
|
146
|
+
return { finished: false };
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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"}
|
package/dist/http.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type Ctx, type Kernel } from '@yss123/cage-kernel';
|
|
2
|
+
import type { FastifyInstance } from 'fastify';
|
|
3
|
+
import type { InCodeEngine } from './engine.js';
|
|
4
|
+
export declare function approvalRoutes(app: FastifyInstance, opts: {
|
|
5
|
+
kernel: Kernel;
|
|
6
|
+
engines: InCodeEngine[];
|
|
7
|
+
ctxOf: (req: any) => Ctx;
|
|
8
|
+
}): void;
|
package/dist/http.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// 待办接口:GET /api/approval/tasks、POST /api/approval/tasks/:id/approve|reject
|
|
2
|
+
import { cageError } from '@yss123/cage-kernel';
|
|
3
|
+
export function approvalRoutes(app, opts) {
|
|
4
|
+
const user = (req) => {
|
|
5
|
+
const ctx = opts.ctxOf(req);
|
|
6
|
+
if (!ctx.actor || ctx.actor.kind !== 'user' || !ctx.userId)
|
|
7
|
+
throw cageError('UNAUTHENTICATED');
|
|
8
|
+
return ctx;
|
|
9
|
+
};
|
|
10
|
+
app.get('/api/approval/tasks', async (req) => {
|
|
11
|
+
const ctx = user(req);
|
|
12
|
+
const status = req.query?.status ?? 'pending';
|
|
13
|
+
const lists = await Promise.all(opts.engines.map((e) => e.tasksOf(ctx.userId, ctx.tenantId, status)));
|
|
14
|
+
return lists.flat().sort((a, b) => b.createdAt.localeCompare(a.createdAt));
|
|
15
|
+
});
|
|
16
|
+
for (const outcome of ['approve', 'reject']) {
|
|
17
|
+
app.post(`/api/approval/tasks/:id/${outcome}`, async (req) => {
|
|
18
|
+
const ctx = user(req);
|
|
19
|
+
const id = req.params.id;
|
|
20
|
+
if (!/^[0-9a-f-]{36}$/i.test(id))
|
|
21
|
+
throw cageError('NOT_FOUND');
|
|
22
|
+
const comment = typeof req.body?.comment === 'string' ? String(req.body.comment).slice(0, 1000) : undefined;
|
|
23
|
+
for (const e of opts.engines) {
|
|
24
|
+
const owns = await opts.kernel.app.selectFrom('flow.approval_task as t').innerJoin('flow.approval_instance as i', 'i.id', 't.instance_id')
|
|
25
|
+
.select('t.id').where('t.id', '=', id).where('i.process', '=', e.route.process).executeTakeFirst();
|
|
26
|
+
if (owns)
|
|
27
|
+
return e.decide(id, ctx.userId, ctx.tenantId, outcome === 'approve' ? 'approved' : 'rejected', comment);
|
|
28
|
+
}
|
|
29
|
+
throw cageError('NOT_FOUND');
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=http.js.map
|
package/dist/http.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,OAAO,EAAE,SAAS,EAAyB,MAAM,qBAAqB,CAAC;AAIvE,MAAM,UAAU,cAAc,CAAC,GAAoB,EAAE,IAA2E;IAC9H,MAAM,IAAI,GAAG,CAAC,GAAQ,EAAE,EAAE;QACxB,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC5B,IAAI,CAAC,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM;YAAE,MAAM,SAAS,CAAC,iBAAiB,CAAC,CAAC;QAC/F,OAAO,GAAG,CAAC;IACb,CAAC,CAAC;IACF,GAAG,CAAC,GAAG,CAAC,qBAAqB,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QAC3C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;QACtB,MAAM,MAAM,GAAI,GAAG,CAAC,KAAa,EAAE,MAAM,IAAI,SAAS,CAAC;QACvD,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,MAAO,EAAE,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;QACvG,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;IAC7E,CAAC,CAAC,CAAC;IACH,KAAK,MAAM,OAAO,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAU,EAAE,CAAC;QACrD,GAAG,CAAC,IAAI,CAAC,2BAA2B,OAAO,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YAC3D,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;YACtB,MAAM,EAAE,GAAI,GAAG,CAAC,MAAc,CAAC,EAAY,CAAC;YAC5C,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,EAAE,CAAC;gBAAE,MAAM,SAAS,CAAC,WAAW,CAAC,CAAC;YAC/D,MAAM,OAAO,GAAG,OAAQ,GAAG,CAAC,IAAY,EAAE,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAE,GAAG,CAAC,IAAY,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YAC9H,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;gBAC7B,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,yBAAyB,CAAC,CAAC,SAAS,CAAC,6BAA6B,EAAE,MAAM,EAAE,eAAe,CAAC;qBACvI,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,GAAG,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,gBAAgB,EAAE,CAAC;gBACrG,IAAI,IAAI;oBAAE,OAAO,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,GAAG,CAAC,MAAO,EAAE,GAAG,CAAC,QAAQ,EAAE,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;YACrH,CAAC;YACD,MAAM,SAAS,CAAC,WAAW,CAAC,CAAC;QAC/B,CAAC,CAAC,CAAC;IACL,CAAC;AACH,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export * from './types.js';
|
|
2
|
+
export { applyOutcome, bindApproval } from './bridge.js';
|
|
3
|
+
export { route, step, when, approver, gt, gte, lt, lte, eq, ne, isIn, evalCondition, resolveSteps, FLOW_DDL, type Route, type RouteNode, type Step, type When, type Approver, type Condition, } from './route.js';
|
|
4
|
+
export { InCodeEngine, type TaskView } from './engine.js';
|
|
5
|
+
export { approvalRoutes } from './http.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// @yss123/cage-approval(可选):审批适配接口、桥接与代码内封闭路由。
|
|
2
|
+
export * from './types.js';
|
|
3
|
+
export { applyOutcome, bindApproval } from './bridge.js';
|
|
4
|
+
export { route, step, when, approver, gt, gte, lt, lte, eq, ne, isIn, evalCondition, resolveSteps, FLOW_DDL, } from './route.js';
|
|
5
|
+
export { InCodeEngine } from './engine.js';
|
|
6
|
+
export { approvalRoutes } from './http.js';
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,+CAA+C;AAC/C,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EACL,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,YAAY,EAAE,QAAQ,GAEnG,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,YAAY,EAAiB,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC"}
|
package/dist/route.d.ts
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { ROUTE, SYSTEM_DDL, type Kernel } from '@yss123/cage-kernel';
|
|
2
|
+
import { InCodeEngine } from './engine.js';
|
|
3
|
+
export type Approver = {
|
|
4
|
+
kind: 'deptLeader';
|
|
5
|
+
field: string;
|
|
6
|
+
} | {
|
|
7
|
+
kind: 'role';
|
|
8
|
+
code: string;
|
|
9
|
+
} | {
|
|
10
|
+
kind: 'user';
|
|
11
|
+
userId: string;
|
|
12
|
+
};
|
|
13
|
+
/** 审批人组合子 */
|
|
14
|
+
export declare const approver: {
|
|
15
|
+
/** 单据某个部门字段对应部门的负责人 */
|
|
16
|
+
deptLeader: (field: string) => Approver;
|
|
17
|
+
/** 拥有某角色的所有用户 */
|
|
18
|
+
role: (code: string) => Approver;
|
|
19
|
+
/** 指定用户 */
|
|
20
|
+
user: (userId: string) => Approver;
|
|
21
|
+
};
|
|
22
|
+
export type Condition = {
|
|
23
|
+
op: 'gt' | 'gte' | 'lt' | 'lte';
|
|
24
|
+
field: string;
|
|
25
|
+
value: number;
|
|
26
|
+
} | {
|
|
27
|
+
op: 'eq' | 'ne';
|
|
28
|
+
field: string;
|
|
29
|
+
value: string | number | boolean;
|
|
30
|
+
} | {
|
|
31
|
+
op: 'in';
|
|
32
|
+
field: string;
|
|
33
|
+
value: (string | number)[];
|
|
34
|
+
};
|
|
35
|
+
export declare const gt: (field: string, value: number) => Condition;
|
|
36
|
+
export declare const gte: (field: string, value: number) => Condition;
|
|
37
|
+
export declare const lt: (field: string, value: number) => Condition;
|
|
38
|
+
export declare const lte: (field: string, value: number) => Condition;
|
|
39
|
+
export declare const eq: (field: string, value: string | number | boolean) => Condition;
|
|
40
|
+
export declare const ne: (field: string, value: string | number | boolean) => Condition;
|
|
41
|
+
export declare const isIn: (field: string, value: (string | number)[]) => Condition;
|
|
42
|
+
export interface Step {
|
|
43
|
+
kind: 'step';
|
|
44
|
+
title: string;
|
|
45
|
+
approver: Approver;
|
|
46
|
+
/** any:任意一人通过即可(或签);all:全部通过(会签) */
|
|
47
|
+
mode: 'any' | 'all';
|
|
48
|
+
}
|
|
49
|
+
export interface When {
|
|
50
|
+
kind: 'when';
|
|
51
|
+
cond: Condition;
|
|
52
|
+
steps: Step[];
|
|
53
|
+
}
|
|
54
|
+
export type RouteNode = Step | When;
|
|
55
|
+
export declare function step(title: string, who: Approver, opts?: {
|
|
56
|
+
mode?: 'any' | 'all';
|
|
57
|
+
}): Step;
|
|
58
|
+
export declare function when(cond: Condition, ...steps: Step[]): When;
|
|
59
|
+
export declare function evalCondition(c: Condition, vars: Record<string, unknown>): boolean;
|
|
60
|
+
/** 按流程变量展开出实际要走的步骤 */
|
|
61
|
+
export declare function resolveSteps(nodes: readonly RouteNode[], vars: Record<string, unknown>): Step[];
|
|
62
|
+
export interface Route {
|
|
63
|
+
readonly [ROUTE]: true;
|
|
64
|
+
readonly process: string;
|
|
65
|
+
readonly nodes: readonly RouteNode[];
|
|
66
|
+
/** 路由条件和审批人用到的字段(进入本体图的 uses 边) */
|
|
67
|
+
readonly fieldsUsed: readonly string[];
|
|
68
|
+
/** 代码内引擎(worker 与 HTTP 层使用) */
|
|
69
|
+
readonly engine: (k: Kernel) => InCodeEngine;
|
|
70
|
+
readonly [SYSTEM_DDL]: () => string;
|
|
71
|
+
}
|
|
72
|
+
export declare function route(process: string, nodes: RouteNode[]): Route;
|
|
73
|
+
/** 代码内引擎自己的表,放在 flow 模式;引擎没有业务表的写权限 */
|
|
74
|
+
export declare const FLOW_DDL = "\nCREATE TABLE IF NOT EXISTS flow.approval_instance (\n id uuid PRIMARY KEY DEFAULT gen_random_uuid(),\n tenant_id uuid NOT NULL,\n process text NOT NULL,\n aggregate text NOT NULL,\n business_key uuid NOT NULL,\n vars jsonb NOT NULL,\n steps jsonb NOT NULL,\n current_step int NOT NULL DEFAULT 0,\n status text NOT NULL DEFAULT 'running',\n last_approver uuid,\n created_at timestamptz NOT NULL DEFAULT now(),\n updated_at timestamptz NOT NULL DEFAULT now()\n);\nCREATE INDEX IF NOT EXISTS approval_instance_key_idx ON flow.approval_instance (tenant_id, business_key, status);\nCREATE TABLE IF NOT EXISTS flow.approval_task (\n id uuid PRIMARY KEY DEFAULT gen_random_uuid(),\n instance_id uuid NOT NULL REFERENCES flow.approval_instance(id),\n tenant_id uuid NOT NULL,\n step int NOT NULL,\n title text NOT NULL,\n assignee uuid NOT NULL,\n status text NOT NULL DEFAULT 'pending',\n comment text,\n decided_at timestamptz,\n created_at timestamptz NOT NULL DEFAULT now()\n);\nCREATE INDEX IF NOT EXISTS approval_task_assignee_idx ON flow.approval_task (tenant_id, assignee, status);\nGRANT SELECT, INSERT, UPDATE ON flow.approval_instance, flow.approval_task TO cage_app;\n";
|
package/dist/route.js
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// 代码内封闭路由:一小组固定的组合子(step、when、gt、approver.*),和范围组合子一样只能从里面选,
|
|
2
|
+
// 所以抽取器能读懂:改 amount 的口径时,影响分析会把这条路由列出来。
|
|
3
|
+
import { ROUTE, SYSTEM_DDL, declError } from '@yss123/cage-kernel';
|
|
4
|
+
import { InCodeEngine } from './engine.js';
|
|
5
|
+
/** 审批人组合子 */
|
|
6
|
+
export const approver = {
|
|
7
|
+
/** 单据某个部门字段对应部门的负责人 */
|
|
8
|
+
deptLeader: (field) => ({ kind: 'deptLeader', field }),
|
|
9
|
+
/** 拥有某角色的所有用户 */
|
|
10
|
+
role: (code) => ({ kind: 'role', code }),
|
|
11
|
+
/** 指定用户 */
|
|
12
|
+
user: (userId) => ({ kind: 'user', userId }),
|
|
13
|
+
};
|
|
14
|
+
export const gt = (field, value) => ({ op: 'gt', field, value });
|
|
15
|
+
export const gte = (field, value) => ({ op: 'gte', field, value });
|
|
16
|
+
export const lt = (field, value) => ({ op: 'lt', field, value });
|
|
17
|
+
export const lte = (field, value) => ({ op: 'lte', field, value });
|
|
18
|
+
export const eq = (field, value) => ({ op: 'eq', field, value });
|
|
19
|
+
export const ne = (field, value) => ({ op: 'ne', field, value });
|
|
20
|
+
export const isIn = (field, value) => ({ op: 'in', field, value });
|
|
21
|
+
export function step(title, who, opts = {}) {
|
|
22
|
+
return { kind: 'step', title, approver: who, mode: opts.mode ?? 'any' };
|
|
23
|
+
}
|
|
24
|
+
export function when(cond, ...steps) {
|
|
25
|
+
return { kind: 'when', cond, steps };
|
|
26
|
+
}
|
|
27
|
+
export function evalCondition(c, vars) {
|
|
28
|
+
const v = vars[c.field];
|
|
29
|
+
switch (c.op) {
|
|
30
|
+
case 'gt': return Number(v) > c.value;
|
|
31
|
+
case 'gte': return Number(v) >= c.value;
|
|
32
|
+
case 'lt': return Number(v) < c.value;
|
|
33
|
+
case 'lte': return Number(v) <= c.value;
|
|
34
|
+
case 'eq': return String(v) === String(c.value);
|
|
35
|
+
case 'ne': return String(v) !== String(c.value);
|
|
36
|
+
case 'in': return c.value.map(String).includes(String(v));
|
|
37
|
+
default: return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/** 按流程变量展开出实际要走的步骤 */
|
|
41
|
+
export function resolveSteps(nodes, vars) {
|
|
42
|
+
const out = [];
|
|
43
|
+
for (const n of nodes) {
|
|
44
|
+
if (n.kind === 'step')
|
|
45
|
+
out.push(n);
|
|
46
|
+
else if (evalCondition(n.cond, vars))
|
|
47
|
+
out.push(...n.steps);
|
|
48
|
+
}
|
|
49
|
+
return out;
|
|
50
|
+
}
|
|
51
|
+
const engines = new WeakMap();
|
|
52
|
+
export function route(process, nodes) {
|
|
53
|
+
if (!/^[a-z][a-z0-9-]*$/.test(process))
|
|
54
|
+
throw declError('CAGE-APPR-005', `流程名 ${process} 必须是小写短横线`);
|
|
55
|
+
if (nodes.length === 0)
|
|
56
|
+
throw declError('CAGE-APPR-006', `流程 ${process} 至少需要一个步骤`);
|
|
57
|
+
const fields = new Set();
|
|
58
|
+
for (const n of nodes) {
|
|
59
|
+
const steps = n.kind === 'step' ? [n] : n.steps;
|
|
60
|
+
if (n.kind === 'when')
|
|
61
|
+
fields.add(n.cond.field);
|
|
62
|
+
for (const s of steps)
|
|
63
|
+
if (s.approver.kind === 'deptLeader')
|
|
64
|
+
fields.add(s.approver.field);
|
|
65
|
+
}
|
|
66
|
+
const r = Object.freeze({
|
|
67
|
+
[ROUTE]: true,
|
|
68
|
+
process,
|
|
69
|
+
nodes: Object.freeze([...nodes]),
|
|
70
|
+
fieldsUsed: Object.freeze([...fields]),
|
|
71
|
+
engine: (k) => {
|
|
72
|
+
let m = engines.get(k);
|
|
73
|
+
if (!m)
|
|
74
|
+
engines.set(k, (m = new Map()));
|
|
75
|
+
let e = m.get(process);
|
|
76
|
+
if (!e)
|
|
77
|
+
m.set(process, (e = new InCodeEngine(k, r)));
|
|
78
|
+
return e;
|
|
79
|
+
},
|
|
80
|
+
[SYSTEM_DDL]: () => FLOW_DDL,
|
|
81
|
+
});
|
|
82
|
+
return r;
|
|
83
|
+
}
|
|
84
|
+
/** 代码内引擎自己的表,放在 flow 模式;引擎没有业务表的写权限 */
|
|
85
|
+
export const FLOW_DDL = `
|
|
86
|
+
CREATE TABLE IF NOT EXISTS flow.approval_instance (
|
|
87
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
88
|
+
tenant_id uuid NOT NULL,
|
|
89
|
+
process text NOT NULL,
|
|
90
|
+
aggregate text NOT NULL,
|
|
91
|
+
business_key uuid NOT NULL,
|
|
92
|
+
vars jsonb NOT NULL,
|
|
93
|
+
steps jsonb NOT NULL,
|
|
94
|
+
current_step int NOT NULL DEFAULT 0,
|
|
95
|
+
status text NOT NULL DEFAULT 'running',
|
|
96
|
+
last_approver uuid,
|
|
97
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
98
|
+
updated_at timestamptz NOT NULL DEFAULT now()
|
|
99
|
+
);
|
|
100
|
+
CREATE INDEX IF NOT EXISTS approval_instance_key_idx ON flow.approval_instance (tenant_id, business_key, status);
|
|
101
|
+
CREATE TABLE IF NOT EXISTS flow.approval_task (
|
|
102
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
103
|
+
instance_id uuid NOT NULL REFERENCES flow.approval_instance(id),
|
|
104
|
+
tenant_id uuid NOT NULL,
|
|
105
|
+
step int NOT NULL,
|
|
106
|
+
title text NOT NULL,
|
|
107
|
+
assignee uuid NOT NULL,
|
|
108
|
+
status text NOT NULL DEFAULT 'pending',
|
|
109
|
+
comment text,
|
|
110
|
+
decided_at timestamptz,
|
|
111
|
+
created_at timestamptz NOT NULL DEFAULT now()
|
|
112
|
+
);
|
|
113
|
+
CREATE INDEX IF NOT EXISTS approval_task_assignee_idx ON flow.approval_task (tenant_id, assignee, status);
|
|
114
|
+
GRANT SELECT, INSERT, UPDATE ON flow.approval_instance, flow.approval_task TO cage_app;
|
|
115
|
+
`;
|
|
116
|
+
//# sourceMappingURL=route.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route.js","sourceRoot":"","sources":["../src/route.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ApprovalEngine } from '@yss123/cage-kernel/worker';
|
|
2
|
+
export interface ApprovalOutcome {
|
|
3
|
+
process: string;
|
|
4
|
+
tenantId: string;
|
|
5
|
+
/** 单据 id */
|
|
6
|
+
businessKey: string;
|
|
7
|
+
outcome: 'approved' | 'rejected';
|
|
8
|
+
/** 最后一位审批人:结论命令以流程身份执行,代表这个人(职责分离、额度按他判断) */
|
|
9
|
+
lastApprover: string;
|
|
10
|
+
}
|
|
11
|
+
export interface ApprovalAdapter extends ApprovalEngine {
|
|
12
|
+
/** 注册流程结束回调 */
|
|
13
|
+
onCompleted(cb: (r: ApprovalOutcome) => Promise<void>): void;
|
|
14
|
+
}
|
|
15
|
+
export type { ApprovalEngine };
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
CHANGED
|
@@ -1,6 +1,44 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yss123/cage-approval",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "审批适配接口、桥接与代码内封闭路由(可选)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"default": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist"
|
|
16
|
+
],
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"fastify": "^5.6.0",
|
|
19
|
+
"kysely": "^0.28.5",
|
|
20
|
+
"zod": "^4.1.5",
|
|
21
|
+
"@yss123/cage-kernel": "0.1.0"
|
|
22
|
+
},
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "git+https://github.com/shunshunyue/cage.git",
|
|
27
|
+
"directory": "packages/approval"
|
|
28
|
+
},
|
|
29
|
+
"homepage": "https://github.com/shunshunyue/cage#readme",
|
|
30
|
+
"bugs": {
|
|
31
|
+
"url": "https://github.com/shunshunyue/cage/issues"
|
|
32
|
+
},
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"access": "public",
|
|
35
|
+
"registry": "https://registry.npmjs.org/"
|
|
36
|
+
},
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=24"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build": "tsc -p tsconfig.json",
|
|
42
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
43
|
+
}
|
|
6
44
|
}
|