@yss123/cage-kernel 0.1.0 → 0.2.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 (46) hide show
  1. package/README.md +2 -0
  2. package/dist/db/ddl.d.ts +8 -1
  3. package/dist/db/ddl.js +75 -7
  4. package/dist/db/ddl.js.map +1 -1
  5. package/dist/db/rows.js +3 -0
  6. package/dist/db/rows.js.map +1 -1
  7. package/dist/errors.js +2 -0
  8. package/dist/errors.js.map +1 -1
  9. package/dist/index.d.ts +2 -1
  10. package/dist/index.js +2 -1
  11. package/dist/index.js.map +1 -1
  12. package/dist/kernel.d.ts +2 -0
  13. package/dist/kernel.js +18 -2
  14. package/dist/kernel.js.map +1 -1
  15. package/dist/mask.d.ts +1 -1
  16. package/dist/mask.js +5 -0
  17. package/dist/mask.js.map +1 -1
  18. package/dist/pipeline/load.js +9 -6
  19. package/dist/pipeline/load.js.map +1 -1
  20. package/dist/primitives/aggregate.js +3 -1
  21. package/dist/primitives/aggregate.js.map +1 -1
  22. package/dist/primitives/event.js +3 -1
  23. package/dist/primitives/event.js.map +1 -1
  24. package/dist/primitives/fields.d.ts +43 -23
  25. package/dist/primitives/fields.js +30 -1
  26. package/dist/primitives/fields.js.map +1 -1
  27. package/dist/read.d.ts +8 -2
  28. package/dist/read.js +66 -19
  29. package/dist/read.js.map +1 -1
  30. package/dist/registry.js +26 -0
  31. package/dist/registry.js.map +1 -1
  32. package/dist/relations.d.ts +14 -0
  33. package/dist/relations.js +58 -0
  34. package/dist/relations.js.map +1 -0
  35. package/dist/transfer/csv.d.ts +8 -0
  36. package/dist/transfer/csv.js +114 -0
  37. package/dist/transfer/csv.js.map +1 -0
  38. package/dist/transfer/index.d.ts +49 -0
  39. package/dist/transfer/index.js +245 -0
  40. package/dist/transfer/index.js.map +1 -0
  41. package/dist/transfer/schema.d.ts +4 -0
  42. package/dist/transfer/schema.js +31 -0
  43. package/dist/transfer/schema.js.map +1 -0
  44. package/dist/worker/index.js +3 -1
  45. package/dist/worker/index.js.map +1 -1
  46. package/package.json +1 -1
package/dist/mask.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"mask.js","sourceRoot":"","sources":["../src/mask.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mask.js","sourceRoot":"","sources":["../src/mask.ts"],"names":[],"mappings":"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"}
@@ -4,6 +4,8 @@ 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';
7
9
  export const tableRef = (agg, alias = 't') => `${agg.table.schema}.${agg.table.name} as ${alias}`;
