apaas-oapi-client 0.1.39 → 1.0.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.
@@ -0,0 +1,157 @@
1
+ # Schema Maintenance SOP
2
+
3
+ Use this reference for whole-application object maintenance: reading object structure, creating objects, editing fields, deleting fields, deleting objects, converting SQL/ER designs, and managing lookup/reference dependencies.
4
+
5
+ ## Non-Negotiable Rules
6
+
7
+ - Read live object metadata before writing.
8
+ - Do not modify or delete system objects `_user` and `_department`.
9
+ - Do not create, replace, or remove fields whose API name starts with `_`.
10
+ - Treat `schema.create`, `schema.update`, and `schema.delete` as high-risk writes.
11
+ - Batch `schema.create/update/delete` calls by at most 10 objects.
12
+ - After every write, re-read metadata and verify the expected object/field shape.
13
+
14
+ ## Response Validation
15
+
16
+ Check three layers after every raw schema call with the SDK helper:
17
+
18
+ ```ts
19
+ client.schema.checkResponse(result, "schema.update");
20
+ ```
21
+
22
+ `code: "0"` with `data: null` is not success. It usually means a required setting is missing, for example `text.multiline` or `auto_number.generation_method`.
23
+
24
+ ## Create Objects: Three Stages
25
+
26
+ Do not create a full object with raw `schema.create({ fields })`. Use `client.schema.createWithStages()` when fields are involved. It enforces this sequence even when there is no obvious dependency cycle.
27
+
28
+ 1. Stage 1a: create object shells only.
29
+ 2. Stage 1b: add base fields with `schema.update` and `operator: "add"`.
30
+ 3. Stage 2: add `lookup` / `lookup_multi` fields after all target objects exist.
31
+ 4. Stage 3: add `reference_field` after its single-value lookup exists.
32
+ 5. Final: update `display_name`, `allow_search_fields`, and `search_layout` after fields exist.
33
+
34
+ ```ts
35
+ await client.schema.createWithStages({
36
+ objects: [{
37
+ api_name: "customer",
38
+ label: { zh_cn: "客户", en_us: "Customer" },
39
+ settings: { display_name: "name", allow_search_fields: ["_id", "name"], search_layout: [] },
40
+ fields: [{
41
+ operator: "add",
42
+ api_name: "name",
43
+ label: { zh_cn: "客户名称", en_us: "Name" },
44
+ type: {
45
+ name: "text",
46
+ settings: {
47
+ required: true,
48
+ unique: false,
49
+ case_sensitive: false,
50
+ multiline: false,
51
+ max_length: 100
52
+ }
53
+ },
54
+ encrypt_type: "none"
55
+ }]
56
+ }]
57
+ });
58
+ ```
59
+
60
+ ## Update Fields
61
+
62
+ - Add: send `operator: "add"` and the full field definition.
63
+ - Replace: send `operator: "replace"` and the full `type.name + type.settings`; label-only replace fails.
64
+ - Remove: send `operator: "remove"` and only `api_name`.
65
+ - Skip existing fields when rerunning idempotently.
66
+
67
+ ```ts
68
+ await client.schema.addFieldsIdempotent({
69
+ object_name: "customer",
70
+ fields: fieldsToAdd
71
+ });
72
+ ```
73
+
74
+ ## Dependency Order
75
+
76
+ Add order:
77
+
78
+ 1. Object shells
79
+ 2. Base fields
80
+ 3. `lookup` / `lookup_multi`
81
+ 4. `reference_field`
82
+
83
+ Remove order:
84
+
85
+ 1. `reference_field`
86
+ 2. `lookup` / `lookup_multi`
87
+ 3. Other custom fields
88
+ 4. Custom objects
89
+
90
+ Never add a lookup and a reference field that depends on it in the same `schema.update` batch; execution order is not guaranteed.
91
+
92
+ ## Delete All Custom Objects
93
+
94
+ Use the SDK helper for environment cleanup or model rebuilds:
95
+
96
+ ```ts
97
+ await client.schema.deleteAllCustomObjects({
98
+ confirm: true,
99
+ removeOtherFields: true
100
+ });
101
+ ```
102
+
103
+ The helper lists custom objects, reads fields, removes `referenceField`, then `lookup`, then optional other custom fields, deletes objects in batches of 10, and re-lists objects to verify deletion.
104
+
105
+ ## SQL / ER To aPaaS Mapping
106
+
107
+ Map relational designs into aPaaS objects only after presenting a conversion table for user confirmation.
108
+
109
+ | SQL Concept | aPaaS Mapping |
110
+ | --- | --- |
111
+ | Table | Object |
112
+ | Primary key `id` | Ignore; aPaaS provides `_id` |
113
+ | `created_at` / `updated_at` audit columns | Usually ignore; aPaaS provides system fields |
114
+ | `VARCHAR(n)` / `CHAR(n)` | `text` with `max_length: n`, `multiline: false` |
115
+ | `TEXT` / long text | `text` with `multiline: true`, `max_length: 100000` |
116
+ | `INT` / `INTEGER` / `BIGINT` | `bigint` |
117
+ | `FLOAT` / `DOUBLE` | `float` |
118
+ | `DECIMAL(p,s)` | `decimal` with `decimal_places: s` |
119
+ | `DATE` | `date` |
120
+ | `DATETIME` / `TIMESTAMP` | `datetime` |
121
+ | `BOOLEAN` / `TINYINT(1)` | `boolean` |
122
+ | `ENUM(...)` | `enum` with `options` |
123
+ | Foreign key | `lookup` with `multiple: false` |
124
+ | Pure join table | Usually eliminate; use `lookup` with `multiple: true` on one side |
125
+ | Join table with business fields | Keep as an object with two lookup fields |
126
+
127
+ Semantic overrides:
128
+
129
+ - Column names containing `email` / `mail`: prefer `email`.
130
+ - Column names containing `phone` / `mobile` / `tel`: prefer `phone`.
131
+ - Column names containing `avatar` / `logo`: prefer `avatar`.
132
+ - Column names containing `region` / `province` / `city`: consider `region`, but ask before converting address text to region.
133
+
134
+ Unsupported SQL features:
135
+
136
+ - Stored procedures
137
+ - Triggers
138
+ - Views
139
+ - Composite primary keys
140
+ - Composite unique constraints
141
+ - CHECK constraints
142
+ - Partitioning
143
+
144
+ ## Confirmation Table
145
+
146
+ Before creating from SQL/ER input, present a table like this and wait for confirmation:
147
+
148
+ | SQL Table | aPaaS Object | Treatment |
149
+ | --- | --- | --- |
150
+ | customer | customer | Create |
151
+ | customer_tag | - | Convert pure join table into multi lookup |
152
+
153
+ | Object | SQL Column | SQL Type | aPaaS Type | API Name | Notes |
154
+ | --- | --- | --- | --- | --- | --- |
155
+ | customer | id | INT PK | - | - | Ignored; use `_id` |
156
+ | customer | status | ENUM | enum | status | Confirm labels and colors |
157
+ | order | customer_id | FK | lookup | customer | Target object must exist first |
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: apaas-shared
3
- description: "Use for aPaaS Node SDK shared setup and safety rules: client initialization, credentials, namespace, token cache, logger level, pagination defaults, retry expectations, write/delete confirmation, and error triage before using module-specific apaas skills."
3
+ description: "Use for aPaaS Node SDK shared setup and safety rules: client initialization, credentials, namespace, token cache, logger level, OpenAPI coverage, pagination defaults, retry expectations, write/delete confirmation, and error-code triage before using module-specific apaas skills."
4
4
  ---
