openxiangda 1.0.16 → 1.0.17

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.
@@ -69,6 +69,8 @@ When the user provides a root domain such as `https://yida.wisejob.cn/`, use it
69
69
  - Publish normal form pages, workflow form pages, and custom code pages through `openxiangda workspace publish --profile <name>` from the app workspace.
70
70
  - Never store token data in the project directory. User tokens live in `~/.openxiangda/profiles.json`; project state lives in `.openxiangda/state.json` and stores only IDs and mappings.
71
71
  - Use logical resource codes in local files. Platform-specific IDs such as `formUuid`, `pageId`, `workflowId`, and `automationId` must be isolated by profile.
72
+ - Put engineering-managed resources in `src/resources/` and use `openxiangda resource validate|plan|publish|pull`. `workspace publish` publishes workspace forms/pages first, then runs non-destructive resource upsert. Only pass `--prune` when the user explicitly wants local manifests to delete platform-side extras.
73
+ - For external APIs, create a connector manifest in `src/resources/connectors/` and call it from pages through `sdk.connector`; never put third-party API keys in page source.
72
74
  - Before publishing to another platform, verify the workspace is bound for that profile. Resource IDs from one profile must not be reused for another profile.
73
75
  - Use `openxiangda app snapshot <APP_XXX> --profile <name> --json` for diagnosis before changing an existing app.
74
76
  - Run write commands that update `.openxiangda/state.json` sequentially within the same workspace. Read-only commands can run in parallel.
@@ -100,6 +102,7 @@ Core CLI / state:
100
102
 
101
103
  - `references/openxiangda-api.md` — `/openxiangda-api/v1` request and response fields.
102
104
  - `references/workspace-state.md` — `.openxiangda/state.json` shape and profile isolation rules.
105
+ - `references/connector-resources.md` — `src/resources` manifests, connector schema, and SDK connector calls.
103
106
 
104
107
  Form authoring:
105
108
 
@@ -0,0 +1,68 @@
1
+ # Connector Resources
2
+
3
+ OpenXiangda engineering resources live under `src/resources/`.
4
+
5
+ Common folders:
6
+
7
+ - `connectors`
8
+ - `roles`
9
+ - `menus`
10
+ - `workflows`
11
+ - `automations`
12
+ - `permissions/page-groups`
13
+ - `permissions/form-groups`
14
+ - `settings/forms`
15
+
16
+ Commands:
17
+
18
+ ```bash
19
+ openxiangda resource validate --profile dev
20
+ openxiangda resource plan --profile dev
21
+ openxiangda resource publish --profile dev
22
+ openxiangda resource pull --profile dev
23
+ ```
24
+
25
+ Connector manifests use stable `code` values. The platform maps connector `code` to the existing connector `methodName`, and API `code` to the existing connector API `methodName`.
26
+
27
+ ```json
28
+ {
29
+ "code": "crm",
30
+ "name": "CRM Service",
31
+ "url": "https://crm.internal.example.com/api",
32
+ "authType": "apiKey",
33
+ "authConfig": {
34
+ "apiKey": {
35
+ "key": "X-API-Key",
36
+ "value": "internal-secret",
37
+ "in": "header"
38
+ }
39
+ },
40
+ "userContext": {
41
+ "enabled": true,
42
+ "inject": [
43
+ { "target": "header", "key": "X-User-Id", "value": "userId" },
44
+ { "target": "body", "key": "operator", "value": "user" }
45
+ ]
46
+ },
47
+ "apis": [
48
+ {
49
+ "code": "getCustomer",
50
+ "name": "Get Customer",
51
+ "method": "POST",
52
+ "path": "/customers/search",
53
+ "requestBodyType": "json",
54
+ "responseType": "json"
55
+ }
56
+ ]
57
+ }
58
+ ```
59
+
60
+ Runtime page code:
61
+
62
+ ```ts
63
+ await sdk.connector.call("crm.getCustomer", {
64
+ body: { keyword: "Acme" },
65
+ })
66
+ ```
67
+
68
+ Do not put third-party domains or API keys in page source. The runtime page calls `/:appType/v1/connectors/actions/invoke` for normal responses and `/:appType/v1/connectors/actions/download` for binary downloads; the platform backend applies auth, user context, and redaction.
@@ -6,6 +6,7 @@ Guidelines:
6
6
 
7
7
  - Do not hardcode `/openxiangda-api` calls inside end-user page components unless the page is explicitly an admin tool.
8
8
  - Prefer SDK modules for form data, user context, permissions, and platform navigation.
9
+ - Use `sdk.connector.invoke`, `sdk.connector.call("connector.api")`, or `sdk.connector.download` for external services. The SDK calls the platform runtime connector endpoint; it must not call third-party domains directly.
9
10
  - For the current user's department hierarchy, use `sdk.department.getCurrentUserParentDepartments()`; do not hardcode `GET /department/:id/parentDepartments` in page code.
10
11
  - Keep API calls behind small local functions so generated UI stays testable.
11
12
  - Treat user context and tenant context as runtime-provided values.
@@ -28,7 +28,12 @@ Tokens never belong in the project. User tokens live in `~/.openxiangda/profiles
28
28
  },
29
29
  "workflows": {},
30
30
  "automations": {},
31
- "menus": {}
31
+ "menus": {},
32
+ "roles": {},
33
+ "connectors": {},
34
+ "pagePermissionGroups": {},
35
+ "formPermissionGroups": {},
36
+ "formSettings": {}
32
37
  }
33
38
  }
34
39
  }
@@ -40,6 +45,7 @@ Tokens never belong in the project. User tokens live in `~/.openxiangda/profiles
40
45
  - Profile is the deployment boundary.