8
10
  export async function loadScoped(tx, agg, id, ctx, forUpdate) {
9
11
  let q = tx
@@ -14,7 +16,7 @@ export async function loadScoped(tx, agg, id, ctx, forUpdate) {
14
16
  if (forUpdate)
15
17
  q = q.forUpdate();
16
18
  const row = await q.executeTakeFirst();
17
- return row ? rowToSnapshot(agg, row) : null;
19
+ return row ? (await loadRelationIds(tx, agg, [rowToSnapshot(agg, row)], ctx))[0] : null;
18
20
  }
19
21
  const SYSTEM_KEYS = {
20
22
  id: 'id', status: 'status', version: 'version', createdAt: 'created_at', updatedAt: 'updated_at',
@@ -29,8 +31,8 @@ export function whereColumns(agg, where) {
29
31
  if (!col)
30
32
  throw cageError('INVALID_INPUT', { field: k, message: `${agg.name} 没有字段 ${k}` });
31
33
  const f = agg.fields[k];
32
- if (f?.sql === 'jsonb')
33
- throw cageError('INVALID_INPUT', { field: k, message: '明细字段不能作为查询条件' });
34
+ if (f && (f.sql === 'jsonb' || isRelation(f)))
35
+ throw cageError('INVALID_INPUT', { field: k, message: '明细或集合关系不能作为主表查询条件' });
34
36
  out.push([col, v]);
35
37
  }
36
38
  return out;
@@ -42,7 +44,8 @@ export function scopedFind(tx, agg, ctx, where, opts = {}) {
42
44
  }
43
45
  if (opts.orderBy) {
44
46
  const col = SYSTEM_KEYS[opts.orderBy] ?? (opts.orderBy in agg.fields ? snake(opts.orderBy) : undefined);
45
- if (!col)
47
+ const field = agg.fields[opts.orderBy];
48
+ if (!col || (field && isRelation(field)))
46
49
  throw cageError('INVALID_INPUT', { field: opts.orderBy });
47
50
  q = q.orderBy(sql.ref(`t.${col}`), opts.desc ? 'desc' : 'asc');
48
51
  }
@@ -57,10 +60,10 @@ export function bindReads(tx, reads, ctx) {
57
60
  for (const [alias, agg] of Object.entries(reads)) {
58
61
  deps[alias] = Object.freeze({
59
62
  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)),
63
+ find: async (where, opts) => loadRelationIds(tx, agg, (await scopedFind(tx, agg, ctx, where, opts).execute()).map((r) => rowToSnapshot(agg, r)), ctx),
61
64
  findOne: async (where) => {
62
65
  const rows = await scopedFind(tx, agg, ctx, where, { limit: 1 }).execute();
63
- return rows[0] ? rowToSnapshot(agg, rows[0]) : null;
66
+ return rows[0] ? (await loadRelationIds(tx, agg, [rowToSnapshot(agg, rows[0])], ctx))[0] : null;
64
67
  },
65
68
  count: async (where) => {
66
69
  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":"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"}
1
+ {"version":3,"file":"load.js","sourceRoot":"","sources":["../../src/pipeline/load.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,7 @@
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, objectSchema } from './fields.js';
5
5
  import { DEFAULT_STATES, isStates } from './states.js';
6
6
  export const RESERVED_FIELDS = ['id', 'version', 'status', 'createdAt', 'updatedAt', 'tenantId'];
7
7
  export function aggregate(name, def) {
@@ -35,6 +35,8 @@ export function aggregate(name, def) {
35
35
  for (const f of [sc.deptField, ownerField]) {
36
36
  if (f && f !== 'id' && !(f in fields))
37
37
  throw declError('CAGE-SCOPE-002', `${name} 的范围字段 ${f} 不存在`);
38
+ if (f && f !== 'id' && isRelation(fields[f]))
39
+ throw declError('CAGE-REL-001', `${name} 的范围字段必须是主表列,不能使用集合关系 ${f}`);
38
40
  }
39
41
  const scope = Object.freeze({ ...sc, ownerField });
40
42
  return Object.freeze({
@@ -1 +1 @@
1
- {"version":3,"file":"aggregate.js","sourceRoot":"","sources":["../../src/primitives/aggregate.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"aggregate.js","sourceRoot":"","sources":["../../src/primitives/aggregate.ts"],"names":[],"mappings":"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"}
@@ -1,9 +1,11 @@
1
1
  import { EVENT } from '../brands.js';
2
2
  import { declError } from '../errors.js';
3
- import { objectSchema } from './fields.js';
3
+ import { isRelation, objectSchema } from './fields.js';
4
4
  export function event(name, fields, opts) {
5
5
  if (!/^[A-Z][A-Za-z0-9]*$/.test(name))
6
6
  throw declError('CAGE-NAME-003', `事件名 ${name} 必须是大驼峰`);
7
+ if (Object.values(fields).some(isRelation))
8
+ throw declError('CAGE-REL-001', '事件载荷不声明持久化关系;传递 ID 数组请使用 list(id())');
7
9
  return Object.freeze({
8
10
  [EVENT]: true,
9
11
  name,
@@ -1 +1 @@
1
- {"version":3,"file":"event.js","sourceRoot":"","sources":["../../src/primitives/event.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAiC,YAAY,EAAE,MAAM,aAAa,CAAC;AAa1E,MAAM,UAAU,KAAK,CAA2C,IAAO,EAAE,MAAS,EAAE,IAA+B;IACjH,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,MAAM,SAAS,CAAC,eAAe,EAAE,OAAO,IAAI,SAAS,CAAC,CAAC;IAC9F,OAAO,MAAM,CAAC,MAAM,CAAC;QACnB,CAAC,KAAK,CAAC,EAAE,IAAa;QACtB,IAAI;QACJ,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,MAAM,EAAE,CAAC;QACpC,MAAM,EAAE,YAAY,CAAC,MAAM,CAAC;QAC5B,WAAW,EAAE,IAAI,EAAE,WAAW;KAC/B,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,CAAU;IAChC,OAAO,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAK,CAAS,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC;AACpE,CAAC;AASD,MAAM,UAAU,IAAI,CAAqB,EAAK,EAAE,GAA+D;IAC7G,OAAO,MAAM,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,CAAsB;IAC/C,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;AACvC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,EAAY,EAAE,QAAiC,EAAE,KAA8B;IAC5G,MAAM,GAAG,GAA4B,EAAE,CAAC;IACxC,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC;QACvC,IAAI,CAAC,IAAI,QAAQ;YAAE,GAAG,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;aACnC,IAAI,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC;YAAE,GAAG,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC;aAC3C,IAAI,KAAK,IAAI,CAAC,IAAI,KAAK;YAAE,GAAG,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAClD,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
1
+ {"version":3,"file":"event.js","sourceRoot":"","sources":["../../src/primitives/event.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAiC,UAAU,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAatF,MAAM,UAAU,KAAK,CAA2C,IAAO,EAAE,MAAS,EAAE,IAA+B;IACjH,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,MAAM,SAAS,CAAC,eAAe,EAAE,OAAO,IAAI,SAAS,CAAC,CAAC;IAC9F,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC;QAAE,MAAM,SAAS,CAAC,cAAc,EAAE,qCAAqC,CAAC,CAAC;IACnH,OAAO,MAAM,CAAC,MAAM,CAAC;QACnB,CAAC,KAAK,CAAC,EAAE,IAAa;QACtB,IAAI;QACJ,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,MAAM,EAAE,CAAC;QACpC,MAAM,EAAE,YAAY,CAAC,MAAM,CAAC;QAC5B,WAAW,EAAE,IAAI,EAAE,WAAW;KAC/B,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,CAAU;IAChC,OAAO,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAK,CAAS,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC;AACpE,CAAC;AASD,MAAM,UAAU,IAAI,CAAqB,EAAK,EAAE,GAA+D;IAC7G,OAAO,MAAM,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,CAAsB;IAC/C,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;AACvC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,EAAY,EAAE,QAAiC,EAAE,KAA8B;IAC5G,MAAM,GAAG,GAA4B,EAAE,CAAC;IACxC,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC;QACvC,IAAI,CAAC,IAAI,QAAQ;YAAE,GAAG,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;aACnC,IAAI,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC;YAAE,GAAG,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC;aAC3C,IAAI,KAAK,IAAI,CAAC,IAAI,KAAK;YAAE,GAAG,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAClD,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -2,7 +2,7 @@ import { z } from 'zod';
2
2
  /** 敏感种类是封闭集合,每种在内核里有一个掩码函数(见 db/ddl.ts 的 sys.mask)。 */
3
3
  export type SensitiveKind = 'phone' | 'idCard' | 'bankCard' | 'email' | 'name' | 'address';
4
4
  export declare const SENSITIVE_KINDS: readonly SensitiveKind[];
5
- export type FieldKind = 'id' | 'ref' | 'text' | 'int' | 'number' | 'money' | 'date' | 'datetime' | 'bool' | 'enum' | 'list';
5
+ export type FieldKind = 'id' | 'ref' | 'text' | 'int' | 'number' | 'money' | 'date' | 'datetime' | 'bool' | 'enum' | 'list' | 'hasMany' | 'manyToMany';
6
6
  export interface FieldMeta {
7
7
  readonly kind: FieldKind;
8
8
  readonly sql: string;
@@ -10,6 +10,8 @@ export interface FieldMeta {
10
10
  readonly sensitiveKind?: SensitiveKind;
11
11
  /** 引用的聚合名,抽取器据此画引用边,生成外键 */
12
12
  readonly ref?: string;
13
+ /** hasMany 的反向外键。关系字段不占主表列,sql 为空字符串。 */
14
+ readonly mappedBy?: string;
13
15
  readonly isUnique?: boolean;
14
16
  /** 隐藏字段:不进入安全视图、审计、事件、前端契约(例如密码哈希) */
15
17
  readonly isHidden?: boolean;
@@ -26,12 +28,13 @@ export interface FieldMeta {
26
28
  * 字段定义。方法都返回新对象(不可变),可以链式调用:
27
29
  * text(50).sensitive('phone').optional()
28
30
  */
29
- export declare class Field<T extends z.ZodType = z.ZodType, Opt extends boolean = boolean> implements FieldMeta {
30
- readonly kind: FieldKind;
31
+ export declare class Field<T extends z.ZodType = z.ZodType, Opt extends boolean = boolean, K extends FieldKind = FieldKind> implements FieldMeta {
32
+ readonly kind: K;
31
33
  readonly sql: string;
32
34
  readonly isOptional: boolean;
33
35
  readonly sensitiveKind?: SensitiveKind;
34
36
  readonly ref?: string;
37
+ readonly mappedBy?: string;
35
38
  readonly isUnique?: boolean;
36
39
  readonly isHidden?: boolean;
37
40
  readonly description?: string;
@@ -42,53 +45,70 @@ export declare class Field<T extends z.ZodType = z.ZodType, Opt extends boolean
42
45
  readonly base: T;
43
46
  /** 仅用于类型推导 */
44
47
  readonly __opt?: Opt;
45
- constructor(base: T, meta: Omit<FieldMeta, 'isOptional'> & {
48
+ constructor(base: T, meta: Omit<FieldMeta, 'kind' | 'isOptional'> & {
49
+ kind: K;
46
50
  isOptional?: boolean;
47
51
  });
48
- meta(): FieldMeta;
52
+ meta(): FieldMeta & {
53
+ kind: K;
54
+ };
49
55
  private with;
50
56
  /** 校验规则(前后端共用);可选字段允许 null 与缺省 */
51
57
  get zod(): z.ZodType;
52
58
  /** 可选字段:列可空,输入可以不填 */
53
- optional(): Field<T, true>;
59
+ optional(): Field<T, true, K>;
54
60
  /** 敏感字段:读侧按种类掩码,持有 聚合.unmask.种类 能力才能看明文 */
55
- sensitive(kind: SensitiveKind): Field<T, Opt>;
61
+ sensitive(kind: SensitiveKind): Field<T, Opt, K>;
56
62
  /** 唯一约束(租户内) */
57
- unique(): Field<T, Opt>;
63
+ unique(): Field<T, Opt, K>;
58
64
  /** 隐藏字段:不出现在读侧、审计、事件和前端契约 */
59
- hidden(): Field<T, Opt>;
65
+ hidden(): Field<T, Opt, K>;
60
66
  /** 口径说明 */
61
- describe(text: string): Field<T, Opt>;
67
+ describe(text: string): Field<T, Opt, K>;
62
68
  }
63
69
  export type AnyField = Field<z.ZodType, boolean>;
64
70
  export type Fields = Record<string, AnyField>;
65
71
  /** 字段的 TypeScript 值类型 */
66
- export type ValueOf<F> = F extends Field<infer T, infer O> ? ([O] extends [true] ? z.output<T> | null : z.output<T>) : never;
72
+ export type ValueOf<F> = F extends Field<infer T, infer O, any> ? ([O] extends [true] ? z.output<T> | null : z.output<T>) : never;
67
73
  export type FieldValues<F extends Fields> = {
68
- -readonly [K in keyof F]: ValueOf<F[K]>;
74
+ -readonly [K in keyof F as F[K]['kind'] extends 'hasMany' ? never : K]: ValueOf<F[K]>;
69
75
  };
70
76
  /** 文本,默认最长 500 字 */
71
- export declare const text: (max?: number) => Field<z.ZodString, false>;
77
+ export declare const text: (max?: number) => Field<z.ZodString, false, "text">;
72
78
  /** 整数 */
73
- export declare const int: () => Field<z.ZodNumber, false>;
79
+ export declare const int: () => Field<z.ZodNumber, false, "int">;
74
80
  /** 数值(双精度)。金额请用 money() */
75
- export declare const number: () => Field<z.ZodNumber, false>;
81
+ export declare const number: () => Field<z.ZodNumber, false, "number">;
76
82
  /** 金额:用字符串传输,避免浮点误差 */
77
- export declare const money: () => Field<z.ZodString, false>;
83
+ export declare const money: () => Field<z.ZodString, false, "money">;
78
84
  /** 主键或外键 */
79
- export declare const id: () => Field<z.ZodString, false>;
85
+ export declare const id: () => Field<z.ZodString, false, "id">;
80
86
  /** 日期 YYYY-MM-DD */
81
- export declare const date: () => Field<z.ZodString, false>;
87
+ export declare const date: () => Field<z.ZodString, false, "date">;
82
88
  /** 时间戳(ISO 8601) */
83
- export declare const datetime: () => Field<z.ZodString, false>;
89
+ export declare const datetime: () => Field<z.ZodString, false, "datetime">;
84
90
  /** 布尔 */
85
- export declare const bool: () => Field<z.ZodBoolean, false>;
91
+ export declare const bool: () => Field<z.ZodBoolean, false, "bool">;
86
92
  /** 枚举:enumOf('A', 'B') */
87
- export declare function enumOf<const V extends readonly [string, ...string[]]>(...values: V): Field<z.ZodEnum<{ [k_1 in V[number]]: k_1; } extends infer T ? { [k in keyof T]: T[k]; } : never>, false>;
93
+ export declare function enumOf<const V extends readonly [string, ...string[]]>(...values: V): Field<z.ZodEnum<{ [k_1 in V[number]]: k_1; } extends infer T ? { [k in keyof T]: T[k]; } : never>, false, "enum">;
88
94
  /** 引用另一个聚合:本质是 uuid,额外记下引用目标。可传聚合对象或聚合名(解决循环导入)。 */
89
95
  export declare function ref(to: {
90
96
  name: string;
91
- } | string): Field<z.ZodString, false>;
97
+ } | string): Field<z.ZodString, false, "ref">;
98
+ /** 一对多反查:实际外键在目标聚合上,不创建另一份关系或可写数组。 */
99
+ export declare function hasMany(to: {
100
+ name: string;
101
+ } | string, options: {
102
+ mappedBy: string;
103
+ description?: string;
104
+ }): Field<z.ZodArray<z.ZodString>, false, "hasMany">;
105
+ /** 多对多:字段与普通属性一处声明,中间表由框架生成;业务命令返回目标 ID 数组。 */
106
+ export declare function manyToMany(to: {
107
+ name: string;
108
+ } | string, options?: {
109
+ description?: string;
110
+ }): Field<z.ZodArray<z.ZodString>, false, "manyToMany">;
111
+ export declare const isRelation: (field: FieldMeta) => boolean;
92
112
  type ShapeZod<S extends Fields> = {
93
113
  [K in keyof S]: S[K] extends Field<infer T, infer O> ? ([O] extends [true] ? z.ZodOptional<z.ZodNullable<T>> : T) : never;
94
114
  };
@@ -103,5 +123,5 @@ export declare function isField(v: unknown): v is AnyField;
103
123
  /** 把一组字段拼成对象 schema */
104
124
  export declare function objectSchema(fields: Fields): z.ZodObject<any>;
105
125
  /** 结构不固定的 JSON 对象(例如能力点参数),存为 jsonb。业务字段请优先用 list 或具体类型。 */
106
- export declare const json: () => Field<z.ZodRecord<z.ZodString, z.ZodUnknown>, false>;
126
+ export declare const json: () => Field<z.ZodRecord<z.ZodString, z.ZodUnknown>, false, "list">;
107
127
  export {};
@@ -12,6 +12,7 @@ export class Field {
12
12
  isOptional;
13
13
  sensitiveKind;
14
14
  ref;
15
+ mappedBy;
15
16
  isUnique;
16
17
  isHidden;
17
18
  description;
@@ -27,6 +28,7 @@ export class Field {
27
28
  this.isOptional = meta.isOptional ?? false;
28
29
  this.sensitiveKind = meta.sensitiveKind;
29
30
  this.ref = meta.ref;
31
+ this.mappedBy = meta.mappedBy;
30
32
  this.isUnique = meta.isUnique;
31
33
  this.isHidden = meta.isHidden;
32
34
  this.description = meta.description;
@@ -40,6 +42,7 @@ export class Field {
40
42
  kind: this.kind, sql: this.sql, isOptional: this.isOptional, sensitiveKind: this.sensitiveKind, ref: this.ref,
41
43
  isUnique: this.isUnique, isHidden: this.isHidden, description: this.description, values: this.values,
42
44
  shape: this.shape, max: this.max,
45
+ mappedBy: this.mappedBy,
43
46
  };
44
47
  }
45
48
  with(patch) {
@@ -51,20 +54,28 @@ export class Field {
51
54
  }
52
55
  /** 可选字段:列可空,输入可以不填 */
53
56
  optional() {
57
+ if (isRelation(this))
58
+ throw declError('CAGE-REL-001', '集合关系使用空集合表示没有关联,不支持 optional');
54
59
  return this.with({ isOptional: true });
55
60
  }
56
61
  /** 敏感字段:读侧按种类掩码,持有 聚合.unmask.种类 能力才能看明文 */
57
62
  sensitive(kind) {
63
+ if (isRelation(this))
64
+ throw declError('CAGE-REL-001', '关系字段不支持 sensitive;目标字段按目标聚合权限掩码');
58
65
  if (!SENSITIVE_KINDS.includes(kind))
59
66
  throw declError('CAGE-FIELD-001', `未知的敏感种类 ${kind}`);
60
67
  return this.with({ sensitiveKind: kind });
61
68
  }
62
69
  /** 唯一约束(租户内) */
63
70
  unique() {
71
+ if (isRelation(this))
72
+ throw declError('CAGE-REL-001', '集合关系自动保证关联对唯一,不支持单字段 unique');
64
73
  return this.with({ isUnique: true });
65
74
  }
66
75
  /** 隐藏字段:不出现在读侧、审计、事件和前端契约 */
67
76
  hidden() {
77
+ if (isRelation(this))
78
+ throw declError('CAGE-REL-001', '关系使用所属对象和目标对象权限,不支持 hidden 修饰');
68
79
  return this.with({ isHidden: true });
69
80
  }
70
81
  /** 口径说明 */
@@ -103,7 +114,25 @@ export function ref(to) {
103
114
  throw declError('CAGE-FIELD-002', 'ref() 的目标为空,可能是循环导入;可改为 ref("聚合名")');
104
115
  return mk(z.string().uuid(), { kind: 'ref', sql: 'uuid', ref: target });
105
116
  }
117
+ /** 一对多反查:实际外键在目标聚合上,不创建另一份关系或可写数组。 */
118
+ export function hasMany(to, options) {
119
+ const target = typeof to === 'string' ? to : to?.name;
120
+ if (!target)
121
+ throw declError('CAGE-REL-001', 'hasMany() 需要目标聚合;循环引用请使用聚合名字符串');
122
+ return mk(z.array(z.string().uuid()), { kind: 'hasMany', sql: '', ref: target, ...options });
123
+ }
124
+ /** 多对多:字段与普通属性一处声明,中间表由框架生成;业务命令返回目标 ID 数组。 */
125
+ export function manyToMany(to, options = {}) {
126
+ const target = typeof to === 'string' ? to : to?.name;
127
+ if (!target)
128
+ throw declError('CAGE-REL-001', 'manyToMany() 需要目标聚合;循环引用请使用聚合名字符串');
129
+ return mk(z.array(z.string().uuid()), { kind: 'manyToMany', sql: '', ref: target, ...options });
130
+ }
131
+ export const isRelation = (field) => field.kind === 'hasMany' || field.kind === 'manyToMany';
106
132
  export function list(arg) {
133
+ const members = isField(arg) ? [arg] : Object.values(arg);
134
+ if (members.some(isRelation))
135
+ throw declError('CAGE-REL-001', '关系字段只能直接声明在聚合 fields 中,不能嵌入 JSON 明细');
107
136
  if (isField(arg)) {
108
137
  return mk(z.array(arg.zod), { kind: 'list', sql: 'jsonb', shape: { item: arg } });
109
138
  }
@@ -115,7 +144,7 @@ export function isField(v) {
115
144
  }
116
145
  /** 把一组字段拼成对象 schema */
117
146
  export function objectSchema(fields) {
118
- return z.object(Object.fromEntries(Object.entries(fields).map(([k, f]) => [k, f.zod])));
147
+ return z.object(Object.fromEntries(Object.entries(fields).filter(([, f]) => f.kind !== 'hasMany').map(([k, f]) => [k, f.zod])));
119
148
  }
120
149
  /** 结构不固定的 JSON 对象(例如能力点参数),存为 jsonb。业务字段请优先用 list 或具体类型。 */
121
150
  export const json = () => mk(z.record(z.string(), z.unknown()), { kind: 'list', sql: 'jsonb' });
@@ -1 +1 @@
1
- {"version":3,"file":"fields.js","sourceRoot":"","sources":["../../src/primitives/fields.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"fields.js","sourceRoot":"","sources":["../../src/primitives/fields.ts"],"names":[],"mappings":"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"}
package/dist/read.d.ts CHANGED
@@ -24,15 +24,21 @@ export declare class ReadSide {
24
24
  private readonly k;
25
25
  constructor(k: Kernel);
26
26
  /** 读侧账号上的只读事务,已写入会话变量 */
27
- tx<T>(ctx: Ctx, fn: (tx: Transaction<any>) => Promise<T>): Promise<T>;
27
+ tx<T>(ctx: Ctx, fn: (tx: Transaction<any>) => Promise<T>, isolation?: 'read committed' | 'repeatable read'): Promise<T>;
28
28
  private agg;
29
29
  private need;
30
30
  /** 明文读取敏感字段要写读审计 */
31
31
  private auditUnmask;
32
32
  get(a: AnyAggregate | string, id: string, ctx: Ctx): Promise<Record<string, any>>;
33
33
  list(a: AnyAggregate | string, params: ListParams, ctx: Ctx): Promise<Page<Record<string, any>>>;
34
+ /** 关联按需分页:先确认所属对象可见,再从目标安全视图读取;两侧能力、范围和掩码都生效。 */
35
+ related(a: AnyAggregate | string, id: string, fieldName: string, params: ListParams, ctx: Ctx): Promise<Page<Record<string, any>>>;
34
36
  private listIn;
35
- /** 导出:就是读,走安全视图,自然带范围和脱敏;需要 聚合.export 能力,并写读审计 */
37
+ /**
38
+ * worker 使用的导出构建器:同一只读快照内分批读取,避免翻页期间数据变化导致漏行或重复。
39
+ * 上限用于约束单任务内存与数据库文件存储;超限明确失败,绝不能静默截断成“成功文件”。
40
+ * HTTP 只提交任务,保留本方法供受控的服务端调用。
41
+ */
36
42
  export(a: AnyAggregate | string, params: ListParams, ctx: Ctx): Promise<{
37
43
  filename: string;
38
44
  contentType: string;
package/dist/read.js CHANGED
@@ -6,7 +6,10 @@ import { viewRowToObject } from './db/rows.js';
6
6
  import { setSessionVars } from './db/session.js';
7
7
  import { cageError } from './errors.js';
8
8
  import { hiddenFields, sensitiveFields } from './primitives/aggregate.js';
9
+ import { isRelation } from './primitives/fields.js';
9
10
  import { snake } from './util/names.js';
11
+ import { csvCell } from './transfer/csv.js';
12
+ import { relationTable } from './relations.js';
10
13
  const SYS = { id: 'id', status: 'status', version: 'version', createdAt: 'created_at', updatedAt: 'updated_at' };
11
14
  const RESERVED = new Set(['page', 'pageSize', 'sort', 'createdAfter', 'createdBefore']);
12
15
  function coerce(kind, v) {
@@ -24,9 +27,10 @@ export class ReadSide {
24
27
  this.k = k;
25
28
  }
26
29
  /** 读侧账号上的只读事务,已写入会话变量 */
27
- async tx(ctx, fn) {
30
+ async tx(ctx, fn, isolation = 'read committed') {
28
31
  return this.k.readDb
29
32
  .transaction()
33
+ .setIsolationLevel(isolation)
30
34
  .setAccessMode('read only')
31
35
  .execute(async (tx) => {
32
36
  await setSessionVars(tx, ctx);
@@ -56,7 +60,12 @@ export class ReadSide {
56
60
  capability: `${agg.name}.${kind}`, command: null, aggregate: agg.name,
57
61
  aggregateId: ids.length === 1 ? ids[0] : null, result: 'ok',
58
62
  diff: { unmasked: kinds, rows: ids.length }, requestId: ctx.requestId ?? null, client: ctx.client ?? null,
59
- }).catch((e) => this.k.log.warn({ err: String(e) }, '读审计写入失败'));
63
+ }).catch((e) => {
64
+ // 导出会产生可离线保存的文件,审计失败不能交付文件;交给任务系统重试。
65
+ if (kind === 'export')
66
+ throw e;
67
+ this.k.log.warn({ err: String(e) }, '读审计写入失败');
68
+ });
60
69
  }
61
70
  async get(a, id, ctx) {
62
71
  const agg = this.agg(a);
@@ -76,7 +85,28 @@ export class ReadSide {
76
85
  void this.auditUnmask(agg, ctx, res.items.map((r) => r.id), 'read');
77
86
  return res;
78
87
  }
79
- async listIn(tx, agg, params, maxPageSize) {
88
+ /** 关联按需分页:先确认所属对象可见,再从目标安全视图读取;两侧能力、范围和掩码都生效。 */
89
+ async related(a, id, fieldName, params, ctx) {
90
+ const owner = this.agg(a), field = owner.fields[fieldName];
91
+ this.need(ctx, `${owner.name}.read`);
92
+ if (!field || !isRelation(field) || !/^[0-9a-f-]{36}$/i.test(id))
93
+ throw cageError('NOT_FOUND');
94
+ const target = this.agg(field.ref);
95
+ this.need(ctx, `${target.name}.read`);
96
+ const res = await this.tx(ctx, async (tx) => {
97
+ const source = await tx.selectFrom(`safe.${owner.table.name}`).select('id').where('id', '=', id).where('status', '<>', 'deleted').executeTakeFirst();
98
+ if (!source)
99
+ throw cageError('NOT_FOUND');
100
+ const table = relationTable(owner, fieldName);
101
+ const restrict = field.kind === 'hasMany'
102
+ ? (q) => q.where(sql.ref(snake(field.mappedBy)), '=', id)
103
+ : (q) => q.where('id', 'in', tx.selectFrom(`safe.${table.name}`).select('target_id').where('source_id', '=', id));
104
+ return this.listIn(tx, target, params, 200, true, restrict);
105
+ }, 'repeatable read');
106
+ void this.auditUnmask(target, ctx, res.items.map((row) => row.id), 'read');
107
+ return res;
108
+ }
109
+ async listIn(tx, agg, params, maxPageSize, count = true, restrict = (q) => q) {
80
110
  const page = Math.max(1, Number(params.page ?? 1) || 1);
81
111
  const pageSize = Math.min(maxPageSize, Math.max(1, Number(params.pageSize ?? 20) || 20));
82
112
  const hidden = new Set(hiddenFields(agg));
@@ -86,7 +116,7 @@ export class ReadSide {
86
116
  if (RESERVED.has(k) || v === undefined || v === '')
87
117
  continue;
88
118
  const f = agg.fields[k];
89
- const col = SYS[k] ?? (f && !hidden.has(k) && f.sql !== 'jsonb' && !f.sensitiveKind ? snake(k) : undefined);
119
+ const col = SYS[k] ?? (f && !isRelation(f) && !hidden.has(k) && f.sql !== 'jsonb' && !f.sensitiveKind ? snake(k) : undefined);
90
120
  if (!col)
91
121
  throw cageError('INVALID_INPUT', [{ path: k, message: `不能按 ${k} 过滤` }]);
92
122
  if (k === 'status' && typeof v === 'string' && v.includes(','))
@@ -100,34 +130,51 @@ export class ReadSide {
100
130
  q = q.where('created_at', '>=', new Date(String(params.createdAfter)));
101
131
  if (params.createdBefore)
102
132
  q = q.where('created_at', '<', new Date(String(params.createdBefore)));
103
- return q;
133
+ return restrict(q);
104
134
  };
105
135
  let q = apply(tx.selectFrom(view).selectAll());
106
136
  const sortKey = typeof params.sort === 'string' && params.sort ? params.sort.replace(/^-/, '') : 'createdAt';
107
- const sortCol = SYS[sortKey] ?? (agg.fields[sortKey] && !hidden.has(sortKey) ? snake(sortKey) : undefined);
137
+ const sortField = agg.fields[sortKey];
138
+ const sortCol = SYS[sortKey] ?? (sortField && !isRelation(sortField) && !hidden.has(sortKey) ? snake(sortKey) : undefined);
108
139
  if (!sortCol)
109
140
  throw cageError('INVALID_INPUT', [{ path: 'sort', message: `不能按 ${sortKey} 排序` }]);
110
141
  const desc = typeof params.sort === 'string' ? params.sort.startsWith('-') : true;
111
142
  q = q.orderBy(sql.ref(sortCol), desc ? 'desc' : 'asc').orderBy('id').limit(pageSize).offset((page - 1) * pageSize);
112
143
  const rows = await q.execute();
113
- const cnt = await apply(tx.selectFrom(view).select(sql `count(*)::int`.as('n'))).executeTakeFirst();
144
+ const cnt = count ? await apply(tx.selectFrom(view).select(sql `count(*)::int`.as('n'))).executeTakeFirst() : null;
114
145
  return { items: rows.map(viewRowToObject), total: Number(cnt?.n ?? 0), page, pageSize };
115
146
  }
116
- /** 导出:就是读,走安全视图,自然带范围和脱敏;需要 聚合.export 能力,并写读审计 */
147
+ /**
148
+ * worker 使用的导出构建器:同一只读快照内分批读取,避免翻页期间数据变化导致漏行或重复。
149
+ * 上限用于约束单任务内存与数据库文件存储;超限明确失败,绝不能静默截断成“成功文件”。
150
+ * HTTP 只提交任务,保留本方法供受控的服务端调用。
151
+ */
117
152
  async export(a, params, ctx) {
118
153
  const agg = this.agg(a);
119
154
  this.need(ctx, `${agg.name}.export`);
120
- const res = await this.tx(ctx, (tx) => this.listIn(tx, agg, { ...params, page: 1, pageSize: 10000 }, 10000));
121
- const cols = ['id', ...Object.keys(agg.fields).filter((k) => !hiddenFields(agg).includes(k)), 'status', 'version', 'createdAt', 'updatedAt'];
122
- const cell = (v) => {
123
- const s = v === null || v === undefined ? '' : typeof v === 'object' ? JSON.stringify(v) : String(v);
124
- // 防 CSV 公式注入
125
- const safe = /^[=+\-@\t\r]/.test(s) ? `'${s}` : s;
126
- return /[",\n]/.test(safe) ? `"${safe.replace(/"/g, '""')}"` : safe;
127
- };
128
- const body = [cols.join(','), ...res.items.map((r) => cols.map((c) => cell(r[c])).join(','))].join('\n');
129
- await this.auditUnmask(agg, ctx, res.items.map((r) => r.id), 'export');
130
- return { filename: `${agg.table.name}.csv`, contentType: 'text/csv; charset=utf-8', body: '' + body, rows: res.items.length };
155
+ const cols = ['id', ...Object.entries(agg.fields).filter(([, f]) => !f.isHidden && !isRelation(f)).map(([k]) => k), 'status', 'version', 'createdAt', 'updatedAt'];
156
+ const lines = ['\uFEFF' + cols.map(csvCell).join(',')], ids = [];
157
+ let bytes = Buffer.byteLength(lines[0]);
158
+ await this.tx(ctx, async (tx) => {
159
+ let total = 0;
160
+ for (let page = 1; page === 1 || ids.length < total; page++) {
161
+ const res = await this.listIn(tx, agg, { ...params, page, pageSize: 1000 }, 1000, page === 1);
162
+ if (page === 1)
163
+ total = res.total;
164
+ if (total > 100_000)
165
+ throw cageError('INVALID_INPUT', '导出最多支持 100000 行,请缩小筛选范围');
166
+ for (const row of res.items) {
167
+ const line = cols.map((c) => csvCell(row[c])).join(',');
168
+ bytes += Buffer.byteLength(line) + 1;
169
+ if (bytes > 20 * 1024 * 1024)
170
+ throw cageError('INVALID_INPUT', '导出文件超过 20 MiB,请缩小筛选范围');
171
+ lines.push(line);
172
+ ids.push(row.id);
173
+ }
174
+ }
175
+ }, 'repeatable read');
176
+ await this.auditUnmask(agg, ctx, ids, 'export');
177
+ return { filename: `${agg.table.name}.csv`, contentType: 'text/csv; charset=utf-8', body: lines.join('\n'), rows: ids.length };
131
178
  }
132
179
  /** 执行 query 声明:需要 reads 里每个聚合的 read 能力;手写 SQL 用 :name 占位,全部参数化 */
133
180
  async run(q, raw, ctx) {