5
5
 
6
6
  # aPaaS Shared
@@ -14,6 +14,8 @@ Use this skill before module-specific aPaaS SDK work.
14
14
  - Keep `clientId`, `clientSecret`, access tokens, and app secrets out of logs and final answers.
15
15
  - Prefer `client.setLoggerLevel(3)` for normal work, `4` for debugging, and `5` only when inspecting non-sensitive payloads.
16
16
  - Treat SDK responses with `code !== "0"` as failed even if the HTTP call succeeded.
17
+ - Read [references/openapi-coverage.md](references/openapi-coverage.md) when checking whether an API doc endpoint is already wrapped.
18
+ - Read [references/openapi-error-codes.md](references/openapi-error-codes.md) when triaging non-zero response codes.
17
19
 
18
20
  ```ts
19
21
  import { apaas } from "apaas-oapi-client";
@@ -39,8 +41,9 @@ client.setLoggerLevel(3);
39
41
  ## Module Routing
40
42
 
41
43
  - Object metadata and record CRUD: use `apaas-object`.
44
+ - OQL, cross-object search, constant objects, and datasets: use `apaas-object`.
42
45
  - Object and field schema changes: use `apaas-schema`.
43
- - Cloud functions and automation flows: use `apaas-function-flow`.
46
+ - Cloud functions, automation flows, workflow status, user tasks, and Lark integration tokens: use `apaas-function-flow`.
44
47
  - Builder page metadata and links: use `apaas-builder`.
45
48
  - Global options and variables: use `apaas-global`.
46
49
  - User/department ID exchange and attachments: use `apaas-exchange-attachment`.
@@ -1,4 +1,4 @@
1
1
  interface:
2
2
  display_name: "aPaaS Shared"
3
- short_description: "aPaaS SDK 认证、凭证、安全与通用调用规则指南"
4
- default_prompt: "Use $apaas-shared to initialize aPaaS SDK access safely."
3
+ short_description: "aPaaS SDK 认证、凭证、安全、OpenAPI 覆盖与错误码规则指南"
4
+ default_prompt: "Use $apaas-shared to initialize aPaaS SDK access and route OpenAPI/error-code work safely."
@@ -0,0 +1,33 @@
1
+ # OpenAPI Coverage
2
+
3
+ Source snapshot: API doc share for namespace `package_154107__c`, fetched on 2026-06-30. The left navigation contained 167 doc nodes and 72 unique current endpoints after normalizing namespace/object placeholders.
4
+
5
+ ## Current SDK Coverage
6
+
7
+ | Doc area | Endpoints | SDK entry |
8
+ | --- | --- | --- |
9
+ | Auth | `POST /auth/v1/appToken` | `client.init()` / token cache |
10
+ | Page | list/detail/link | `client.page.list`, `client.page.detail`, `client.page.url` |
11
+ | Object metadata | object list, object metadata, field metadata | `client.object.list*`, `client.object.metadata.*` |
12
+ | Object records | get/list/create/update/delete/batch | `client.object.search/create/update/delete.*` |
13
+ | Object other APIs | OQL, cross-object search | `client.object.oql`, `client.object.search.recordsAcrossObjects*` |
14
+ | Schema | batch create/update/delete objects | `client.schema.create/update/delete`, plus staged helpers |
15
+ | Constant objects | `_currency`, `_country`, `_timeZone` records and metadata | `client.constant.records`, `record`, `metadata.*` |
16
+ | Dataset | dataset list | `client.dataset.list`, `client.dataset.listWithIterator` |
17
+ | Attachments | files and images | `client.attachment.file.*`, `client.attachment.avatar.*` |
18
+ | Global config | global options and variables | `client.global.options.*`, `client.global.variables.*` |
19
+ | Workflow execution | async execution status, flow detail | `client.workflow.execution.status`, `client.workflow.definition.detail` |
20
+ | Workflow user tasks | task/instance list, detail, agree/reject/transfer/add assignee/cc/expedite/cancel/rollback/chat | `client.workflow.userTask.*` |
21
+ | Flow invocation | v1/v2 flow execute | `client.automation.v1.execute`, `client.automation.v2.execute` |
22
+ | Lark integration | default/custom app and tenant tokens | `client.integration.lark.*` |
23
+ | Feishu ID exchange | v2 user/department exchange | `client.user.*`, `client.department.*` |
24
+
25
+ ## Deprecated Docs
26
+
27
+ The doc tree also exposes `历史版本(不推荐)`. Do not add those endpoints as default SDK methods unless a user explicitly asks for legacy compatibility. Prefer current `/v1/data/...`, `/api/data/v1/...`, `/api/flow/v1/...`, and `/api/integration/v1/...` endpoints.
28
+
29
+ ## Validation Checklist
30
+
31
+ - Run `npm test -- --runInBand` after endpoint changes.
32
+ - Run `npm run build` so `dist/index.d.ts` exposes new methods.
33
+ - Run `npm pack --dry-run --json` and verify `skills/` plus `dist/` are included.
@@ -0,0 +1,55 @@
1
+ # OpenAPI Error Codes
2
+
3
+ All OpenAPI responses use `code`, `msg`, and often `data`. Treat `code !== "0"` as failed even when HTTP status is 200.
4
+
5
+ ```json
6
+ {
7
+ "code": "k_op_ec_10100",
8
+ "msg": "Request parameters are not valid: userId is empty"
9
+ }
10
+ ```
11
+
12
+ ## Families
13
+
14
+ The 2026-06-30 API doc bundle listed 531 error codes:
15
+
16
+ | Family | Prefix | Count | Typical area |
17
+ | --- | --- | ---: | --- |
18
+ | 通用 | `k_ec_` | 26 | generic parameter, permission, naming, unsupported |
19
+ | OpenAPI | `k_op_ec_` | 19 | auth, request parsing, rate limit, backend errors |
20
+ | 身份集成 | `k_ident_` | 38 | identity provider, user/account integration |
21
+ | 元数据 | `k_mt_ec_` | 329 | object/field/schema metadata, record constraints |
22
+ | 规则引擎 | `k_ru_ec_` | 24 | rule/formula/field picker validation |
23
+ | 流程 | `k_wf_ec_` | 70 | workflow definition, execution, user tasks |
24
+ | 权限 | `k_perm_ec_` | 10 | permission and security group errors |
25
+ | 图片/附件 | `k_img_ec_` | 15 | image/file upload, preview, storage |
26
+
27
+ ## High-Signal Codes
28
+
29
+ | Code | Meaning | First action |
30
+ | --- | --- | --- |
31
+ | `k_ec_000009` | 权限不足 | Verify app permission, namespace, and API credential scope. |
32
+ | `k_ec_000011` | 缺少必填参数 | Compare payload with SDK method params and doc body/query/path fields. |
33
+ | `k_ec_000012` | 必填参数为空 | Check empty strings, empty arrays, and missing nested fields. |
34
+ | `k_ec_000015` | 请求参数不合法 | Read `msg`; for schema writes also run `client.schema.validateResponse`. |
35
+ | `k_op_ec_10100` | 请求参数不合法 | Validate body shape and enum values. |
36
+ | `k_op_ec_10101` | 无法解析 body | Check JSON serialization and `Content-Type: application/json`. |
37
+ | `k_op_ec_10102` | URL 路径参数错误 | Check path params such as object, field, task, flow, or integration API names. |
38
+ | `k_op_ec_10201` | 鉴权失败 | Re-init client and verify `clientId/clientSecret`. |
39
+ | `k_op_ec_10203` | 无访问权限 | Check package role and API credential permission. |
40
+ | `k_op_ec_20003` | 请求被限流 | Reduce concurrency; prefer iterator helpers and ID cursor pagination. |
41
+ | `k_mt_ec_900064` | 字段被报表/页面/规则引用,无法删除 | Remove dependent page/rule/report references before schema delete. |
42
+ | `k_mt_ec_900075` | 一次最多创建或更新 1000 条记录 | Split record writes into chunks. |
43
+ | `k_wf_ec_2001002` | 缺少流程入参 | Re-read flow definition and required input params. |
44
+ | `k_wf_ec_2001005` | 流程实例 ID 不存在 | Verify `execution_id` or approval instance ID from list/status APIs. |
45
+ | `k_wf_ec_2001008` | 幂等键已使用 | Generate a new idempotency key before resubmit. |
46
+ | `k_perm_ec_000001` | 权限不足 | Do not retry blindly; fix permission first. |
47
+ | `k_img_ec_000004` | 附件超过 100M | Compress or split file before upload. |
48
+ | `k_img_ec_000011` | 文件 ID 错误 | Re-read upload response and avoid mixing image/file IDs. |
49
+
50
+ ## Triage Rules
51
+
52
+ - Do not rely on HTTP status alone.
53
+ - Log `code`, `msg`, and safe request identifiers; do not log secrets or access tokens.
54
+ - For batch APIs, inspect item-level status in `data.items` because the request can be partially successful.
55
+ - For schema writes, `code: "0"` with `data: null` is a silent failure and must be treated as failed.
@@ -7,6 +7,7 @@ This file summarizes the verified schema rules for field types in aPaaS.
7
7
  - Source object for metadata: `full_field_format`
8
8
  - Lookup target used in tests: `_user`
9
9
  - Reference field target used in tests: `_lark_user_id`
10
+ - Option color live check: `package_154107__c.object_test2.option_colors` on `2026-06-30`
10
11
 
11
12
  ## Why this file exists
12
13
 
@@ -66,6 +67,41 @@ Allowed option colors:
66
67
  - `blueMagenta`
67
68
  - `grey`
68
69
 
70
+ Use the colors in this order and cycle from the beginning when options exceed 10.
71
+
72
+ Live metadata for option fields returns camelCase keys:
73
+
74
+ - Type name: `option`
75
+ - Options path: `type.settings.optionList`
76
+ - Option source: `type.settings.optionSource`
77
+ - Global option API name: `type.settings.globalOptionAPIName`
78
+
79
+ Create/update schema payloads use snake_case keys:
80
+
81
+ - Type name: `enum`
82
+ - Options path: `type.settings.options`
83
+ - Option source: `type.settings.option_source`
84
+ - Global option API name: `type.settings.global_option_api_name`
85
+
86
+ OpenAPI write payloads require color short codes, while metadata returns color names. The SDK normalizes these metadata names before writing:
87
+
88
+ | Metadata color | OpenAPI write code |
89
+ |---|---|
90
+ | `blue` | `B` |
91
+ | `cyan` | `W` |
92
+ | `green` | `G` |
93
+ | `yellow` | `Y` |
94
+ | `orange` | `O` |
95
+ | `red` | `R` |
96
+ | `magenta` | `V` |
97
+ | `purple` | `P` |
98
+ | `blueMagenta` | `I` |
99
+ | `grey` | `N` |
100
+
101
+ Do not copy metadata `optionList` back into `schema.update`; convert it to `options`.
102
+
103
+ Use `getOptionColor(index)` from the SDK to assign metadata color names in this stable 10-color cycle. Use `getOptionColorCode(color)` only when constructing raw OpenAPI payloads outside the SDK normalizer.
104
+
69
105
  ## Batch update (`batch_update`) rules
70
106
 
71
107
  - `add`: use `operator: "add"` and send full field definition.
@@ -79,3 +115,5 @@ Allowed option colors:
79
115
  ## Canonical machine-readable source
80
116
 
81
117
  Use `/Users/Ethan/apaas/apaas-sdk/node-client/src/field-schema-rules.ts` as the source of truth for code.
118
+
119
+ The SDK exports `SQL_TYPE_TO_SCHEMA_TYPE`, `COLUMN_NAME_SEMANTIC_RULES`, and `SQL_CONSTRAINT_TO_SETTINGS` for SQL/ER conversion workflows.
@@ -94,6 +94,74 @@ export const OPTION_COLOR_LIST = [
94
94
  'grey'
95
95
  ] as const;
96
96
 
97
+ export type OptionColor = typeof OPTION_COLOR_LIST[number];
98
+
99
+ export const OPTION_COLOR_CODE_BY_NAME: Record<OptionColor, string> = {
100
+ blue: 'B',
101
+ cyan: 'W',
102
+ green: 'G',
103
+ yellow: 'Y',
104
+ orange: 'O',
105
+ red: 'R',
106
+ magenta: 'V',
107
+ purple: 'P',
108
+ blueMagenta: 'I',
109
+ grey: 'N'
110
+ } as const;
111
+
112
+ export const OPTION_COLOR_NAME_BY_CODE = Object.fromEntries(
113
+ Object.entries(OPTION_COLOR_CODE_BY_NAME).map(([name, code]) => [code, name])
114
+ ) as Record<string, OptionColor>;
115
+
116
+ export type OptionColorCode = typeof OPTION_COLOR_CODE_BY_NAME[OptionColor];
117
+
118
+ export function getOptionColor(index: number): OptionColor {
119
+ if (!Number.isInteger(index) || index < 0) {
120
+ throw new Error('Option color index must be a non-negative integer.');
121
+ }
122
+
123
+ return OPTION_COLOR_LIST[index % OPTION_COLOR_LIST.length];
124
+ }
125
+
126
+ export function getOptionColorCode(color: OptionColor): OptionColorCode {
127
+ return OPTION_COLOR_CODE_BY_NAME[color] as OptionColorCode;
128
+ }
129
+
130
+ export function normalizeOptionColorForSchema(color: unknown): unknown {
131
+ if (typeof color !== 'string') {
132
+ return color;
133
+ }
134
+
135
+ return OPTION_COLOR_CODE_BY_NAME[color as OptionColor] || color;
136
+ }
137
+
138
+ export const OPTION_COLOR_RULES = {
139
+ allowedColors: OPTION_COLOR_LIST,
140
+ assignment: 'Use OPTION_COLOR_LIST in order and cycle from the beginning when options exceed 10.',
141
+ verifiedBy: {
142
+ namespace: 'package_154107__c',
143
+ object: 'object_test2',
144
+ field: 'option_colors',
145
+ date: '2026-06-30'
146
+ },
147
+ metadataShape: {
148
+ typeName: 'option',
149
+ optionListPath: 'type.settings.optionList',
150
+ colorPath: 'type.settings.optionList[].color',
151
+ sourcePath: 'type.settings.optionSource',
152
+ globalOptionPath: 'type.settings.globalOptionAPIName'
153
+ },
154
+ createShape: {
155
+ typeName: 'enum',
156
+ optionsPath: 'type.settings.options',
157
+ colorPath: 'type.settings.options[].color',
158
+ colorInput: 'SDK accepts metadata color names and normalizes them to OpenAPI color codes before writing.',
159
+ colorCodeByName: OPTION_COLOR_CODE_BY_NAME,
160
+ sourcePath: 'type.settings.option_source',
161
+ globalOptionPath: 'type.settings.global_option_api_name'
162
+ }
163
+ } as const;
164
+
97
165
  export const FIELD_SCHEMA_RULES: FieldCreateRule[] = [
98
166
  {
99
167
  metadataType: 'text',
@@ -175,7 +243,7 @@ export const FIELD_SCHEMA_RULES: FieldCreateRule[] = [
175
243
  }
176
244
  ]
177
245
  },
178
- notes: `Do not send create type as \`option\`. Available option colors: ${OPTION_COLOR_LIST.join(', ')}.`
246
+ notes: `Do not send create type as \`option\`. Metadata returns optionList/optionSource/globalOptionAPIName; create/update expects options/option_source/global_option_api_name. The SDK accepts metadata color names and sends OpenAPI color codes (${Object.entries(OPTION_COLOR_CODE_BY_NAME).map(([name, code]) => `${name}=${code}`).join(', ')}).`
179
247
  },
