apaas-oapi-client 0.1.39 → 0.1.40

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/README.md CHANGED
@@ -51,7 +51,25 @@ mkdir -p "${CODEX_HOME:-$HOME/.codex}/skills"
51
51
  cp -R skills/apaas-* "${CODEX_HOME:-$HOME/.codex}/skills/"
52
52
  ```
53
53
 
54
- 通过 npxnpm 直接安装:
54
+ 通过官方 Skills CLI GitHub 安装:
55
+
56
+ ```bash
57
+ npx skills add https://github.com/ennann/apaas-oapi-node-client --skill apaas-object
58
+ ```
59
+
60
+ 安装全部 aPaaS Skills:
61
+
62
+ ```bash
63
+ npx skills add https://github.com/ennann/apaas-oapi-node-client --skill '*'
64
+ ```
65
+
66
+ 查看仓库内可用 Skills:
67
+
68
+ ```bash
69
+ npx skills add https://github.com/ennann/apaas-oapi-node-client --list
70
+ ```
71
+
72
+ 通过本包内置安装器从 npm 安装:
55
73
 
56
74
  ```bash
57
75
  npx apaas-oapi-client install-skills
@@ -18,6 +18,32 @@ export interface FieldCreateRule {
18
18
  }
19
19
  export declare const SCHEMA_TYPE_BY_METADATA_TYPE: Record<MetadataFieldType, SchemaFieldType>;
20
20
  export declare const OPTION_COLOR_LIST: readonly ["blue", "cyan", "green", "yellow", "orange", "red", "magenta", "purple", "blueMagenta", "grey"];
21
+ export type OptionColor = typeof OPTION_COLOR_LIST[number];
22
+ export declare function getOptionColor(index: number): OptionColor;
23
+ export declare const OPTION_COLOR_RULES: {
24
+ readonly allowedColors: readonly ["blue", "cyan", "green", "yellow", "orange", "red", "magenta", "purple", "blueMagenta", "grey"];
25
+ readonly assignment: "Use OPTION_COLOR_LIST in order and cycle from the beginning when options exceed 10.";
26
+ readonly verifiedBy: {
27
+ readonly namespace: "package_154107__c";
28
+ readonly object: "object_test2";
29
+ readonly field: "option_colors";
30
+ readonly date: "2026-06-30";
31
+ };
32
+ readonly metadataShape: {
33
+ readonly typeName: "option";
34
+ readonly optionListPath: "type.settings.optionList";
35
+ readonly colorPath: "type.settings.optionList[].color";
36
+ readonly sourcePath: "type.settings.optionSource";
37
+ readonly globalOptionPath: "type.settings.globalOptionAPIName";
38
+ };
39
+ readonly createShape: {
40
+ readonly typeName: "enum";
41
+ readonly optionsPath: "type.settings.options";
42
+ readonly colorPath: "type.settings.options[].color";
43
+ readonly sourcePath: "type.settings.option_source";
44
+ readonly globalOptionPath: "type.settings.global_option_api_name";
45
+ };
46
+ };
21
47
  export declare const FIELD_SCHEMA_RULES: FieldCreateRule[];
