openxiangda-cli 2.0.0-alpha.86 → 2.0.0-alpha.89

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openxiangda-cli",
3
- "version": "2.0.0-alpha.86",
3
+ "version": "2.0.0-alpha.89",
4
4
  "description": "Thin application-level CLI for OpenXiangda 2.0.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -23,9 +23,9 @@
23
23
  ],
24
24
  "dependencies": {
25
25
  "@oclif/core": "4.13.3",
26
- "openxiangda-contracts": "2.0.0-alpha.40",
27
- "openxiangda-devkit-core": "2.0.0-alpha.53",
28
- "openxiangda-mcp": "2.0.0-alpha.53"
26
+ "openxiangda-contracts": "2.0.0-alpha.43",
27
+ "openxiangda-devkit-core": "2.0.0-alpha.56",
28
+ "openxiangda-mcp": "2.0.0-alpha.56"
29
29
  },
30
30
  "devDependencies": {
31
31
  "tsx": "4.23.12",
@@ -8,9 +8,9 @@ pnpm openxiangda dev
8
8
 
9
9
  当前应用已经把 CLI 和全部 2.0 包精确锁定在 `package.json` 与 lockfile 中。创建另一个应用时使用当前安装的 OpenXiangda 2.0 Skill 给出的精确 CLI 版本,不使用 `latest`、`alpha` 或全局命令。
10
10
 
11
- 在 `openxiangda.config.ts` 的 `data.resources` 中只声明资源 `code`、`name`、`fields` 和可选列表/布局/数据策略。每个字段一次写完 typelabelrequired、列表/筛选标记、reference/file 与 access;不要再写 `schema`、`surface`、资源 capabilities、资源 fieldPolicies 或 `platform/data` 模块。编译器据此生成标准后台列表、筛选、表单、详情、审计、文件上传、独立移动端页面和 AI Schema。
11
+ 在 `openxiangda.config.ts` 的 `data.resources` 中只声明资源 `code`、`name`、`fields` 和可选列表/布局/数据策略。每个字段一次写完语义 `type`、`label`、`required`、列表/筛选标记、`options`/`source`/`file``access`;不要再写 `schema`、`surface`、PostgreSQL 类型、资源 capabilities、资源 fieldPolicies 或 `platform/data` 模块。编译器据此生成标准后台列表、筛选、表单、详情、审计、文件上传、独立移动端页面、Data API 查询计划和 AI Schema。
12
12
 
13
- 成员、部门和资源关系分别使用 `reference: { kind: 'directory-user' }`、`directory-department`、`resource`,只保存稳定 ID,不复制目录数据。单选成员字段使用 `string`,单选部门和资源引用使用 `uuid`,多选引用统一使用 `json`。需要目录选择器的业务角色必须显式拥有 `app:<app-code>:directory:read` 能力。用 `resourceCapabilityCodes` 生成资源 CRUD 权限,用 `currentUserDataPolicy` 声明唯一的当前用户行规则。
13
+ 成员、部门分别声明 `user.single`/`user.multiple` `department.single`/`department.multiple`;同应用动态资源声明 `resource-ref.single`/`resource-ref.multiple` 及编译期固定的 `source`。这些字段都保存完整 `{label,value,...}` 显示快照,资源引用还保存 `resourceCode` 和声明的 `snapshot` 字段;查询和权限以稳定 `value` 为比较键。需要目录选择器的业务角色必须显式拥有 `app:<app-code>:directory:read` 能力。用 `resourceCapabilityCodes` 生成资源 CRUD 权限,用 `currentUserDataPolicy` 声明唯一的当前用户行规则。
14
14
 
15
15
  所有 CRUD 位于 `apps/web/src/platform-client.ts` 的 Data API adapter;Nest 只在需要复杂事务或外部集成时增加 App API。选择器显式携带当前环境、使用同源 Cookie。标准客户端在内存中恢复平台 RoleSession,并自动为 Data、Directory、文件和 App API 请求携带 header;业务代码不读取或持久化 Token、RoleSession 标识与授权结果。
16
16
 
@@ -15,7 +15,7 @@
15
15
  "@nestjs/common": "11.1.29",
16
16
  "@nestjs/core": "11.1.29",
17
17
  "@nestjs/platform-fastify": "11.1.29",
18
- "openxiangda-nest": "2.0.0-alpha.49",
18
+ "openxiangda-nest": "2.0.0-alpha.52",
19
19
  "reflect-metadata": "0.2.2",
20
20
  "rxjs": "7.8.2"
21
21
  },
@@ -1,5 +1,13 @@
1
1
  import { Module } from '@nestjs/common';
2
- import { OpenXiangdaModule } from 'openxiangda-nest';
2
+ import {
3
+ eventHandlerManifest,
4
+ eventSchemas,
5
+ eventSubscriptionCodes,
6
+ } from '@app/contracts';
7
+ import {
8
+ OpenXiangdaModule,
9
+ eventSigningSecretsFromEnvironment,
10
+ } from 'openxiangda-nest';
3
11
 
4
12
  @Module({
5
13
  imports: [
@@ -24,6 +32,11 @@ import { OpenXiangdaModule } from 'openxiangda-nest';
24
32
  version: process.env.OPENXIANGDA_APP_VERSION || '0.1.0-dev',
25
33
  connectedDevelopment:
26
34
  process.env.OPENXIANGDA_CONNECTED_DEV === 'true',
35
+ eventHandlerManifest,
36
+ eventSchemas,
37
+ eventSigningSecrets: eventSigningSecretsFromEnvironment(
38
+ eventSubscriptionCodes
39
+ ),
27
40
  }),
28
41
  ],