180
248
  {
181
249
  metadataType: 'boolean',
@@ -324,6 +392,67 @@ export const SCHEMA_TYPE_MISMATCHES: Array<{
324
392
  schemaType: rule.schemaType
325
393
  }));
326
394
 
395
+ export interface SqlTypeMapping {
396
+ /** SQL type regex pattern, case-insensitive. */
397
+ sqlPattern: string;
398
+ /** Mapped aPaaS schema type. */
399
+ schemaType: SchemaFieldType;
400
+ /** Settings derivation rule. */
401
+ settingsMapping: string;
402
+ }
403
+
404
+ export const SQL_TYPE_TO_SCHEMA_TYPE: SqlTypeMapping[] = [
405
+ { sqlPattern: 'VARCHAR\\(\\d+\\)|CHAR\\(\\d+\\)', schemaType: 'text', settingsMapping: 'max_length from (n), multiline: false' },
406
+ { sqlPattern: 'TEXT|LONGTEXT|MEDIUMTEXT|TINYTEXT|CLOB', schemaType: 'text', settingsMapping: 'multiline: true, max_length: 100000' },
407
+ { sqlPattern: 'INT|INTEGER|BIGINT|SMALLINT|TINYINT(?!\\(1\\))|MEDIUMINT|SERIAL', schemaType: 'bigint', settingsMapping: 'required/unique from constraints' },
408
+ { sqlPattern: 'FLOAT|DOUBLE|REAL', schemaType: 'float', settingsMapping: 'decimal_places_number: 2' },
409
+ { sqlPattern: 'DECIMAL\\(\\d+,\\d+\\)|NUMERIC\\(\\d+,\\d+\\)', schemaType: 'decimal', settingsMapping: 'decimal_places from scale (s)' },
410
+ { sqlPattern: 'DATE', schemaType: 'date', settingsMapping: 'required from constraints' },
411
+ { sqlPattern: 'DATETIME|TIMESTAMP', schemaType: 'datetime', settingsMapping: 'required from constraints' },
412
+ { sqlPattern: 'BOOLEAN|BOOL|TINYINT\\(1\\)|BIT', schemaType: 'boolean', settingsMapping: 'default_value from DEFAULT' },
413
+ { sqlPattern: 'ENUM\\(.*\\)', schemaType: 'enum', settingsMapping: 'options from enum values, colors auto-assigned with getOptionColor(index)' },
414
+ { sqlPattern: 'BLOB|BINARY|VARBINARY|LONGBLOB|MEDIUMBLOB', schemaType: 'attachment', settingsMapping: 'any_type: true' },
415
+ { sqlPattern: 'JSON', schemaType: 'richText', settingsMapping: 'only when JSON stores rich text content' }
416
+ ];
417
+
418
+ export interface ColumnNameSemanticRule {
419
+ /** Column name regex pattern, case-insensitive. */
420
+ columnPattern: string;
421
+ /** Inferred aPaaS schema type. */
422
+ schemaType: SchemaFieldType;
423
+ /** Inference notes. */
424
+ notes: string;
425
+ }
426
+
427
+ export const COLUMN_NAME_SEMANTIC_RULES: ColumnNameSemanticRule[] = [
428
+ { columnPattern: '(^|_)(e?mail)(s?$|_)', schemaType: 'email', notes: 'Column name contains email/mail' },
429
+ { columnPattern: '(^|_)(phone|mobile|tel)(s?$|_)', schemaType: 'phone', notes: 'Column name contains phone/mobile/tel' },
430
+ { columnPattern: '(^|_)(avatar|logo|profile_image)(s?$|_)', schemaType: 'avatar', notes: 'Column name contains avatar/logo' },
431
+ { columnPattern: '(^|_)(region|province|city|district|address)(s?$|_)', schemaType: 'region', notes: 'Column name implies geographic data' }
432
+ ];
433
+
434
+ export interface SqlConstraintMapping {
435
+ /** SQL constraint. */
436
+ sqlConstraint: string;
437
+ /** Mapped aPaaS settings field. */
438
+ settingsField: string;
439
+ /** Mapped value. */
440
+ settingsValue: string;
441
+ /** Mapping notes. */
442
+ notes: string;
443
+ }
444
+
445
+ export const SQL_CONSTRAINT_TO_SETTINGS: SqlConstraintMapping[] = [
446
+ { sqlConstraint: 'NOT NULL', settingsField: 'required', settingsValue: 'true', notes: 'Maps to required: true' },
447
+ { sqlConstraint: 'UNIQUE', settingsField: 'unique', settingsValue: 'true', notes: 'Maps to unique: true' },
448
+ { sqlConstraint: 'PRIMARY KEY', settingsField: '-', settingsValue: '-', notes: 'Ignored: aPaaS uses system _id' },
449
+ { sqlConstraint: 'AUTO_INCREMENT', settingsField: '-', settingsValue: '-', notes: 'Ignored: aPaaS _id auto-increments. For business serial numbers, use auto_number' },
450
+ { sqlConstraint: 'FOREIGN KEY', settingsField: 'referenced_object_api_name', settingsValue: '(target table)', notes: 'Convert to lookup field' },
451
+ { sqlConstraint: 'DEFAULT', settingsField: 'default_value', settingsValue: '(value)', notes: 'Only boolean type supports default_value in aPaaS' },
452
+ { sqlConstraint: 'CHECK', settingsField: '-', settingsValue: '-', notes: 'Not supported in aPaaS, handle in application logic' },
453
+ { sqlConstraint: 'INDEX', settingsField: '-', settingsValue: '-', notes: 'Not applicable, aPaaS manages indexing automatically' }
454
+ ];
455
+
327
456
  export const BATCH_UPDATE_REQUIREMENTS = {
328
457
  add: 'Use operator=add with full field definition.',
329
458
  replace: 'Use operator=replace and include full `type` (name + settings). Label-only replace fails.',
@@ -92,8 +92,8 @@ export interface CreateObjectDefinition {
92
92
  label: MultilingualText;
93
93
  /** 对象设置(可选) */
94
94
  settings?: ObjectSettings;
95
- /** 字段列表(必填,至少包含一个自定义字段) */
96
- fields: CreateFieldDefinition[];
95
+ /** 字段列表(可选)。schema.create 会忽略 fields,安全做法是先建空壳,再用 schema.update 添加字段。 */
96
+ fields?: CreateFieldDefinition[];
97
97
  }
98
98
 
99
99
  /**