22
48
  export declare const SCHEMA_TYPE_MISMATCHES: Array<{
23
49
  metadataType: MetadataFieldType;
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { LoggerLevel } from './logger';
2
2
  import { FIELD_TYPES, SYSTEM_FIELDS, LANGUAGE_CODES, SCHEMA_GUIDELINES, CREATE_OBJECT_EXAMPLE, UPDATE_OBJECT_ADD_FIELD_EXAMPLE, UPDATE_OBJECT_REPLACE_FIELD_EXAMPLE, UPDATE_OBJECT_REMOVE_FIELD_EXAMPLE, UPDATE_OBJECT_SETTINGS_EXAMPLE, getSystemFields, getCustomFieldTypes, getFieldType, isSystemField, createMultilingualText, extractMultilingualText, type FieldTypeMetadata, type MultilingualText, type ObjectSettings, type FieldTypeDefinition, type EncryptType, type FieldOperator, type CreateFieldDefinition, type UpdateFieldDefinition, type CreateObjectDefinition, type UpdateObjectDefinition } from './field-types';
3
+ import { SCHEMA_TYPE_BY_METADATA_TYPE, OPTION_COLOR_LIST, OPTION_COLOR_RULES, FIELD_SCHEMA_RULES, SCHEMA_TYPE_MISMATCHES, BATCH_UPDATE_REQUIREMENTS, getOptionColor, type MetadataFieldType, type SchemaFieldType, type FieldCreateRule, type OptionColor } from './field-schema-rules';
3
4
  /**
4
5
  * 批量操作结果
5
6
  */
@@ -803,5 +804,5 @@ declare class Client {
803
804
  export declare const apaas: {
804
805
  Client: typeof Client;
805
806
  };
806
- export type { BatchResult, RetryOptions, FieldTypeMetadata, MultilingualText, ObjectSettings, FieldTypeDefinition, EncryptType, FieldOperator, CreateFieldDefinition, UpdateFieldDefinition, CreateObjectDefinition, UpdateObjectDefinition };
807
- export { FIELD_TYPES, SYSTEM_FIELDS, LANGUAGE_CODES, SCHEMA_GUIDELINES, CREATE_OBJECT_EXAMPLE, UPDATE_OBJECT_ADD_FIELD_EXAMPLE, UPDATE_OBJECT_REPLACE_FIELD_EXAMPLE, UPDATE_OBJECT_REMOVE_FIELD_EXAMPLE, UPDATE_OBJECT_SETTINGS_EXAMPLE, getSystemFields, getCustomFieldTypes, getFieldType, isSystemField, createMultilingualText, extractMultilingualText };
807
+ export type { BatchResult, RetryOptions, FieldTypeMetadata, MultilingualText, ObjectSettings, FieldTypeDefinition, EncryptType, FieldOperator, CreateFieldDefinition, UpdateFieldDefinition, CreateObjectDefinition, UpdateObjectDefinition, MetadataFieldType, SchemaFieldType, FieldCreateRule, OptionColor };
808
+ export { FIELD_TYPES, SYSTEM_FIELDS, LANGUAGE_CODES, SCHEMA_GUIDELINES, CREATE_OBJECT_EXAMPLE, UPDATE_OBJECT_ADD_FIELD_EXAMPLE, UPDATE_OBJECT_REPLACE_FIELD_EXAMPLE, UPDATE_OBJECT_REMOVE_FIELD_EXAMPLE, UPDATE_OBJECT_SETTINGS_EXAMPLE, getSystemFields, getCustomFieldTypes, getFieldType, isSystemField, createMultilingualText, extractMultilingualText, SCHEMA_TYPE_BY_METADATA_TYPE, OPTION_COLOR_LIST, OPTION_COLOR_RULES, FIELD_SCHEMA_RULES, SCHEMA_TYPE_MISMATCHES, BATCH_UPDATE_REQUIREMENTS, getOptionColor };
package/dist/index.js CHANGED
@@ -735,6 +735,315 @@ function extractMultilingualText(multilingualText, languageCode = LANGUAGE_CODES
735
735
  return found ? found.text : ((_a = multilingualText[0]) === null || _a === void 0 ? void 0 : _a.text) || '';
736
736
  }
737
737
 
738
+ /**
739
+ * Canonical field rules for schema.create/schema.update.
740
+ * Verified in namespace `package_5dc5b7__c` on 2026-02-10.
741
+ *
742
+ * Notes:
743
+ * - `metadataType` is usually the type name returned by object.metadata.fields.
744
+ * - Some entries are rule extensions for practical schema usage (`text_multiline`, `lookup_multi`).
745
+ * - `schemaType` is the type name that should be sent to schema.create/update.
746
+ */
747
+ const SCHEMA_TYPE_BY_METADATA_TYPE = {
748
+ text: 'text',
749
+ text_multiline: 'text',
750
+ bigint: 'bigint',
751
+ number: 'float',
752
+ date: 'date',
753
+ datetime: 'datetime',
754
+ option: 'enum',
755
+ boolean: 'boolean',
756
+ lookup: 'lookup',
757
+ lookup_multi: 'lookup',
758
+ referenceField: 'reference_field',
759
+ file: 'attachment',
760
+ autoId: 'auto_number',
761
+ richText: 'richText',
762
+ mobileNumber: 'phone',
763
+ avatarOrLogo: 'avatar',
764
+ email: 'email',
765
+ region: 'region',
766
+ decimal: 'decimal',
767
+ multilingual: 'multilingual'
768
+ };
769
+ const OPTION_COLOR_LIST = [
770
+ 'blue',
771
+ 'cyan',
772
+ 'green',
773
+ 'yellow',
774
+ 'orange',
775
+ 'red',
776
+ 'magenta',
777
+ 'purple',
778
+ 'blueMagenta',
779
+ 'grey'
780
+ ];
781
+ function getOptionColor(index) {
782
+ if (!Number.isInteger(index) || index < 0) {
783
+ throw new Error('Option color index must be a non-negative integer.');
784
+ }
785
+ return OPTION_COLOR_LIST[index % OPTION_COLOR_LIST.length];
786
+ }
787
+ const OPTION_COLOR_RULES = {
788
+ allowedColors: OPTION_COLOR_LIST,
789
+ assignment: 'Use OPTION_COLOR_LIST in order and cycle from the beginning when options exceed 10.',
790
+ verifiedBy: {
791
+ namespace: 'package_154107__c',
792
+ object: 'object_test2',
793
+ field: 'option_colors',
794
+ date: '2026-06-30'
795
+ },
796
+ metadataShape: {
797
+ typeName: 'option',
798
+ optionListPath: 'type.settings.optionList',
799
+ colorPath: 'type.settings.optionList[].color',
800
+ sourcePath: 'type.settings.optionSource',
801
+ globalOptionPath: 'type.settings.globalOptionAPIName'
802
+ },
803
+ createShape: {
804
+ typeName: 'enum',
805
+ optionsPath: 'type.settings.options',
806
+ colorPath: 'type.settings.options[].color',
807
+ sourcePath: 'type.settings.option_source',
808
+ globalOptionPath: 'type.settings.global_option_api_name'
809
+ }
810
+ };
811
+ const FIELD_SCHEMA_RULES = [
812
+ {
813
+ metadataType: 'text',
814
+ schemaType: 'text',
815
+ settingsExample: {
816
+ required: false,
817
+ unique: false,
818
+ case_sensitive: false,
819
+ multiline: false,
820
+ max_length: 255
821
+ }
822
+ },
823
+ {
824
+ metadataType: 'text_multiline',
825
+ schemaType: 'text',
826
+ settingsExample: {
827
+ required: false,
828
+ unique: false,
829
+ case_sensitive: false,
830
+ multiline: true,
831
+ max_length: 100000
832
+ }
833
+ },
834
+ {
835
+ metadataType: 'bigint',
836
+ schemaType: 'bigint',
837
+ settingsExample: {
838
+ required: false,
839
+ unique: false
840
+ }
841
+ },
842
+ {
843
+ metadataType: 'number',
844
+ schemaType: 'float',
845
+ settingsExample: {
846
+ required: false,
847
+ unique: false,
848
+ display_as_percentage: false,
849
+ decimal_places_number: 2
850
+ },
851
+ notes: 'Do not send create type as `number`.'
852
+ },
853
+ {
854
+ metadataType: 'date',
855
+ schemaType: 'date',
856
+ settingsExample: {
857
+ required: false
858
+ }
859
+ },
860
+ {
861
+ metadataType: 'datetime',
862
+ schemaType: 'datetime',
863
+ settingsExample: {
864
+ required: false
865
+ }
866
+ },
867
+ {
868
+ metadataType: 'option',
869
+ schemaType: 'enum',
870
+ settingsExample: {
871
+ required: false,
872
+ multiple: false,
873
+ option_source: 'custom',
874
+ global_option_api_name: '',
875
+ options: [
876
+ {
877
+ label: { zh_cn: 'Option One', en_us: 'Option One' },
878
+ api_name: 'option_one',
879
+ description: null,
880
+ color: 'blue',
881
+ active: true
882
+ },
883
+ {
884
+ label: { zh_cn: 'Option Two', en_us: 'Option Two' },
885
+ api_name: 'option_two',
886
+ description: null,
887
+ color: 'green',
888
+ active: true
889
+ }
890
+ ]
891
+ },
892
+ notes: `Do not send create type as \`option\`. Metadata returns optionList/optionSource/globalOptionAPIName; create/update expects options/option_source/global_option_api_name. Available option colors: ${OPTION_COLOR_LIST.join(', ')}.`
893
+ },
894
+ {
895
+ metadataType: 'boolean',
896
+ schemaType: 'boolean',
897
+ settingsExample: {
898
+ default_value: true,
899
+ description_if_true: { zh_cn: '1', en_us: '1' },
900
+ description_if_false: { zh_cn: '0', en_us: '0' }
901
+ }
902
+ },
903
+ {
904
+ metadataType: 'lookup',
905
+ schemaType: 'lookup',
906
+ settingsExample: {
907
+ required: false,
908
+ multiple: false,
909
+ referenced_object_api_name: '_user',
910
+ display_as_tree: false,
911
+ display_style: 'select'
912
+ },
913
+ dependsOn: ['Target object must exist first: `_user`'],
914
+ notes: 'Single-value lookup (`multiple: false`), can be used by `reference_field`.'
915
+ },
916
+ {
917
+ metadataType: 'lookup_multi',
918
+ schemaType: 'lookup',
919
+ settingsExample: {
920
+ required: false,
921
+ multiple: true,
922
+ referenced_object_api_name: '_user',
923
+ display_as_tree: false,
924
+ display_style: 'select'
925
+ },
926
+ dependsOn: ['Target object must exist first: `_user`'],
927
+ notes: 'Multi-value lookup (`multiple: true`) cannot be used by `reference_field`.'
928
+ },
929
+ {
930
+ metadataType: 'referenceField',
931
+ schemaType: 'reference_field',
932
+ settingsExample: {
933
+ current_lookup_field_api_name: 'lookup_835c2a2457b',
934
+ target_reference_field_api_name: '_lark_user_id'
935
+ },
936
+ dependsOn: [
937
+ 'A single-value lookup field must exist first in the same object',
938
+ 'The target field must exist in the lookup target object'
939
+ ],
940
+ notes: 'Guide field must be single lookup (`multiple: false`).'
941
+ },
942
+ {
943
+ metadataType: 'file',
944
+ schemaType: 'attachment',
945
+ settingsExample: {
946
+ required: false,
947
+ any_type: true,
948
+ max_uploaded_num: 10,
949
+ mime_types: []
950
+ },
951
+ notes: 'Do not send create type as `file`.'
952
+ },
953
+ {
954
+ metadataType: 'autoId',
955
+ schemaType: 'auto_number',
956
+ settingsExample: {
957
+ generation_method: 'random',
958
+ digits: 1,
959
+ prefix: '',
960
+ suffix: '',
961
+ start_at: '1'
962
+ },
963
+ notes: 'Do not send create type as `autoId`.'
964
+ },
965
+ {
966
+ metadataType: 'richText',
967
+ schemaType: 'richText',
968
+ settingsExample: {
969
+ required: false,
970
+ max_length: 1000
971
+ }
972
+ },
973
+ {
974
+ metadataType: 'mobileNumber',
975
+ schemaType: 'phone',
976
+ settingsExample: {
977
+ required: false,
978
+ unique: false
979
+ },
980
+ notes: 'Do not send create type as `mobileNumber`.'
981
+ },
982
+ {
983
+ metadataType: 'avatarOrLogo',
984
+ schemaType: 'avatar',
985
+ settingsExample: {
986
+ display_style: 'square'
987
+ },
988
+ notes: 'Do not send create type as `avatarOrLogo`.'
989
+ },
990
+ {
991
+ metadataType: 'email',
992
+ schemaType: 'email',
993
+ settingsExample: {
994
+ required: false,
995
+ unique: false
996
+ }
997
+ },
998
+ {
999
+ metadataType: 'region',
1000
+ schemaType: 'region',
1001
+ settingsExample: {
1002
+ required: false,
1003
+ multiple: false,
1004
+ has_level_strict: true,
1005
+ strict_level: 4
1006
+ }
1007
+ },
1008
+ {
1009
+ metadataType: 'decimal',
1010
+ schemaType: 'decimal',
1011
+ settingsExample: {
1012
+ required: false,
1013
+ unique: false,
1014
+ display_as_percentage: false,
1015
+ decimal_places: 2
1016
+ }
1017
+ },
1018
+ {
1019
+ metadataType: 'multilingual',
1020
+ schemaType: 'multilingual',
1021
+ settingsExample: {
1022
+ required: false,
1023
+ unique: false,
1024
+ case_sensitive: false,
1025
+ multiline: false,
1026
+ max_length: 1000
1027
+ }
1028
+ }
1029
+ ];
1030
+ const SCHEMA_TYPE_MISMATCHES = FIELD_SCHEMA_RULES
1031
+ .filter((rule) => rule.metadataType !== rule.schemaType)
1032
+ .map((rule) => ({
1033
+ metadataType: rule.metadataType,
1034
+ schemaType: rule.schemaType
1035
+ }));
1036
+ const BATCH_UPDATE_REQUIREMENTS = {
1037
+ add: 'Use operator=add with full field definition.',
1038
+ replace: 'Use operator=replace and include full `type` (name + settings). Label-only replace fails.',
1039
+ remove: 'Use operator=remove with api_name only.',
1040
+ dependencyOrder: {
1041
+ add: ['lookup/lookup_multi before reference_field'],
1042
+ remove: ['reference_field before lookup/lookup_multi']
1043
+ },
1044
+ referenceFieldConstraint: 'reference_field only works with single lookup (`multiple: false`).'
1045
+ };
1046
+
738
1047
  /**
739
1048
  * 判断错误是否可重试
740
1049
  */
@@ -2744,10 +3053,16 @@ const apaas = {
2744
3053
  Client
2745
3054
  };
2746
3055
 
3056
+ exports.BATCH_UPDATE_REQUIREMENTS = BATCH_UPDATE_REQUIREMENTS;
2747
3057
  exports.CREATE_OBJECT_EXAMPLE = CREATE_OBJECT_EXAMPLE;
3058
+ exports.FIELD_SCHEMA_RULES = FIELD_SCHEMA_RULES;
2748
3059
  exports.FIELD_TYPES = FIELD_TYPES;
2749
3060
  exports.LANGUAGE_CODES = LANGUAGE_CODES;
3061
+ exports.OPTION_COLOR_LIST = OPTION_COLOR_LIST;
3062
+ exports.OPTION_COLOR_RULES = OPTION_COLOR_RULES;
2750
3063
  exports.SCHEMA_GUIDELINES = SCHEMA_GUIDELINES;
3064
+ exports.SCHEMA_TYPE_BY_METADATA_TYPE = SCHEMA_TYPE_BY_METADATA_TYPE;
3065
+ exports.SCHEMA_TYPE_MISMATCHES = SCHEMA_TYPE_MISMATCHES;
2751
3066
  exports.SYSTEM_FIELDS = SYSTEM_FIELDS;
2752
3067
  exports.UPDATE_OBJECT_ADD_FIELD_EXAMPLE = UPDATE_OBJECT_ADD_FIELD_EXAMPLE;
2753
3068
  exports.UPDATE_OBJECT_REMOVE_FIELD_EXAMPLE = UPDATE_OBJECT_REMOVE_FIELD_EXAMPLE;
@@ -2758,5 +3073,6 @@ exports.createMultilingualText = createMultilingualText;
2758
3073
  exports.extractMultilingualText = extractMultilingualText;
2759
3074
  exports.getCustomFieldTypes = getCustomFieldTypes;
2760
3075
  exports.getFieldType = getFieldType;
3076
+ exports.getOptionColor = getOptionColor;
2761
3077
  exports.getSystemFields = getSystemFields;
2762
3078
  exports.isSystemField = isSystemField;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apaas-oapi-client",
3
- "version": "0.1.39",
3
+ "version": "0.1.40",
4
4
  "main": "dist/index.js",
5
5
  "bin": {
6
6
  "apaas-oapi-client": "./bin/apaas-oapi-client.js"
@@ -7,40 +7,35 @@ description: "Use for aPaaS Node SDK schema management: creating objects, updati
7
7
 
8
8
  Use this skill for `client.schema.*`.
9
9
 
10
- ## Required Reference
10
+ ## Required References
11
11
 
12
- Before creating or updating fields, read `references/field-schema-rules.md`. It contains the verified metadata-to-create type mapping and dependency order.
12
+ - Before creating or updating fields, read `references/field-schema-rules.md`. It contains the verified metadata-to-create type mapping and option color rules.
13
+ - For whole-app object maintenance, SQL/ER conversion, multi-object dependencies, or delete/rebuild workflows, read `references/schema-maintenance-sop.md`.
13
14
 
14
15
  ## Workflow
15
16
 
16
17
  1. Use `apaas-shared` to initialize the client.
17
18
  2. Read current objects with `client.object.listWithIterator()`.
18
19
  3. Read existing fields with `client.object.metadata.fields({ object_name })`.
19
- 4. Build the smallest schema payload that matches the requested change.
20
- 5. For dependency fields, create target objects first, then lookup fields, then reference fields.
21
- 6. After the call, re-read metadata to verify the effective schema.
20
+ 4. For new objects, create shells first, then add fields with `schema.update`.
21
+ 5. For dependency fields, create base fields first, then lookup fields, then reference fields.
22
+ 6. Check request-level, silent-failure, and item-level response status.
23
+ 7. After the call, re-read metadata to verify the effective schema.
22
24
 
23
25
  ## Create Object
24
26
 
27
+ Create objects as shells first. Do not rely on `schema.create({ fields })` to create fields.
28
+
25
29
  ```ts
26
30
  await client.schema.create({
27
31
  objects: [{
28
32
  api_name: "product",
29
33
  label: { zh_cn: "产品", en_us: "Product" },
30
34
  settings: {
31
- display_name: "name",
32
- allow_search_fields: ["_id", "code", "name"],
33
- search_layout: ["code", "name"]
34
- },
35
- fields: [{
36
- api_name: "code",
37
- label: { zh_cn: "产品编号", en_us: "Product Code" },
38
- type: {
39
- name: "text",
40
- settings: { required: true, unique: true, max_length: 50 }
41
- },
42
- encrypt_type: "none"
43
- }]
35
+ display_name: "_id",
36
+ allow_search_fields: ["_id"],
37
+ search_layout: []
38
+ }
44
39
  }]
45
40
  });
46
41
  ```
@@ -5,6 +5,7 @@ Use this reference before `client.schema.create` or `client.schema.update` field
5
5
  - Verified source in this repo: `src/FIELD_SCHEMA_RULES.md`
6
6
  - Machine-readable source: `src/field-schema-rules.ts`
7
7
  - Verification date in source: `2026-02-10`
8
+ - Option color live check: `package_154107__c.object_test2.option_colors` on `2026-06-30`
8
9
 
9
10
  ## Metadata To Create Type Mapping
10
11
 
@@ -61,6 +62,52 @@ Allowed option colors:
61
62
  - `blueMagenta`
62
63
  - `grey`
63
64
 
65
+ Use the colors in this order and cycle from the beginning when options exceed 10. The live `object_test2.option_colors` check returned colors 1-12 as:
66
+
67
+ ```text
68
+ blue, cyan, green, yellow, orange, red, magenta, purple, blueMagenta, grey, blue, cyan
69
+ ```
70
+
71
+ ### Option Metadata Shape vs Create Shape
72
+
73
+ `object.metadata.fields` returns option fields in metadata shape:
74
+
75
+ ```ts
76
+ {
77
+ type: {
78
+ name: "option",
79
+ settings: {
80
+ optionSource: "custom",
81
+ globalOptionAPIName: "",
82
+ optionList: [
83
+ { apiName: "option_x", color: "blue", active: true, label: [{ language_code: 2052, text: "1" }] }
84
+ ]
85
+ }
86
+ }
87
+ }
88
+ ```
89
+
90
+ `schema.create` / `schema.update` must use create shape:
91
+
92
+ ```ts
93
+ {
94
+ type: {
95
+ name: "enum",
96
+ settings: {
97
+ option_source: "custom",
98
+ global_option_api_name: "",
99
+ options: [
100
+ { api_name: "option_x", color: "blue", active: true, label: { zh_cn: "1", en_us: "1" } }
101
+ ]
102
+ }
103
+ }
104
+ }
105
+ ```
106
+
107
+ Do not copy metadata `optionList` back into `schema.update`; convert it to `options`.
108
+
109
+ Use `getOptionColor(index)` from the SDK to assign colors in this stable 10-color cycle.
110
+
64
111
  ## Minimal Lookup Example
65
112
 
66
113
  ```ts
@@ -0,0 +1,181 @@
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 schema call:
17
+
18
+ ```ts
19
+ function checkSchemaResponse(result: any, context: string) {
20
+ if (result.code !== "0") {
21
+ throw new Error(`${context} request failed: ${result.code} ${result.msg || ""}`);
22
+ }
23
+
24
+ if (result.data === null) {
25
+ throw new Error(`${context} silently failed: result.code is 0 but result.data is null`);
26
+ }
27
+
28
+ const failed = (result.data?.items || []).filter((item: any) => item.status?.code !== "0");
29
+ if (failed.length > 0) {
30
+ throw new Error(`${context} item failures: ${JSON.stringify(failed)}`);
31
+ }
32
+ }
33
+ ```
34
+
35
+ `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`.
36
+
37
+ ## Create Objects: Three Stages
38
+
39
+ Do not create a full object with fields in one call. Use this sequence even when there is no obvious dependency cycle.
40
+
41
+ 1. Stage 1a: create object shells only.
42
+ 2. Stage 1b: add base fields with `schema.update` and `operator: "add"`.
43
+ 3. Stage 2: add `lookup` / `lookup_multi` fields after all target objects exist.
44
+ 4. Stage 3: add `reference_field` after its single-value lookup exists.
45
+ 5. Final: update `display_name`, `allow_search_fields`, and `search_layout` after fields exist.
46
+
47
+ ```ts
48
+ await client.schema.create({
49
+ objects: [{
50
+ api_name: "customer",
51
+ label: { zh_cn: "客户", en_us: "Customer" },
52
+ settings: { display_name: "_id", allow_search_fields: ["_id"], search_layout: [] }
53
+ }]
54
+ });
55
+ ```
56
+
57
+ ```ts
58
+ await client.schema.update({
59
+ objects: [{
60
+ api_name: "customer",
61
+ fields: [{
62
+ operator: "add",
63
+ api_name: "name",
64
+ label: { zh_cn: "客户名称", en_us: "Name" },
65
+ type: {
66
+ name: "text",
67
+ settings: {
68
+ required: true,
69
+ unique: false,
70
+ case_sensitive: false,
71
+ multiline: false,
72
+ max_length: 100
73
+ }
74
+ },
75
+ encrypt_type: "none"
76
+ }]
77
+ }]
78
+ });
79
+ ```
80
+
81
+ ## Update Fields
82
+
83
+ - Add: send `operator: "add"` and the full field definition.
84
+ - Replace: send `operator: "replace"` and the full `type.name + type.settings`; label-only replace fails.
85
+ - Remove: send `operator: "remove"` and only `api_name`.
86
+ - Skip existing fields when rerunning idempotently.
87
+
88
+ ```ts
89
+ async function addFieldsIdempotent(client: any, objectName: string, fieldsToAdd: any[]) {
90
+ const metadata = await client.object.metadata.fields({ object_name: objectName });
91
+ const existingNames = new Set((metadata.data?.fields || []).map((field: any) => field.apiName));
92
+ const newFields = fieldsToAdd.filter(field => !existingNames.has(field.api_name));
93
+ if (newFields.length === 0) return;
94
+ const result = await client.schema.update({ objects: [{ api_name: objectName, fields: newFields }] });
95
+ checkSchemaResponse(result, `addFields(${objectName})`);
96
+ }
97
+ ```
98
+
99
+ ## Dependency Order
100
+
101
+ Add order:
102
+
103
+ 1. Object shells
104
+ 2. Base fields
105
+ 3. `lookup` / `lookup_multi`
106
+ 4. `reference_field`
107
+
108
+ Remove order:
109
+
110
+ 1. `reference_field`
111
+ 2. `lookup` / `lookup_multi`
112
+ 3. Other custom fields
113
+ 4. Custom objects
114
+
115
+ Never add a lookup and a reference field that depends on it in the same `schema.update` batch; execution order is not guaranteed.
116
+
117
+ ## Delete All Custom Objects
118
+
119
+ Use this flow for environment cleanup or model rebuilds:
120
+
121
+ 1. List objects with `client.object.listWithIterator()`.
122
+ 2. Filter out system objects whose API name starts with `_`.
123
+ 3. Read fields for each custom object.
124
+ 4. Remove `referenceField` fields grouped by object.
125
+ 5. Remove `lookup` fields grouped by object.
126
+ 6. Delete custom objects in batches of 10.
127
+ 7. Re-list objects to verify deletion.
128
+
129
+ ## SQL / ER To aPaaS Mapping
130
+
131
+ Map relational designs into aPaaS objects only after presenting a conversion table for user confirmation.
132
+
133
+ | SQL Concept | aPaaS Mapping |
134
+ | --- | --- |
135
+ | Table | Object |
136
+ | Primary key `id` | Ignore; aPaaS provides `_id` |
137
+ | `created_at` / `updated_at` audit columns | Usually ignore; aPaaS provides system fields |
138
+ | `VARCHAR(n)` / `CHAR(n)` | `text` with `max_length: n`, `multiline: false` |
139
+ | `TEXT` / long text | `text` with `multiline: true`, `max_length: 100000` |
140
+ | `INT` / `INTEGER` / `BIGINT` | `bigint` |
141
+ | `FLOAT` / `DOUBLE` | `float` |
142
+ | `DECIMAL(p,s)` | `decimal` with `decimal_places: s` |
143
+ | `DATE` | `date` |
144
+ | `DATETIME` / `TIMESTAMP` | `datetime` |
145
+ | `BOOLEAN` / `TINYINT(1)` | `boolean` |
146
+ | `ENUM(...)` | `enum` with `options` |
147
+ | Foreign key | `lookup` with `multiple: false` |
148
+ | Pure join table | Usually eliminate; use `lookup` with `multiple: true` on one side |
149
+ | Join table with business fields | Keep as an object with two lookup fields |
150
+
151
+ Semantic overrides:
152
+
153
+ - Column names containing `email` / `mail`: prefer `email`.
154
+ - Column names containing `phone` / `mobile` / `tel`: prefer `phone`.
155
+ - Column names containing `avatar` / `logo`: prefer `avatar`.
156
+ - Column names containing `region` / `province` / `city`: consider `region`, but ask before converting address text to region.
157
+
158
+ Unsupported SQL features:
159
+
160
+ - Stored procedures
161
+ - Triggers
162
+ - Views
163
+ - Composite primary keys
164
+ - Composite unique constraints
165
+ - CHECK constraints
166
+ - Partitioning
167
+
168
+ ## Confirmation Table
169
+
170
+ Before creating from SQL/ER input, present a table like this and wait for confirmation:
171
+
172
+ | SQL Table | aPaaS Object | Treatment |
173
+ | --- | --- | --- |
174
+ | customer | customer | Create |
175
+ | customer_tag | - | Convert pure join table into multi lookup |
176
+
177
+ | Object | SQL Column | SQL Type | aPaaS Type | API Name | Notes |
178
+ | --- | --- | --- | --- | --- | --- |
179
+ | customer | id | INT PK | - | - | Ignored; use `_id` |
180
+ | customer | status | ENUM | enum | status | Confirm labels and colors |
181
+ | order | customer_id | FK | lookup | customer | Target object must exist first |
@@ -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,26 @@ 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
+ Do not copy metadata `optionList` back into `schema.update`; convert it to `options`.
87
+
88
+ Use `getOptionColor(index)` from the SDK to assign colors in this stable 10-color cycle.
89
+
69
90
  ## Batch update (`batch_update`) rules
70
91
 
71
92
  - `add`: use `operator: "add"` and send full field definition.
@@ -94,6 +94,41 @@ 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 function getOptionColor(index: number): OptionColor {
100
+ if (!Number.isInteger(index) || index < 0) {
101
+ throw new Error('Option color index must be a non-negative integer.');
102
+ }
103
+
104
+ return OPTION_COLOR_LIST[index % OPTION_COLOR_LIST.length];
105
+ }
106
+
107
+ export const OPTION_COLOR_RULES = {
108
+ allowedColors: OPTION_COLOR_LIST,
109
+ assignment: 'Use OPTION_COLOR_LIST in order and cycle from the beginning when options exceed 10.',
110
+ verifiedBy: {
111
+ namespace: 'package_154107__c',
112
+ object: 'object_test2',
113
+ field: 'option_colors',
114
+ date: '2026-06-30'
115
+ },
116
+ metadataShape: {
117
+ typeName: 'option',
118
+ optionListPath: 'type.settings.optionList',
119
+ colorPath: 'type.settings.optionList[].color',
120
+ sourcePath: 'type.settings.optionSource',
121
+ globalOptionPath: 'type.settings.globalOptionAPIName'
122
+ },
123
+ createShape: {
124
+ typeName: 'enum',
125
+ optionsPath: 'type.settings.options',
126
+ colorPath: 'type.settings.options[].color',
127
+ sourcePath: 'type.settings.option_source',
128
+ globalOptionPath: 'type.settings.global_option_api_name'
129
+ }
130
+ } as const;
131
+
97
132
  export const FIELD_SCHEMA_RULES: FieldCreateRule[] = [
98
133
  {
99
134
  metadataType: 'text',
@@ -175,7 +210,7 @@ export const FIELD_SCHEMA_RULES: FieldCreateRule[] = [
175
210
  }
176
211
  ]
177
212
  },
178
- notes: `Do not send create type as \`option\`. Available option colors: ${OPTION_COLOR_LIST.join(', ')}.`
213
+ notes: `Do not send create type as \`option\`. Metadata returns optionList/optionSource/globalOptionAPIName; create/update expects options/option_source/global_option_api_name. Available option colors: ${OPTION_COLOR_LIST.join(', ')}.`
179
214
  },
180
215
  {
181
216
  metadataType: 'boolean',
package/src/index.ts CHANGED
@@ -29,6 +29,19 @@ import {
29
29
  type CreateObjectDefinition,
30
30
  type UpdateObjectDefinition
31
31
  } from './field-types';
32
+ import {
33
+ SCHEMA_TYPE_BY_METADATA_TYPE,
34
+ OPTION_COLOR_LIST,
35
+ OPTION_COLOR_RULES,
36
+ FIELD_SCHEMA_RULES,
37
+ SCHEMA_TYPE_MISMATCHES,
38
+ BATCH_UPDATE_REQUIREMENTS,
39
+ getOptionColor,
40
+ type MetadataFieldType,
41
+ type SchemaFieldType,
42
+ type FieldCreateRule,
43
+ type OptionColor
44
+ } from './field-schema-rules';
32
45
 
33
46
  /**
34
47
  * 批量操作结果
@@ -2669,7 +2682,11 @@ export type {
2669
2682
  CreateFieldDefinition,
2670
2683
  UpdateFieldDefinition,
2671
2684
  CreateObjectDefinition,
2672
- UpdateObjectDefinition
2685
+ UpdateObjectDefinition,
2686
+ MetadataFieldType,
2687
+ SchemaFieldType,
2688
+ FieldCreateRule,
2689
+ OptionColor
2673
2690
  };
2674
2691
 
2675
2692
  export {
@@ -2690,5 +2707,13 @@ export {
2690
2707
  getFieldType,
2691
2708
  isSystemField,
2692
2709
  createMultilingualText,
2693
- extractMultilingualText
2710
+ extractMultilingualText,
2711
+ // Schema rules
2712
+ SCHEMA_TYPE_BY_METADATA_TYPE,
2713
+ OPTION_COLOR_LIST,
2714
+ OPTION_COLOR_RULES,
2715
+ FIELD_SCHEMA_RULES,
2716
+ SCHEMA_TYPE_MISMATCHES,
2717
+ BATCH_UPDATE_REQUIREMENTS,
2718
+ getOptionColor
2694
2719
  };