41
46
  - `baseUrl` is the backend API base. On standard private deployments it is `<origin>/service`; management pages use `/platform`, and app runtime pages use `/view`.
42
47
  - Local resource keys are logical codes.
43
- - Live IDs are nested under the profile that produced them.
48
+ - Live IDs and lightweight runtime aliases are nested under the profile that produced them.
49
+ - Do not store business configuration or secrets in `.openxiangda/state.json`; store those in `src/resources/`.
44
50
  - A prod publish must not read dev IDs.
45
51
  - Before publishing to another platform, run `openxiangda workspace bind --profile <name> --app-type <APP_XXX>`.
@@ -60,6 +60,8 @@ openxiangda workspace bind --profile dev --app-type APP_DEV
60
60
  openxiangda workspace bind --profile prod --app-type APP_PROD
61
61
  cd /path/to/sy-lowcode-app-workspace
62
62
  openxiangda workspace publish --profile dev
63
+ openxiangda resource validate --profile dev
64
+ openxiangda resource plan --profile dev
63
65
  ```
64
66
 
65
67
  Read-only diagnosis can use resource commands:
@@ -93,8 +95,10 @@ Project state is stored in `.openxiangda/state.json`:
93
95
  "automations": {},
94
96
  "menus": {},
95
97
  "roles": {},
98
+ "connectors": {},
96
99
  "pagePermissionGroups": {},
97
- "formPermissionGroups": {}
100
+ "formPermissionGroups": {},
101
+ "formSettings": {}
98
102
  }
99
103
  }
100
104
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openxiangda",
3
- "version": "1.0.16",
3
+ "version": "1.0.17",
4
4
  "description": "OpenXiangda CLI, workspace build tools, runtime SDK, and form components.",
5
5
  "private": false,
6
6
  "bin": {
@@ -54,6 +54,7 @@
54
54
  "check": "node --check bin/openxiangda.js && node --check lib/*.js && node --check packages/sdk/src/build-source/src/cli.mjs && node --check packages/sdk/src/build-source/scripts/*.mjs && node --check packages/sdk/src/build-source/scripts/utils/*.mjs",
55
55
  "prepack": "npm run build:sdk",
56
56
  "test:profile-isolation": "bash scripts/profile-isolation-smoke.sh",
57
+ "test:resource-plan": "node scripts/resource-plan-smoke.mjs",
57
58
  "test:skill-install": "bash scripts/skill-install-smoke.sh",
58
59
  "test:workspace-init": "bash scripts/workspace-init-smoke.sh"
59
60
  },
@@ -98,6 +98,17 @@ var parseContentDispositionFileName = (contentDisposition) => {
98
98
  }
99
99
  return void 0;
100
100
  };
101
+ var parseConnectorCallName = (name) => {
102
+ const value = String(name || "").trim();
103
+ const separatorIndex = value.indexOf(".");
104
+ if (separatorIndex <= 0 || separatorIndex === value.length - 1) {
105
+ throw new Error("\u8FDE\u63A5\u5668\u8C03\u7528\u540D\u5FC5\u987B\u662F connector.api \u683C\u5F0F");
106
+ }
107
+ return {
108
+ connector: value.slice(0, separatorIndex),
109
+ api: value.slice(separatorIndex + 1)
110
+ };
111
+ };
101
112
  var serializeQuery = (query) => {
102
113
  if (!query) {
103
114
  return void 0;
@@ -387,6 +398,52 @@ var createPageSdk = (context) => {
387
398
  throw toSdkError(error, payload);
388
399
  }
389
400
  };
401
+ const connector = {
402
+ invoke: (params) => request({
403
+ path: buildAppPath(
404
+ context,
405
+ params.appType,
406
+ "/v1/connectors/actions/invoke"
407
+ ),
408
+ method: "post",
409
+ body: {
410
+ connector: params.connector,
411
+ api: params.api,
412
+ pathParams: params.pathParams,
413
+ query: params.query,
414
+ body: params.body,
415
+ headers: params.headers,
416
+ requestBodyType: params.requestBodyType,
417
+ responseType: params.responseType
418
+ }
419
+ }),
420
+ call: (name, params = {}) => {
421
+ const target = parseConnectorCallName(name);
422
+ return connector.invoke({
423
+ ...params,
424
+ connector: target.connector,
425
+ api: target.api
426
+ });
427
+ },
428
+ download: (params) => download({
429
+ path: buildAppPath(
430
+ context,
431
+ params.appType,
432
+ "/v1/connectors/actions/download"
433
+ ),
434
+ method: "post",
435
+ body: {
436
+ connector: params.connector,
437
+ api: params.api,
438
+ pathParams: params.pathParams,
439
+ query: params.query,
440
+ body: params.body,
441
+ headers: params.headers,
442
+ requestBodyType: params.requestBodyType,
443
+ responseType: "binary"
444
+ }
445
+ })
446
+ };
390
447
  const form = {
391
448
  getDetail: (params) => request({
392
449
  path: buildAppPath(
@@ -1044,6 +1101,7 @@ var createPageSdk = (context) => {
1044
1101
  request,
1045
1102
  download
1046
1103
  },
1104
+ connector,
1047
1105
  form,
1048
1106
  user,
1049
1107
  department,