29
42
  })
@@ -6,9 +6,11 @@ test('the clean server template only owns platform bootstrap', () => {
6
6
  const source = readFileSync(new URL('../src/app.module.ts', import.meta.url), 'utf8');
7
7
  const main = readFileSync(new URL('../src/main.ts', import.meta.url), 'utf8');
8
8
  assert.match(source, /OpenXiangdaModule\.forRoot/);
9
+ assert.match(source, /eventHandlerManifest/);
10
+ assert.match(source, /eventSigningSecretsFromEnvironment/);
9
11
  assert.match(main, /bootstrapOpenXiangdaApplication\(AppModule\)/);
10
12
  assert.doesNotMatch(main, /NestFactory|FastifyAdapter/);
11
- const legacyMarkers = ['oauth' + 'Client', 'event' + 'Signing', 'Workflow', 'instrument' + '-context'];
13
+ const legacyMarkers = ['oauth' + 'Client', 'Workflow', 'instrument' + '-context'];
12
14
  assert.doesNotMatch(source, new RegExp(legacyMarkers.join('|')));
13
15
  assert.doesNotMatch(source, /createData|updateData|deleteData|queryData/);
14
16
  });
@@ -16,7 +16,7 @@
16
16
  "antd": "6.4.3",
17
17
  "dayjs": "1.11.18",
18
18
  "docx-preview": "0.3.7",
19
- "openxiangda-contracts": "2.0.0-alpha.40",
19
+ "openxiangda-contracts": "2.0.0-alpha.43",
20
20
  "react": "19.2.8",
21
21
  "react-dom": "19.2.8",
22
22
  "react-router-dom": "7.8.2",
@@ -932,7 +932,7 @@ function GeneratedResourceFormPage({
932
932
  try {
933
933
  const result = await transactNativeData(operations);
934
934
  const parent = result.items[0];
935
- if (!parent) {
935
+ if (!parent || parent.operation === 'emitEvent') {
936
936
  throw new Error('OPENXIANGDA_SUBTABLE_PARENT_RESULT_MISSING');
937
937
  }
938
938
  message.success(mode === 'create' ? `${name}已创建` : '修改已保存');
@@ -25,6 +25,6 @@ export default defineOpenXiangdaApp({
25
25
  authorizationTransitions: [],
26
26
  },
27
27
  data: { resources: [] },
28
- events: { subscriptions: [], timers: [] },
28
+ events: { schemas: [], subscriptions: [], timers: [], dateTriggers: [] },
29
29
  workflows: { definitions: [], bindings: [], activations: [], providers: [], editableParameters: [] },
30
30
  });
@@ -22,9 +22,9 @@
22
22
  "build": "pnpm --recursive build"
23
23
  },
24
24
  "devDependencies": {
25
- "openxiangda-cli": "2.0.0-alpha.86",
26
- "openxiangda-contracts": "2.0.0-alpha.40",
27
- "openxiangda-devkit-core": "2.0.0-alpha.53",
25
+ "openxiangda-cli": "2.0.0-alpha.89",
26
+ "openxiangda-contracts": "2.0.0-alpha.43",
27
+ "openxiangda-devkit-core": "2.0.0-alpha.56",
28
28
  "typescript": "5.9.3"
29
29
  }
30
30
  }
@@ -17,6 +17,13 @@ export const appOperations = {} as const;
17
17
  export const appRoutes = {} as const;
18
18
  export const eventTypes = [] as const;
19
19
  export const eventSubscriptionCodes = [] as const;
20
+ export const eventHandlerManifest = {
21
+ "schemaVersion": "openxiangda.event-handler-manifest/v2",
22
+ "appCode": "openxiangda-application",
23
+ "handlers": []
24
+ } as const;
25
+ export const eventHandlers = {} as const;
26
+ export const eventSchemas = [] as const;
20
27
  export const workflowCodes = [] as const;
21
28
 
22
29
  export type ResourceCode = (typeof resourceCodes)[number];
@@ -25,8 +32,27 @@ export type AppOperation = (typeof appOperations)[keyof typeof appOperations];
25
32
  export type AppRoute = (typeof appRoutes)[keyof typeof appRoutes];
26
33
  export type EventType = (typeof eventTypes)[number];
27
34
  export type EventSubscriptionCode = (typeof eventSubscriptionCodes)[number];
35
+ export type EventTypesForSubscription<TCode extends EventSubscriptionCode> = never;
28
36
  export type WorkflowCode = (typeof workflowCodes)[number];
29
37
 
38
+ export interface OpenXiangdaCloudEvent<TType extends EventType = EventType, TData extends Record<string, unknown> = Record<string, unknown>> {
39
+ specversion: "1.0";
40
+ id: string;
41
+ source: string;
42
+ type: TType;
43
+ subject?: string;
44
+ time: string;
45
+ datacontenttype: "application/json";
46
+ dataschema?: string;
47
+ data: TData;
48
+ tenantid: string;
49
+ appcode: typeof appCode;
50
+ environment: string;
51
+ traceid?: string;
52
+ schemaversion: string;
53
+ }
54
+ export type EventHandlerInput<TCode extends EventSubscriptionCode> = OpenXiangdaCloudEvent<EventTypesForSubscription<TCode>>;
55
+
30
56
  export interface LabeledValue {
31
57
  label: string;
32
58
  value: string;