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.
package/dist/index.js CHANGED
@@ -735,6 +735,850 @@ 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
+ const OPTION_COLOR_CODE_BY_NAME = {
782
+ blue: 'B',
783
+ cyan: 'W',
784
+ green: 'G',
785
+ yellow: 'Y',
786
+ orange: 'O',
787
+ red: 'R',
788
+ magenta: 'V',
789
+ purple: 'P',
790
+ blueMagenta: 'I',
791
+ grey: 'N'
792
+ };
793
+ const OPTION_COLOR_NAME_BY_CODE = Object.fromEntries(Object.entries(OPTION_COLOR_CODE_BY_NAME).map(([name, code]) => [code, name]));
794
+ function getOptionColor(index) {
795
+ if (!Number.isInteger(index) || index < 0) {
796
+ throw new Error('Option color index must be a non-negative integer.');
797
+ }
798
+ return OPTION_COLOR_LIST[index % OPTION_COLOR_LIST.length];
799
+ }
800
+ function getOptionColorCode(color) {
801
+ return OPTION_COLOR_CODE_BY_NAME[color];
802
+ }
803
+ function normalizeOptionColorForSchema(color) {
804
+ if (typeof color !== 'string') {
805
+ return color;
806
+ }
807
+ return OPTION_COLOR_CODE_BY_NAME[color] || color;
808
+ }
809
+ const OPTION_COLOR_RULES = {
810
+ allowedColors: OPTION_COLOR_LIST,
811
+ assignment: 'Use OPTION_COLOR_LIST in order and cycle from the beginning when options exceed 10.',
812
+ verifiedBy: {
813
+ namespace: 'package_154107__c',
814
+ object: 'object_test2',
815
+ field: 'option_colors',
816
+ date: '2026-06-30'
817
+ },
818
+ metadataShape: {
819
+ typeName: 'option',
820
+ optionListPath: 'type.settings.optionList',
821
+ colorPath: 'type.settings.optionList[].color',
822
+ sourcePath: 'type.settings.optionSource',
823
+ globalOptionPath: 'type.settings.globalOptionAPIName'
824
+ },
825
+ createShape: {
826
+ typeName: 'enum',
827
+ optionsPath: 'type.settings.options',
828
+ colorPath: 'type.settings.options[].color',
829
+ colorInput: 'SDK accepts metadata color names and normalizes them to OpenAPI color codes before writing.',
830
+ colorCodeByName: OPTION_COLOR_CODE_BY_NAME,
831
+ sourcePath: 'type.settings.option_source',
832
+ globalOptionPath: 'type.settings.global_option_api_name'
833
+ }
834
+ };
835
+ const FIELD_SCHEMA_RULES = [
836
+ {
837
+ metadataType: 'text',
838
+ schemaType: 'text',
839
+ settingsExample: {
840
+ required: false,
841
+ unique: false,
842
+ case_sensitive: false,
843
+ multiline: false,
844
+ max_length: 255
845
+ }
846
+ },
847
+ {
848
+ metadataType: 'text_multiline',
849
+ schemaType: 'text',
850
+ settingsExample: {
851
+ required: false,
852
+ unique: false,
853
+ case_sensitive: false,
854
+ multiline: true,
855
+ max_length: 100000
856
+ }
857
+ },
858
+ {
859
+ metadataType: 'bigint',
860
+ schemaType: 'bigint',
861
+ settingsExample: {
862
+ required: false,
863
+ unique: false
864
+ }
865
+ },
866
+ {
867
+ metadataType: 'number',
868
+ schemaType: 'float',
869
+ settingsExample: {
870
+ required: false,
871
+ unique: false,
872
+ display_as_percentage: false,
873
+ decimal_places_number: 2
874
+ },
875
+ notes: 'Do not send create type as `number`.'
876
+ },
877
+ {
878
+ metadataType: 'date',
879
+ schemaType: 'date',
880
+ settingsExample: {
881
+ required: false
882
+ }
883
+ },
884
+ {
885
+ metadataType: 'datetime',
886
+ schemaType: 'datetime',
887
+ settingsExample: {
888
+ required: false
889
+ }
890
+ },
891
+ {
892
+ metadataType: 'option',
893
+ schemaType: 'enum',
894
+ settingsExample: {
895
+ required: false,
896
+ multiple: false,
897
+ option_source: 'custom',
898
+ global_option_api_name: '',
899
+ options: [
900
+ {
901
+ label: { zh_cn: 'Option One', en_us: 'Option One' },
902
+ api_name: 'option_one',
903
+ description: null,
904
+ color: 'blue',
905
+ active: true
906
+ },
907
+ {
908
+ label: { zh_cn: 'Option Two', en_us: 'Option Two' },
909
+ api_name: 'option_two',
910
+ description: null,
911
+ color: 'green',
912
+ active: true
913
+ }
914
+ ]
915
+ },
916
+ 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(', ')}).`
917
+ },
918
+ {
919
+ metadataType: 'boolean',
920
+ schemaType: 'boolean',
921
+ settingsExample: {
922
+ default_value: true,
923
+ description_if_true: { zh_cn: '1', en_us: '1' },
924
+ description_if_false: { zh_cn: '0', en_us: '0' }
925
+ }
926
+ },
927
+ {
928
+ metadataType: 'lookup',
929
+ schemaType: 'lookup',
930
+ settingsExample: {
931
+ required: false,
932
+ multiple: false,
933
+ referenced_object_api_name: '_user',
934
+ display_as_tree: false,
935
+ display_style: 'select'
936
+ },
937
+ dependsOn: ['Target object must exist first: `_user`'],
938
+ notes: 'Single-value lookup (`multiple: false`), can be used by `reference_field`.'
939
+ },
940
+ {
941
+ metadataType: 'lookup_multi',
942
+ schemaType: 'lookup',
943
+ settingsExample: {
944
+ required: false,
945
+ multiple: true,
946
+ referenced_object_api_name: '_user',
947
+ display_as_tree: false,
948
+ display_style: 'select'
949
+ },
950
+ dependsOn: ['Target object must exist first: `_user`'],
951
+ notes: 'Multi-value lookup (`multiple: true`) cannot be used by `reference_field`.'
952
+ },
953
+ {
954
+ metadataType: 'referenceField',
955
+ schemaType: 'reference_field',
956
+ settingsExample: {
957
+ current_lookup_field_api_name: 'lookup_835c2a2457b',
958
+ target_reference_field_api_name: '_lark_user_id'
959
+ },
960
+ dependsOn: [
961
+ 'A single-value lookup field must exist first in the same object',
962
+ 'The target field must exist in the lookup target object'
963
+ ],
964
+ notes: 'Guide field must be single lookup (`multiple: false`).'
965
+ },
966
+ {
967
+ metadataType: 'file',
968
+ schemaType: 'attachment',
969
+ settingsExample: {
970
+ required: false,
971
+ any_type: true,
972
+ max_uploaded_num: 10,
973
+ mime_types: []
974
+ },
975
+ notes: 'Do not send create type as `file`.'
976
+ },
977
+ {
978
+ metadataType: 'autoId',
979
+ schemaType: 'auto_number',
980
+ settingsExample: {
981
+ generation_method: 'random',
982
+ digits: 1,
983
+ prefix: '',
984
+ suffix: '',
985
+ start_at: '1'
986
+ },
987
+ notes: 'Do not send create type as `autoId`.'
988
+ },
989
+ {
990
+ metadataType: 'richText',
991
+ schemaType: 'richText',
992
+ settingsExample: {
993
+ required: false,
994
+ max_length: 1000
995
+ }
996
+ },
997
+ {
998
+ metadataType: 'mobileNumber',
999
+ schemaType: 'phone',
1000
+ settingsExample: {
1001
+ required: false,
1002
+ unique: false
1003
+ },
1004
+ notes: 'Do not send create type as `mobileNumber`.'
1005
+ },
1006
+ {
1007
+ metadataType: 'avatarOrLogo',
1008
+ schemaType: 'avatar',
1009
+ settingsExample: {
1010
+ display_style: 'square'
1011
+ },
1012
+ notes: 'Do not send create type as `avatarOrLogo`.'
1013
+ },
1014
+ {
1015
+ metadataType: 'email',
1016
+ schemaType: 'email',
1017
+ settingsExample: {
1018
+ required: false,
1019
+ unique: false
1020
+ }
1021
+ },
1022
+ {
1023
+ metadataType: 'region',
1024
+ schemaType: 'region',
1025
+ settingsExample: {
1026
+ required: false,
1027
+ multiple: false,
1028
+ has_level_strict: true,
1029
+ strict_level: 4
1030
+ }
1031
+ },
1032
+ {
1033
+ metadataType: 'decimal',
1034
+ schemaType: 'decimal',
1035
+ settingsExample: {
1036
+ required: false,
1037
+ unique: false,
1038
+ display_as_percentage: false,
1039
+ decimal_places: 2
1040
+ }
1041
+ },
1042
+ {
1043
+ metadataType: 'multilingual',
1044
+ schemaType: 'multilingual',
1045
+ settingsExample: {
1046
+ required: false,
1047
+ unique: false,
1048
+ case_sensitive: false,
1049
+ multiline: false,
1050
+ max_length: 1000
1051
+ }
1052
+ }
1053
+ ];
1054
+ const SCHEMA_TYPE_MISMATCHES = FIELD_SCHEMA_RULES
1055
+ .filter((rule) => rule.metadataType !== rule.schemaType)
1056
+ .map((rule) => ({
1057
+ metadataType: rule.metadataType,
1058
+ schemaType: rule.schemaType
1059
+ }));
1060
+ const SQL_TYPE_TO_SCHEMA_TYPE = [
1061
+ { sqlPattern: 'VARCHAR\\(\\d+\\)|CHAR\\(\\d+\\)', schemaType: 'text', settingsMapping: 'max_length from (n), multiline: false' },
1062
+ { sqlPattern: 'TEXT|LONGTEXT|MEDIUMTEXT|TINYTEXT|CLOB', schemaType: 'text', settingsMapping: 'multiline: true, max_length: 100000' },
1063
+ { sqlPattern: 'INT|INTEGER|BIGINT|SMALLINT|TINYINT(?!\\(1\\))|MEDIUMINT|SERIAL', schemaType: 'bigint', settingsMapping: 'required/unique from constraints' },
1064
+ { sqlPattern: 'FLOAT|DOUBLE|REAL', schemaType: 'float', settingsMapping: 'decimal_places_number: 2' },
1065
+ { sqlPattern: 'DECIMAL\\(\\d+,\\d+\\)|NUMERIC\\(\\d+,\\d+\\)', schemaType: 'decimal', settingsMapping: 'decimal_places from scale (s)' },
1066
+ { sqlPattern: 'DATE', schemaType: 'date', settingsMapping: 'required from constraints' },
1067
+ { sqlPattern: 'DATETIME|TIMESTAMP', schemaType: 'datetime', settingsMapping: 'required from constraints' },
1068
+ { sqlPattern: 'BOOLEAN|BOOL|TINYINT\\(1\\)|BIT', schemaType: 'boolean', settingsMapping: 'default_value from DEFAULT' },
1069
+ { sqlPattern: 'ENUM\\(.*\\)', schemaType: 'enum', settingsMapping: 'options from enum values, colors auto-assigned with getOptionColor(index)' },
1070
+ { sqlPattern: 'BLOB|BINARY|VARBINARY|LONGBLOB|MEDIUMBLOB', schemaType: 'attachment', settingsMapping: 'any_type: true' },
1071
+ { sqlPattern: 'JSON', schemaType: 'richText', settingsMapping: 'only when JSON stores rich text content' }
1072
+ ];
1073
+ const COLUMN_NAME_SEMANTIC_RULES = [
1074
+ { columnPattern: '(^|_)(e?mail)(s?$|_)', schemaType: 'email', notes: 'Column name contains email/mail' },
1075
+ { columnPattern: '(^|_)(phone|mobile|tel)(s?$|_)', schemaType: 'phone', notes: 'Column name contains phone/mobile/tel' },
1076
+ { columnPattern: '(^|_)(avatar|logo|profile_image)(s?$|_)', schemaType: 'avatar', notes: 'Column name contains avatar/logo' },
1077
+ { columnPattern: '(^|_)(region|province|city|district|address)(s?$|_)', schemaType: 'region', notes: 'Column name implies geographic data' }
1078
+ ];
1079
+ const SQL_CONSTRAINT_TO_SETTINGS = [
1080
+ { sqlConstraint: 'NOT NULL', settingsField: 'required', settingsValue: 'true', notes: 'Maps to required: true' },
1081
+ { sqlConstraint: 'UNIQUE', settingsField: 'unique', settingsValue: 'true', notes: 'Maps to unique: true' },
1082
+ { sqlConstraint: 'PRIMARY KEY', settingsField: '-', settingsValue: '-', notes: 'Ignored: aPaaS uses system _id' },
1083
+ { sqlConstraint: 'AUTO_INCREMENT', settingsField: '-', settingsValue: '-', notes: 'Ignored: aPaaS _id auto-increments. For business serial numbers, use auto_number' },
1084
+ { sqlConstraint: 'FOREIGN KEY', settingsField: 'referenced_object_api_name', settingsValue: '(target table)', notes: 'Convert to lookup field' },
1085
+ { sqlConstraint: 'DEFAULT', settingsField: 'default_value', settingsValue: '(value)', notes: 'Only boolean type supports default_value in aPaaS' },
1086
+ { sqlConstraint: 'CHECK', settingsField: '-', settingsValue: '-', notes: 'Not supported in aPaaS, handle in application logic' },
1087
+ { sqlConstraint: 'INDEX', settingsField: '-', settingsValue: '-', notes: 'Not applicable, aPaaS manages indexing automatically' }
1088
+ ];
1089
+ const BATCH_UPDATE_REQUIREMENTS = {
1090
+ add: 'Use operator=add with full field definition.',
1091
+ replace: 'Use operator=replace and include full `type` (name + settings). Label-only replace fails.',
1092
+ remove: 'Use operator=remove with api_name only.',
1093
+ dependencyOrder: {
1094
+ add: ['lookup/lookup_multi before reference_field'],
1095
+ remove: ['reference_field before lookup/lookup_multi']
1096
+ },
1097
+ referenceFieldConstraint: 'reference_field only works with single lookup (`multiple: false`).'
1098
+ };
1099
+
1100
+ const SCHEMA_BATCH_SIZE = 10;
1101
+ function isSystemSchemaName(apiName) {
1102
+ return apiName.startsWith('_');
1103
+ }
1104
+ function normalizeSchemaObjectsForWrite(objects) {
1105
+ return objects.map((object) => {
1106
+ if (!object.fields) {
1107
+ return { ...object };
1108
+ }
1109
+ return {
1110
+ ...object,
1111
+ fields: object.fields.map((field) => normalizeSchemaFieldForWrite(field))
1112
+ };
1113
+ });
1114
+ }
1115
+ function normalizeSchemaFieldForWrite(field) {
1116
+ var _a, _b;
1117
+ const settings = (_a = field.type) === null || _a === void 0 ? void 0 : _a.settings;
1118
+ if (((_b = field.type) === null || _b === void 0 ? void 0 : _b.name) !== 'enum' || !Array.isArray(settings === null || settings === void 0 ? void 0 : settings.options)) {
1119
+ return { ...field };
1120
+ }
1121
+ return {
1122
+ ...field,
1123
+ type: {
1124
+ ...field.type,
1125
+ settings: {
1126
+ ...settings,
1127
+ options: settings.options.map((option) => {
1128
+ if (!option || typeof option !== 'object' || !('color' in option)) {
1129
+ return option;
1130
+ }
1131
+ return {
1132
+ ...option,
1133
+ color: normalizeOptionColorForSchema(option.color)
1134
+ };
1135
+ })
1136
+ }
1137
+ }
1138
+ };
1139
+ }
1140
+ function validateSchemaResponse(result, context = 'schema', options = {}) {
1141
+ var _a, _b, _c, _d;
1142
+ const failures = [];
1143
+ if (!result) {
1144
+ failures.push({
1145
+ layer: 'request',
1146
+ context,
1147
+ code: 'NO_RESULT',
1148
+ message: `${context} request failed: empty response`
1149
+ });
1150
+ return { ok: false, result, failures };
1151
+ }
1152
+ if (String(result.code) !== '0') {
1153
+ failures.push({
1154
+ layer: 'request',
1155
+ context,
1156
+ code: result.code === undefined ? 'undefined' : String(result.code),
1157
+ message: `${context} request failed: ${result.code} ${result.msg || result.message || ''}`.trim()
1158
+ });
1159
+ }
1160
+ if (result.data === null && !options.allowDataNull) {
1161
+ failures.push({
1162
+ layer: 'silent',
1163
+ context,
1164
+ code: 'DATA_NULL',
1165
+ message: `${context} silently failed: result.code is 0 but result.data is null`
1166
+ });
1167
+ }
1168
+ for (const item of ((_a = result.data) === null || _a === void 0 ? void 0 : _a.items) || []) {
1169
+ const itemCode = (_b = item.status) === null || _b === void 0 ? void 0 : _b.code;
1170
+ if (itemCode === undefined && options.requireItemStatus) {
1171
+ failures.push({
1172
+ layer: 'item',
1173
+ context,
1174
+ code: 'MISSING_STATUS',
1175
+ apiName: getItemApiName(item),
1176
+ item,
1177
+ message: `${context} item status is missing: ${getItemApiName(item) || 'unknown'}`
1178
+ });
1179
+ continue;
1180
+ }
1181
+ if (itemCode !== undefined && String(itemCode) !== '0') {
1182
+ failures.push({
1183
+ layer: 'item',
1184
+ context,
1185
+ code: String(itemCode),
1186
+ apiName: getItemApiName(item),
1187
+ item,
1188
+ message: `${context} item failed: ${getItemApiName(item) || 'unknown'} ${((_c = item.status) === null || _c === void 0 ? void 0 : _c.message) || ((_d = item.status) === null || _d === void 0 ? void 0 : _d.msg) || ''}`.trim()
1189
+ });
1190
+ }
1191
+ }
1192
+ return { ok: failures.length === 0, result, failures };
1193
+ }
1194
+ function checkSchemaResponse(result, context = 'schema', options) {
1195
+ const validation = validateSchemaResponse(result, context, options);
1196
+ if (!validation.ok) {
1197
+ const error = new Error(validation.failures.map((failure) => failure.message).join('; '));
1198
+ error.failures = validation.failures;
1199
+ throw error;
1200
+ }
1201
+ return result;
1202
+ }
1203
+ async function batchExecute(items, fn, options = {}) {
1204
+ var _a;
1205
+ const batchSize = (_a = options.batchSize) !== null && _a !== void 0 ? _a : SCHEMA_BATCH_SIZE;
1206
+ if (!Number.isInteger(batchSize) || batchSize <= 0) {
1207
+ throw new Error('batchSize must be a positive integer.');
1208
+ }
1209
+ const batchCount = Math.ceil(items.length / batchSize);
1210
+ const results = [];
1211
+ const failures = [];
1212
+ for (let start = 0; start < items.length; start += batchSize) {
1213
+ const batch = items.slice(start, start + batchSize);
1214
+ const batchIndex = Math.floor(start / batchSize) + 1;
1215
+ const info = {
1216
+ batchIndex,
1217
+ batchCount,
1218
+ start,
1219
+ end: start + batch.length,
1220
+ batchSize: batch.length
1221
+ };
1222
+ const context = `${options.context || 'schema batch'} [${batchIndex}/${batchCount}]`;
1223
+ try {
1224
+ const result = await fn(batch, info);
1225
+ results.push(result);
1226
+ if (options.checkResponse !== false) {
1227
+ const validation = validateSchemaResponse(result, context, options.responseOptions);
1228
+ failures.push(...validation.failures);
1229
+ if (!validation.ok && !options.continueOnError) {
1230
+ const error = new Error(validation.failures.map((failure) => failure.message).join('; '));
1231
+ error.failures = validation.failures;
1232
+ throw error;
1233
+ }
1234
+ }
1235
+ }
1236
+ catch (error) {
1237
+ if (!options.continueOnError) {
1238
+ throw error;
1239
+ }
1240
+ failures.push({
1241
+ layer: 'request',
1242
+ context,
1243
+ code: 'EXCEPTION',
1244
+ message: error instanceof Error ? error.message : String(error)
1245
+ });
1246
+ }
1247
+ }
1248
+ return {
1249
+ ok: failures.length === 0,
1250
+ total: items.length,
1251
+ batchSize,
1252
+ batchCount,
1253
+ results,
1254
+ failures
1255
+ };
1256
+ }
1257
+ function splitSchemaFieldsByDependency(fields) {
1258
+ const baseFields = [];
1259
+ const lookupFields = [];
1260
+ const referenceFields = [];
1261
+ for (const field of fields) {
1262
+ const typeName = getSchemaFieldTypeName(field);
1263
+ if (typeName === 'reference_field' || typeName === 'referenceField') {
1264
+ referenceFields.push(field);
1265
+ }
1266
+ else if (typeName === 'lookup' || typeName === 'lookup_multi') {
1267
+ lookupFields.push(field);
1268
+ }
1269
+ else {
1270
+ baseFields.push(field);
1271
+ }
1272
+ }
1273
+ return { baseFields, lookupFields, referenceFields };
1274
+ }
1275
+ async function createSchemaObjectShells(client, objects, options = {}) {
1276
+ assertNoSystemObjects(objects.map((object) => object.api_name), 'createSchemaObjectShells');
1277
+ const existingNames = options.skipExisting === false
1278
+ ? new Set()
1279
+ : await getExistingObjectNames(client);
1280
+ const shells = objects
1281
+ .filter((object) => !existingNames.has(object.api_name))
1282
+ .map(toShellObject);
1283
+ const result = {
1284
+ requested: objects.map((object) => object.api_name),
1285
+ created: shells.map((object) => object.api_name),
1286
+ skippedExisting: objects
1287
+ .filter((object) => existingNames.has(object.api_name))
1288
+ .map((object) => object.api_name)
1289
+ };
1290
+ if (shells.length === 0) {
1291
+ return result;
1292
+ }
1293
+ result.batches = await batchExecute(shells, (batch) => client.schema.create({ objects: batch }), {
1294
+ batchSize: options.batchSize,
1295
+ context: options.context || 'schema.createShells',
1296
+ continueOnError: options.continueOnError,
1297
+ checkResponse: options.checkResponse,
1298
+ responseOptions: options.responseOptions
1299
+ });
1300
+ return result;
1301
+ }
1302
+ async function addFieldsIdempotent(client, objectName, fieldsToAdd, options = {}) {
1303
+ assertNoSystemObjects([objectName], 'addFieldsIdempotent');
1304
+ assertNoSystemFields(fieldsToAdd.map((field) => field.api_name), `addFieldsIdempotent(${objectName})`);
1305
+ const existingNames = options.skipExisting === false
1306
+ ? new Set()
1307
+ : await getExistingFieldNames(client, objectName);
1308
+ const newFields = fieldsToAdd.filter((field) => !existingNames.has(field.api_name)).map(toAddField);
1309
+ const skippedFields = fieldsToAdd
1310
+ .filter((field) => existingNames.has(field.api_name))
1311
+ .map((field) => field.api_name);
1312
+ if (newFields.length === 0) {
1313
+ return {
1314
+ objectName,
1315
+ addedFields: [],
1316
+ skippedFields
1317
+ };
1318
+ }
1319
+ const result = await client.schema.update({
1320
+ objects: [{ api_name: objectName, fields: newFields }]
1321
+ });
1322
+ if (options.checkResponse !== false) {
1323
+ checkSchemaResponse(result, options.context || `schema.addFieldsIdempotent(${objectName})`, options.responseOptions);
1324
+ }
1325
+ return {
1326
+ objectName,
1327
+ addedFields: newFields.map((field) => field.api_name),
1328
+ skippedFields,
1329
+ result
1330
+ };
1331
+ }
1332
+ async function createSchemaObjectsInStages(client, objects, options = {}) {
1333
+ const context = options.context || 'schema.createWithStages';
1334
+ const shells = await createSchemaObjectShells(client, objects, {
1335
+ ...options,
1336
+ context: `${context}.shells`
1337
+ });
1338
+ const baseFields = [];
1339
+ const lookupFields = [];
1340
+ const referenceFields = [];
1341
+ for (const object of objects) {
1342
+ assertNoSystemFields((object.fields || []).map((field) => field.api_name), `${context}(${object.api_name})`);
1343
+ const split = splitSchemaFieldsByDependency(object.fields || []);
1344
+ if (split.baseFields.length > 0) {
1345
+ baseFields.push(await addFieldsIdempotent(client, object.api_name, split.baseFields, {
1346
+ context: `${context}.baseFields(${object.api_name})`,
1347
+ skipExisting: options.skipExisting,
1348
+ checkResponse: options.checkResponse,
1349
+ responseOptions: options.responseOptions
1350
+ }));
1351
+ }
1352
+ if (split.lookupFields.length > 0) {
1353
+ lookupFields.push(await addFieldsIdempotent(client, object.api_name, split.lookupFields, {
1354
+ context: `${context}.lookupFields(${object.api_name})`,
1355
+ skipExisting: options.skipExisting,
1356
+ checkResponse: options.checkResponse,
1357
+ responseOptions: options.responseOptions
1358
+ }));
1359
+ }
1360
+ if (split.referenceFields.length > 0) {
1361
+ referenceFields.push(await addFieldsIdempotent(client, object.api_name, split.referenceFields, {
1362
+ context: `${context}.referenceFields(${object.api_name})`,
1363
+ skipExisting: options.skipExisting,
1364
+ checkResponse: options.checkResponse,
1365
+ responseOptions: options.responseOptions
1366
+ }));
1367
+ }
1368
+ }
1369
+ const result = {
1370
+ shells,
1371
+ baseFields,
1372
+ lookupFields,
1373
+ referenceFields
1374
+ };
1375
+ const finalSettings = options.updateFinalSettings === false
1376
+ ? []
1377
+ : objects
1378
+ .filter((object) => object.settings && Object.keys(object.settings).length > 0)
1379
+ .map((object) => ({ api_name: object.api_name, settings: object.settings }));
1380
+ if (finalSettings.length > 0) {
1381
+ result.finalSettings = await batchExecute(finalSettings, (batch) => client.schema.update({ objects: batch }), {
1382
+ batchSize: options.batchSize,
1383
+ context: `${context}.finalSettings`,
1384
+ continueOnError: options.continueOnError,
1385
+ checkResponse: options.checkResponse,
1386
+ responseOptions: {
1387
+ ...options.responseOptions,
1388
+ allowDataNull: true
1389
+ }
1390
+ });
1391
+ }
1392
+ if (options.verify !== false) {
1393
+ result.verification = await verifySchemaObjects(client, objects.map((object) => object.api_name), { includeMarkdown: options.includeMarkdown });
1394
+ }
1395
+ return result;
1396
+ }
1397
+ async function verifySchemaObjects(client, objectNames, options = {}) {
1398
+ var _a, _b;
1399
+ const allObjects = await client.object.listWithIterator();
1400
+ const objects = [];
1401
+ for (const objectName of objectNames) {
1402
+ const exists = Boolean((_a = allObjects.items) === null || _a === void 0 ? void 0 : _a.find((object) => object.apiName === objectName || object.api_name === objectName));
1403
+ if (!exists) {
1404
+ objects.push({
1405
+ objectName,
1406
+ exists: false,
1407
+ fields: [],
1408
+ customFields: []
1409
+ });
1410
+ continue;
1411
+ }
1412
+ const fieldResult = await client.object.metadata.fields({ object_name: objectName });
1413
+ if (fieldResult.code !== undefined && String(fieldResult.code) !== '0') {
1414
+ throw new Error(`verifySchemaObjects(${objectName}) failed: ${fieldResult.code} ${fieldResult.msg || fieldResult.message || ''}`.trim());
1415
+ }
1416
+ const fields = ((_b = fieldResult.data) === null || _b === void 0 ? void 0 : _b.fields) || [];
1417
+ objects.push({
1418
+ objectName,
1419
+ exists: true,
1420
+ fields,
1421
+ customFields: fields.filter((field) => !isSystemSchemaName(field.apiName || field.api_name || ''))
1422
+ });
1423
+ }
1424
+ const result = { objects };
1425
+ if (options.includeMarkdown && client.object.metadata.export2markdown) {
1426
+ result.markdown = await client.object.metadata.export2markdown({ object_names: objectNames });
1427
+ }
1428
+ return result;
1429
+ }
1430
+ function buildFieldRemovalPlan(fieldsByObject) {
1431
+ const referenceFieldObjects = new Map();
1432
+ const lookupObjects = new Map();
1433
+ const otherFieldObjects = new Map();
1434
+ for (const [objectName, fields] of Object.entries(fieldsByObject)) {
1435
+ for (const field of fields) {
1436
+ const apiName = field.api_name || field.apiName;
1437
+ if (!apiName || isSystemSchemaName(apiName)) {
1438
+ continue;
1439
+ }
1440
+ const target = getRemovalGroup(field, referenceFieldObjects, lookupObjects, otherFieldObjects);
1441
+ const existing = target.get(objectName) || [];
1442
+ existing.push({ operator: 'remove', api_name: apiName });
1443
+ target.set(objectName, existing);
1444
+ }
1445
+ }
1446
+ return {
1447
+ referenceFieldObjects: mapRemovalObjects(referenceFieldObjects),
1448
+ lookupObjects: mapRemovalObjects(lookupObjects),
1449
+ otherFieldObjects: mapRemovalObjects(otherFieldObjects)
1450
+ };
1451
+ }
1452
+ async function deleteAllCustomObjects(client, options = {}) {
1453
+ var _a;
1454
+ if (options.confirm !== true) {
1455
+ throw new Error('deleteAllCustomObjects requires { confirm: true }.');
1456
+ }
1457
+ const context = options.context || 'schema.deleteAllCustomObjects';
1458
+ const allObjects = await client.object.listWithIterator();
1459
+ const customObjects = (allObjects.items || []).filter((object) => !isSystemSchemaName(object.apiName || object.api_name || ''));
1460
+ const objectNames = customObjects.map((object) => object.apiName || object.api_name);
1461
+ const fieldsByObject = {};
1462
+ for (const objectName of objectNames) {
1463
+ const fieldResult = await client.object.metadata.fields({ object_name: objectName });
1464
+ if (fieldResult.code !== undefined && String(fieldResult.code) !== '0') {
1465
+ throw new Error(`${context}.readFields(${objectName}) failed: ${fieldResult.code} ${fieldResult.msg || fieldResult.message || ''}`.trim());
1466
+ }
1467
+ fieldsByObject[objectName] = ((_a = fieldResult.data) === null || _a === void 0 ? void 0 : _a.fields) || [];
1468
+ }
1469
+ const removalPlan = buildFieldRemovalPlan(fieldsByObject);
1470
+ const fieldRemovalResults = [];
1471
+ for (const [phase, updateObjects] of [
1472
+ ['referenceFields', removalPlan.referenceFieldObjects],
1473
+ ['lookupFields', removalPlan.lookupObjects],
1474
+ ['otherFields', options.removeOtherFields ? removalPlan.otherFieldObjects : []]
1475
+ ]) {
1476
+ if (updateObjects.length === 0) {
1477
+ continue;
1478
+ }
1479
+ fieldRemovalResults.push(await batchExecute(updateObjects, (batch) => client.schema.update({ objects: batch }), {
1480
+ batchSize: options.batchSize,
1481
+ context: `${context}.${phase}`,
1482
+ continueOnError: options.continueOnError,
1483
+ checkResponse: options.checkResponse,
1484
+ responseOptions: options.responseOptions
1485
+ }));
1486
+ }
1487
+ const result = {
1488
+ deletedObjects: objectNames,
1489
+ removalPlan,
1490
+ fieldRemovalResults
1491
+ };
1492
+ if (objectNames.length > 0) {
1493
+ result.deleteResults = await batchExecute(objectNames, (batch) => client.schema.delete({ api_names: batch }), {
1494
+ batchSize: options.batchSize,
1495
+ context: `${context}.deleteObjects`,
1496
+ continueOnError: options.continueOnError,
1497
+ checkResponse: options.checkResponse,
1498
+ responseOptions: {
1499
+ ...options.responseOptions,
1500
+ allowDataNull: true
1501
+ }
1502
+ });
1503
+ }
1504
+ if (options.verify !== false) {
1505
+ const after = await client.object.listWithIterator();
1506
+ result.remainingObjects = (after.items || [])
1507
+ .filter((object) => objectNames.includes(object.apiName || object.api_name))
1508
+ .map((object) => object.apiName || object.api_name);
1509
+ if (result.remainingObjects.length > 0) {
1510
+ throw new Error(`${context} verification failed: remaining objects ${result.remainingObjects.join(', ')}`);
1511
+ }
1512
+ }
1513
+ return result;
1514
+ }
1515
+ function getItemApiName(item) {
1516
+ return item.api_name || item.apiName || item.name;
1517
+ }
1518
+ function getSchemaFieldTypeName(field) {
1519
+ const type = field.type;
1520
+ if (typeof type === 'string') {
1521
+ return type;
1522
+ }
1523
+ return type === null || type === void 0 ? void 0 : type.name;
1524
+ }
1525
+ function toAddField(field) {
1526
+ return {
1527
+ ...field,
1528
+ operator: 'add'
1529
+ };
1530
+ }
1531
+ function toShellObject(object) {
1532
+ const { fields: _fields, ...shell } = object;
1533
+ const originalSettings = object.settings || {};
1534
+ return {
1535
+ ...shell,
1536
+ settings: {
1537
+ ...originalSettings,
1538
+ display_name: '_id',
1539
+ allow_search_fields: ['_id'],
1540
+ search_layout: []
1541
+ }
1542
+ };
1543
+ }
1544
+ async function getExistingObjectNames(client) {
1545
+ const result = await client.object.listWithIterator();
1546
+ return new Set((result.items || []).map((object) => object.apiName || object.api_name).filter(Boolean));
1547
+ }
1548
+ async function getExistingFieldNames(client, objectName) {
1549
+ var _a;
1550
+ const result = await client.object.metadata.fields({ object_name: objectName });
1551
+ if (result.code !== undefined && String(result.code) !== '0') {
1552
+ throw new Error(`getExistingFieldNames(${objectName}) failed: ${result.code} ${result.msg || result.message || ''}`.trim());
1553
+ }
1554
+ return new Set((((_a = result.data) === null || _a === void 0 ? void 0 : _a.fields) || []).map((field) => field.apiName || field.api_name).filter(Boolean));
1555
+ }
1556
+ function assertNoSystemObjects(apiNames, context) {
1557
+ const systemNames = apiNames.filter(isSystemSchemaName);
1558
+ if (systemNames.length > 0) {
1559
+ throw new Error(`${context} cannot modify system objects: ${systemNames.join(', ')}`);
1560
+ }
1561
+ }
1562
+ function assertNoSystemFields(apiNames, context) {
1563
+ const systemNames = apiNames.filter(isSystemSchemaName);
1564
+ if (systemNames.length > 0) {
1565
+ throw new Error(`${context} cannot modify system fields: ${systemNames.join(', ')}`);
1566
+ }
1567
+ }
1568
+ function getRemovalGroup(field, referenceFieldObjects, lookupObjects, otherFieldObjects) {
1569
+ const typeName = getSchemaFieldTypeName(field);
1570
+ if (typeName === 'reference_field' || typeName === 'referenceField') {
1571
+ return referenceFieldObjects;
1572
+ }
1573
+ if (typeName === 'lookup' || typeName === 'lookup_multi') {
1574
+ return lookupObjects;
1575
+ }
1576
+ return otherFieldObjects;
1577
+ }
1578
+ function mapRemovalObjects(fieldsByObject) {
1579
+ return Array.from(fieldsByObject.entries()).map(([api_name, fields]) => ({ api_name, fields }));
1580
+ }
1581
+
738
1582
  /**
739
1583
  * 判断错误是否可重试
740
1584
  */
@@ -1381,8 +2225,70 @@ class Client {
1381
2225
  total: total,
1382
2226
  msg: res.msg
1383
2227
  };
2228
+ },
2229
+ /**
2230
+ * 跨对象搜索记录
2231
+ * @description 在最多 5 个对象中按搜索词查询记录
2232
+ * @param params 请求体,包含 q、search_objects、page_token、page_size、metadata 等
2233
+ */
2234
+ recordsAcrossObjects: async (params) => {
2235
+ await this.ensureTokenValid();
2236
+ const url = `/v1/namespaces/${this.namespace}/objects/records/search`;
2237
+ this.log(LoggerLevel.info, `[object.search.recordsAcrossObjects] Searching records: q=${params.q}`);
2238
+ const res = await this.axiosInstance.post(url, params, {
2239
+ headers: this.authHeaders(true)
2240
+ });
2241
+ this.log(LoggerLevel.debug, `[object.search.recordsAcrossObjects] Records searched: code=${res.data.code}`);
2242
+ this.log(LoggerLevel.trace, `[object.search.recordsAcrossObjects] Response: ${JSON.stringify(res.data)}`);
2243
+ return res.data;
2244
+ },
2245
+ /**
2246
+ * 跨对象搜索记录 - 自动 page_token 分页
2247
+ */
2248
+ recordsAcrossObjectsWithIterator: async (params) => {
2249
+ var _a, _b, _c, _d, _e, _f;
2250
+ const pageSize = params.page_size || 20;
2251
+ let pageToken = params.page_token || '';
2252
+ let hasMore = true;
2253
+ const items = [];
2254
+ let lastResponse = null;
2255
+ while (hasMore) {
2256
+ const res = await this.object.search.recordsAcrossObjects({
2257
+ ...params,
2258
+ page_size: pageSize,
2259
+ page_token: pageToken
2260
+ });
2261
+ if (res.code !== '0') {
2262
+ this.log(LoggerLevel.error, `[object.search.recordsAcrossObjectsWithIterator] Error searching records: code=${res.code}, msg=${res.msg}`);
2263
+ throw new Error(res.msg || `Search failed with code ${res.code}`);
2264
+ }
2265
+ const pageItems = ((_a = res.data) === null || _a === void 0 ? void 0 : _a.items) || ((_b = res.data) === null || _b === void 0 ? void 0 : _b.records) || ((_c = res.data) === null || _c === void 0 ? void 0 : _c.search_results) || [];
2266
+ if (Array.isArray(pageItems)) {
2267
+ items.push(...pageItems);
2268
+ }
2269
+ lastResponse = res;
2270
+ const nextPageToken = ((_d = res.data) === null || _d === void 0 ? void 0 : _d.next_page_token) || ((_e = res.data) === null || _e === void 0 ? void 0 : _e.page_token) || '';
2271
+ hasMore = Boolean(((_f = res.data) === null || _f === void 0 ? void 0 : _f.has_more) && nextPageToken);
2272
+ pageToken = nextPageToken;
2273
+ }
2274
+ return { items, lastResponse };
1384
2275
  }
1385
2276
  },
2277
+ /**
2278
+ * 执行 OQL
2279
+ * @description 执行对象查询语言,支持匿名参数 args 和具名参数 namedArgs
2280
+ */
2281
+ oql: async (params) => {
2282
+ await this.ensureTokenValid();
2283
+ const url = `/api/data/v1/namespaces/${this.namespace}/records/query`;
2284
+ this.log(LoggerLevel.info, '[object.oql] Executing OQL');
2285
+ const res = await this.axiosInstance.post(url, params, {
2286
+ headers: this.authHeaders(true)
2287
+ });
2288
+ this.log(LoggerLevel.debug, `[object.oql] OQL executed: code=${res.data.code}`);
2289
+ this.log(LoggerLevel.trace, `[object.oql] Response: ${JSON.stringify(res.data)}`);
2290
+ return res.data;
2291
+ },
1386
2292
  create: {
1387
2293
  /**
1388
2294
  * 单条记录创建
@@ -1760,6 +2666,73 @@ class Client {
1760
2666
  }
1761
2667
  }
1762
2668
  };
2669
+ /**
2670
+ * 常量对象模块
2671
+ */
2672
+ this.constant = {
2673
+ /**
2674
+ * 查询常量对象列表。object_name 可取 _currency、_country、_timeZone。
2675
+ */
2676
+ records: async (params) => {
2677
+ const { object_name, data = {} } = params;
2678
+ await this.ensureTokenValid();
2679
+ const url = `/api/data/v1/namespaces/${this.namespace}/objects/${object_name}/records`;
2680
+ this.log(LoggerLevel.info, `[constant.records] Fetching constant records: ${object_name}`);
2681
+ const res = await this.axiosInstance.post(url, data, {
2682
+ headers: this.authHeaders(true)
2683
+ });
2684
+ this.log(LoggerLevel.debug, `[constant.records] Constant records fetched: ${object_name}, code=${res.data.code}`);
2685
+ this.log(LoggerLevel.trace, `[constant.records] Response: ${JSON.stringify(res.data)}`);
2686
+ return res.data;
2687
+ },
2688
+ /**
2689
+ * 查询常量对象单条记录详情。
2690
+ */
2691
+ record: async (params) => {
2692
+ const { object_name, record_id } = params;
2693
+ await this.ensureTokenValid();
2694
+ const url = `/api/data/v1/namespaces/${this.namespace}/objects/${object_name}/${record_id}`;
2695
+ this.log(LoggerLevel.info, `[constant.record] Fetching constant record: ${object_name}.${record_id}`);
2696
+ const res = await this.axiosInstance.get(url, {
2697
+ headers: this.authHeaders()
2698
+ });
2699
+ this.log(LoggerLevel.debug, `[constant.record] Constant record fetched: ${object_name}.${record_id}, code=${res.data.code}`);
2700
+ this.log(LoggerLevel.trace, `[constant.record] Response: ${JSON.stringify(res.data)}`);
2701
+ return res.data;
2702
+ },
2703
+ metadata: {
2704
+ /**
2705
+ * 获取常量对象元数据。
2706
+ */
2707
+ fields: async (params) => {
2708
+ const { object_name } = params;
2709
+ await this.ensureTokenValid();
2710
+ const url = `/api/data/v1/namespaces/${this.namespace}/meta/objects/${object_name}`;
2711
+ this.log(LoggerLevel.info, `[constant.metadata.fields] Fetching constant metadata: ${object_name}`);
2712
+ const res = await this.axiosInstance.get(url, {
2713
+ headers: this.authHeaders()
2714
+ });
2715
+ this.log(LoggerLevel.debug, `[constant.metadata.fields] Constant metadata fetched: ${object_name}, code=${res.data.code}`);
2716
+ this.log(LoggerLevel.trace, `[constant.metadata.fields] Response: ${JSON.stringify(res.data)}`);
2717
+ return res.data;
2718
+ },
2719
+ /**
2720
+ * 获取常量对象字段详情。
2721
+ */
2722
+ field: async (params) => {
2723
+ const { object_name, field_name } = params;
2724
+ await this.ensureTokenValid();
2725
+ const url = `/api/data/v1/namespaces/${this.namespace}/meta/objects/${object_name}/fields/${field_name}`;
2726
+ this.log(LoggerLevel.info, `[constant.metadata.field] Fetching constant field metadata: ${object_name}.${field_name}`);
2727
+ const res = await this.axiosInstance.get(url, {
2728
+ headers: this.authHeaders()
2729
+ });
2730
+ this.log(LoggerLevel.debug, `[constant.metadata.field] Constant field metadata fetched: ${object_name}.${field_name}, code=${res.data.code}`);
2731
+ this.log(LoggerLevel.trace, `[constant.metadata.field] Response: ${JSON.stringify(res.data)}`);
2732
+ return res.data;
2733
+ }
2734
+ }
2735
+ };
1763
2736
  /**
1764
2737
  * 部门 ID 交换模块
1765
2738
  */
@@ -1986,6 +2959,71 @@ class Client {
1986
2959
  return result;
1987
2960
  }
1988
2961
  };
2962
+ /**
2963
+ * 集成模块
2964
+ */
2965
+ this.integration = {
2966
+ lark: {
2967
+ /**
2968
+ * 获取默认飞书集成 tenantAccessToken。
2969
+ */
2970
+ defaultTenantAccessToken: async () => {
2971
+ await this.ensureTokenValid();
2972
+ const url = `/api/integration/v1/namespaces/${this.namespace}/defaultLark/tenantAccessToken`;
2973
+ this.log(LoggerLevel.info, '[integration.lark.defaultTenantAccessToken] Fetching default tenant token');
2974
+ const res = await this.axiosInstance.get(url, {
2975
+ headers: this.authHeaders()
2976
+ });
2977
+ this.log(LoggerLevel.debug, `[integration.lark.defaultTenantAccessToken] Token fetched: code=${res.data.code}`);
2978
+ this.log(LoggerLevel.trace, `[integration.lark.defaultTenantAccessToken] Response: ${JSON.stringify(res.data)}`);
2979
+ return res.data;
2980
+ },
2981
+ /**
2982
+ * 获取默认飞书集成 appAccessToken。
2983
+ */
2984
+ defaultAppAccessToken: async () => {
2985
+ await this.ensureTokenValid();
2986
+ const url = `/api/integration/v1/namespaces/${this.namespace}/defaultLark/appAccessToken`;
2987
+ this.log(LoggerLevel.info, '[integration.lark.defaultAppAccessToken] Fetching default app token');
2988
+ const res = await this.axiosInstance.get(url, {
2989
+ headers: this.authHeaders()
2990
+ });
2991
+ this.log(LoggerLevel.debug, `[integration.lark.defaultAppAccessToken] Token fetched: code=${res.data.code}`);
2992
+ this.log(LoggerLevel.trace, `[integration.lark.defaultAppAccessToken] Response: ${JSON.stringify(res.data)}`);
2993
+ return res.data;
2994
+ },
2995
+ /**
2996
+ * 获取自定义飞书集成 tenantAccessToken。
2997
+ */
2998
+ tenantAccessToken: async (params) => {
2999
+ const { lark_integration_api_name } = params;
3000
+ await this.ensureTokenValid();
3001
+ const url = `/api/integration/v1/namespaces/${this.namespace}/lark/tenantAccessToken/${lark_integration_api_name}`;
3002
+ this.log(LoggerLevel.info, `[integration.lark.tenantAccessToken] Fetching tenant token: ${lark_integration_api_name}`);
3003
+ const res = await this.axiosInstance.get(url, {
3004
+ headers: this.authHeaders()
3005
+ });
3006
+ this.log(LoggerLevel.debug, `[integration.lark.tenantAccessToken] Token fetched: ${lark_integration_api_name}, code=${res.data.code}`);
3007
+ this.log(LoggerLevel.trace, `[integration.lark.tenantAccessToken] Response: ${JSON.stringify(res.data)}`);
3008
+ return res.data;
3009
+ },
3010
+ /**
3011
+ * 获取自定义飞书集成 appAccessToken。
3012
+ */
3013
+ appAccessToken: async (params) => {
3014
+ const { lark_integration_api_name } = params;
3015
+ await this.ensureTokenValid();
3016
+ const url = `/api/integration/v1/namespaces/${this.namespace}/lark/appAccessToken/${lark_integration_api_name}`;
3017
+ this.log(LoggerLevel.info, `[integration.lark.appAccessToken] Fetching app token: ${lark_integration_api_name}`);
3018
+ const res = await this.axiosInstance.get(url, {
3019
+ headers: this.authHeaders()
3020
+ });
3021
+ this.log(LoggerLevel.debug, `[integration.lark.appAccessToken] Token fetched: ${lark_integration_api_name}, code=${res.data.code}`);
3022
+ this.log(LoggerLevel.trace, `[integration.lark.appAccessToken] Response: ${JSON.stringify(res.data)}`);
3023
+ return res.data;
3024
+ }
3025
+ }
3026
+ };
1989
3027
  /**
1990
3028
  * 云函数模块
1991
3029
  */
@@ -2439,6 +3477,59 @@ class Client {
2439
3477
  }
2440
3478
  }
2441
3479
  };
3480
+ /**
3481
+ * 数据集模块
3482
+ */
3483
+ this.dataset = {
3484
+ /**
3485
+ * 查询数据集列表。
3486
+ */
3487
+ list: async (params) => {
3488
+ await this.ensureTokenValid();
3489
+ const url = `/v1/namespaces/${this.namespace}/datasets`;
3490
+ const requestData = params || {};
3491
+ this.log(LoggerLevel.info, '[dataset.list] Fetching datasets');
3492
+ const res = await this.axiosInstance.post(url, requestData, {
3493
+ headers: this.authHeaders(true)
3494
+ });
3495
+ this.log(LoggerLevel.debug, `[dataset.list] Datasets fetched: code=${res.data.code}`);
3496
+ this.log(LoggerLevel.trace, `[dataset.list] Response: ${JSON.stringify(res.data)}`);
3497
+ return res.data;
3498
+ },
3499
+ /**
3500
+ * 查询全部数据集 - 默认使用 cursor 分页。
3501
+ */
3502
+ listWithIterator: async (params) => {
3503
+ var _a, _b, _c, _d, _e, _f, _g;
3504
+ const pageSize = (params === null || params === void 0 ? void 0 : params.page_size) || 100;
3505
+ let pageToken = '';
3506
+ let hasMore = true;
3507
+ let total = 0;
3508
+ const datasets = [];
3509
+ let lastResponse = null;
3510
+ while (hasMore) {
3511
+ const res = await this.dataset.list({
3512
+ ...params,
3513
+ page_type: 'cursor',
3514
+ page_size: pageSize,
3515
+ page_token: pageToken
3516
+ });
3517
+ if (res.code !== '0') {
3518
+ this.log(LoggerLevel.error, `[dataset.listWithIterator] Error fetching datasets: code=${res.code}, msg=${res.msg}`);
3519
+ throw new Error(res.msg || `Fetch failed with code ${res.code}`);
3520
+ }
3521
+ const pageDatasets = ((_a = res.data) === null || _a === void 0 ? void 0 : _a.datasets) || ((_b = res.data) === null || _b === void 0 ? void 0 : _b.items) || [];
3522
+ if (Array.isArray(pageDatasets)) {
3523
+ datasets.push(...pageDatasets);
3524
+ }
3525
+ total = (_d = (_c = res.data) === null || _c === void 0 ? void 0 : _c.total) !== null && _d !== void 0 ? _d : total;
3526
+ lastResponse = res;
3527
+ pageToken = ((_e = res.data) === null || _e === void 0 ? void 0 : _e.next_page_token) || ((_f = res.data) === null || _f === void 0 ? void 0 : _f.page_token) || '';
3528
+ hasMore = Boolean(((_g = res.data) === null || _g === void 0 ? void 0 : _g.has_more) && pageToken);
3529
+ }
3530
+ return { total, datasets, lastResponse };
3531
+ }
3532
+ };
2442
3533
  /**
2443
3534
  * 自动化流程模块
2444
3535
  */
@@ -2507,6 +3598,218 @@ class Client {
2507
3598
  }
2508
3599
  }
2509
3600
  };
3601
+ /**
3602
+ * 流程模块
3603
+ */
3604
+ this.workflow = {
3605
+ execution: {
3606
+ /**
3607
+ * 查询异步流程状态。
3608
+ */
3609
+ status: async (params) => {
3610
+ const { execution_id } = params;
3611
+ await this.ensureTokenValid();
3612
+ const url = `/api/v2/workflow/namespaces/${this.namespace}/open_api/execution`;
3613
+ this.log(LoggerLevel.info, `[workflow.execution.status] Fetching execution status: ${execution_id}`);
3614
+ const res = await this.axiosInstance.get(url, {
3615
+ headers: this.authHeaders(true),
3616
+ params: { executionId: execution_id }
3617
+ });
3618
+ this.log(LoggerLevel.debug, `[workflow.execution.status] Execution status fetched: code=${res.data.code}`);
3619
+ this.log(LoggerLevel.trace, `[workflow.execution.status] Response: ${JSON.stringify(res.data)}`);
3620
+ return res.data;
3621
+ }
3622
+ },
3623
+ definition: {
3624
+ /**
3625
+ * 获取流程定义详情。
3626
+ */
3627
+ detail: async (params) => {
3628
+ const { flow_api_name } = params;
3629
+ await this.ensureTokenValid();
3630
+ const url = `/api/flow/v1/namespaces/${this.namespace}/flow/${flow_api_name}`;
3631
+ this.log(LoggerLevel.info, `[workflow.definition.detail] Fetching flow definition: ${flow_api_name}`);
3632
+ const res = await this.axiosInstance.get(url, {
3633
+ headers: this.authHeaders()
3634
+ });
3635
+ this.log(LoggerLevel.debug, `[workflow.definition.detail] Flow definition fetched: ${flow_api_name}, code=${res.data.code}`);
3636
+ this.log(LoggerLevel.trace, `[workflow.definition.detail] Response: ${JSON.stringify(res.data)}`);
3637
+ return res.data;
3638
+ }
3639
+ },
3640
+ userTask: {
3641
+ /**
3642
+ * 获取包含人工任务的流程列表。
3643
+ */
3644
+ flows: async (params) => {
3645
+ await this.ensureTokenValid();
3646
+ const url = `/api/flow/v1/namespaces/${this.namespace}/user_task/flow_list`;
3647
+ this.log(LoggerLevel.info, '[workflow.userTask.flows] Fetching user-task flows');
3648
+ const res = await this.axiosInstance.post(url, params, {
3649
+ headers: this.authHeaders(true)
3650
+ });
3651
+ this.log(LoggerLevel.debug, `[workflow.userTask.flows] User-task flows fetched: code=${res.data.code}`);
3652
+ this.log(LoggerLevel.trace, `[workflow.userTask.flows] Response: ${JSON.stringify(res.data)}`);
3653
+ return res.data;
3654
+ },
3655
+ /**
3656
+ * 获取包含人工任务的流程实例 ID 列表。
3657
+ */
3658
+ instanceIds: async (params) => {
3659
+ const { page_size, page_token, start_time, end_time, api_ids } = params;
3660
+ await this.ensureTokenValid();
3661
+ const url = `/api/flow/v1/namespaces/${this.namespace}/instances/listids`;
3662
+ const data = {};
3663
+ if (start_time !== undefined)
3664
+ data.start_time = String(start_time);
3665
+ if (end_time !== undefined)
3666
+ data.end_time = String(end_time);
3667
+ if (api_ids !== undefined)
3668
+ data.api_ids = api_ids;
3669
+ this.log(LoggerLevel.info, '[workflow.userTask.instanceIds] Fetching workflow instance IDs');
3670
+ const res = await this.axiosInstance.get(url, {
3671
+ headers: this.authHeaders(true),
3672
+ params: { page_size, page_token },
3673
+ data
3674
+ });
3675
+ this.log(LoggerLevel.debug, `[workflow.userTask.instanceIds] Workflow instance IDs fetched: code=${res.data.code}`);
3676
+ this.log(LoggerLevel.trace, `[workflow.userTask.instanceIds] Response: ${JSON.stringify(res.data)}`);
3677
+ return res.data;
3678
+ },
3679
+ /**
3680
+ * 获取流程实例详情。
3681
+ */
3682
+ instanceDetail: async (params) => {
3683
+ const { approval_instance_id, includes } = params;
3684
+ await this.ensureTokenValid();
3685
+ const url = `/api/flow/v1/namespaces/${this.namespace}/instances/${approval_instance_id}`;
3686
+ this.log(LoggerLevel.info, `[workflow.userTask.instanceDetail] Fetching workflow instance: ${approval_instance_id}`);
3687
+ const res = await this.axiosInstance.get(url, {
3688
+ headers: this.authHeaders(),
3689
+ params: includes ? { includes } : undefined
3690
+ });
3691
+ this.log(LoggerLevel.debug, `[workflow.userTask.instanceDetail] Workflow instance fetched: ${approval_instance_id}, code=${res.data.code}`);
3692
+ this.log(LoggerLevel.trace, `[workflow.userTask.instanceDetail] Response: ${JSON.stringify(res.data)}`);
3693
+ return res.data;
3694
+ },
3695
+ /**
3696
+ * 批量获取流程实例的任务列表。
3697
+ */
3698
+ instanceTasks: async (params) => {
3699
+ const { approval_instance_ids, task_status } = params;
3700
+ await this.ensureTokenValid();
3701
+ const url = `/api/flow/v1/namespaces/${this.namespace}/instances/usertasks`;
3702
+ this.log(LoggerLevel.info, '[workflow.userTask.instanceTasks] Fetching workflow instance tasks');
3703
+ const res = await this.axiosInstance.get(url, {
3704
+ headers: this.authHeaders(true),
3705
+ params: task_status ? { task_status } : undefined,
3706
+ data: { approval_instance_ids }
3707
+ });
3708
+ this.log(LoggerLevel.debug, `[workflow.userTask.instanceTasks] Workflow instance tasks fetched: code=${res.data.code}`);
3709
+ this.log(LoggerLevel.trace, `[workflow.userTask.instanceTasks] Response: ${JSON.stringify(res.data)}`);
3710
+ return res.data;
3711
+ },
3712
+ /**
3713
+ * 获取任务列表。
3714
+ */
3715
+ tasks: async (params) => {
3716
+ await this.ensureTokenValid();
3717
+ const url = `/api/flow/v1/namespaces/${this.namespace}/user_tasks`;
3718
+ this.log(LoggerLevel.info, '[workflow.userTask.tasks] Fetching user tasks');
3719
+ const res = await this.axiosInstance.post(url, params, {
3720
+ headers: this.authHeaders(true)
3721
+ });
3722
+ this.log(LoggerLevel.debug, `[workflow.userTask.tasks] User tasks fetched: code=${res.data.code}`);
3723
+ this.log(LoggerLevel.trace, `[workflow.userTask.tasks] Response: ${JSON.stringify(res.data)}`);
3724
+ return res.data;
3725
+ },
3726
+ /**
3727
+ * 获取任务详情。
3728
+ */
3729
+ detail: async (params) => {
3730
+ const { task_id } = params;
3731
+ await this.ensureTokenValid();
3732
+ const url = `/api/flow/v1/namespaces/${this.namespace}/user_task/${task_id}/detail`;
3733
+ this.log(LoggerLevel.info, `[workflow.userTask.detail] Fetching user task: ${task_id}`);
3734
+ const res = await this.axiosInstance.get(url, {
3735
+ headers: this.authHeaders()
3736
+ });
3737
+ this.log(LoggerLevel.debug, `[workflow.userTask.detail] User task fetched: ${task_id}, code=${res.data.code}`);
3738
+ this.log(LoggerLevel.trace, `[workflow.userTask.detail] Response: ${JSON.stringify(res.data)}`);
3739
+ return res.data;
3740
+ },
3741
+ agree: async (params) => {
3742
+ const { approval_task_id, ...data } = params;
3743
+ await this.ensureTokenValid();
3744
+ const url = `/api/flow/v1/namespaces/${this.namespace}/user_task/${approval_task_id}/agree`;
3745
+ const res = await this.axiosInstance.post(url, data, { headers: this.authHeaders(true) });
3746
+ return res.data;
3747
+ },
3748
+ reject: async (params) => {
3749
+ const { approval_task_id, ...data } = params;
3750
+ await this.ensureTokenValid();
3751
+ const url = `/api/flow/v1/namespaces/${this.namespace}/user_task/${approval_task_id}/reject`;
3752
+ const res = await this.axiosInstance.post(url, data, { headers: this.authHeaders(true) });
3753
+ return res.data;
3754
+ },
3755
+ transfer: async (params) => {
3756
+ const { approval_task_id, ...data } = params;
3757
+ await this.ensureTokenValid();
3758
+ const url = `/api/flow/v1/namespaces/${this.namespace}/user_task/${approval_task_id}/trans`;
3759
+ const res = await this.axiosInstance.post(url, data, { headers: this.authHeaders(true) });
3760
+ return res.data;
3761
+ },
3762
+ addAssignee: async (params) => {
3763
+ const { approval_task_id, ...data } = params;
3764
+ await this.ensureTokenValid();
3765
+ const url = `/api/flow/v1/namespaces/${this.namespace}/user_task/${approval_task_id}/add_assignee`;
3766
+ const res = await this.axiosInstance.post(url, data, { headers: this.authHeaders(true) });
3767
+ return res.data;
3768
+ },
3769
+ cc: async (params) => {
3770
+ const { task_id, ...data } = params;
3771
+ await this.ensureTokenValid();
3772
+ const url = `/api/flow/v1/namespaces/${this.namespace}/user_task/${task_id}/cc`;
3773
+ const res = await this.axiosInstance.post(url, data, { headers: this.authHeaders(true) });
3774
+ return res.data;
3775
+ },
3776
+ expedite: async (params) => {
3777
+ const { task_id, ...data } = params;
3778
+ await this.ensureTokenValid();
3779
+ const url = `/api/flow/v1/namespaces/${this.namespace}/user_task/${task_id}/expediting`;
3780
+ const res = await this.axiosInstance.post(url, data, { headers: this.authHeaders(true) });
3781
+ return res.data;
3782
+ },
3783
+ cancelInstance: async (params) => {
3784
+ const { approval_instance_id, ...data } = params;
3785
+ await this.ensureTokenValid();
3786
+ const url = `/api/flow/v1/namespaces/${this.namespace}/instance/${approval_instance_id}/cancel`;
3787
+ const res = await this.axiosInstance.post(url, data, { headers: this.authHeaders(true) });
3788
+ return res.data;
3789
+ },
3790
+ rollbackPoints: async (params) => {
3791
+ const { task_id, ...data } = params;
3792
+ await this.ensureTokenValid();
3793
+ const url = `/api/flow/v1/namespaces/${this.namespace}/user_task/${task_id}/rollback_points`;
3794
+ const res = await this.axiosInstance.post(url, data, { headers: this.authHeaders(true) });
3795
+ return res.data;
3796
+ },
3797
+ rollback: async (params) => {
3798
+ const { task_id, ...data } = params;
3799
+ await this.ensureTokenValid();
3800
+ const url = `/api/flow/v1/namespaces/${this.namespace}/user_task/${task_id}/rollback`;
3801
+ const res = await this.axiosInstance.post(url, data, { headers: this.authHeaders(true) });
3802
+ return res.data;
3803
+ },
3804
+ startChat: async (params) => {
3805
+ const { task_id, ...data } = params;
3806
+ await this.ensureTokenValid();
3807
+ const url = `/api/flow/v1/namespaces/${this.namespace}/user_task/${task_id}/chat`;
3808
+ const res = await this.axiosInstance.post(url, data, { headers: this.authHeaders(true) });
3809
+ return res.data;
3810
+ }
3811
+ }
3812
+ };
2510
3813
  /**
2511
3814
  * 数据对象结构管理模块(Schema)
2512
3815
  */
@@ -2519,12 +3822,13 @@ class Client {
2519
3822
  */
2520
3823
  create: async (params) => {
2521
3824
  const { objects } = params;
3825
+ const requestObjects = normalizeSchemaObjectsForWrite(objects);
2522
3826
  await this.ensureTokenValid();
2523
3827
  const url = `/v1/namespaces/${this.namespace}/objects/batch_create`;
2524
3828
  this.log(LoggerLevel.info, `[schema.create] Creating ${objects.length} object(s)`);
2525
3829
  this.log(LoggerLevel.trace, `[schema.create] Request URL: ${this.axiosInstance.defaults.baseURL}${url}`);
2526
- this.log(LoggerLevel.trace, `[schema.create] Request Body: ${JSON.stringify({ objects }, null, 2)}`);
2527
- const res = await this.axiosInstance.post(url, { objects }, {
3830
+ this.log(LoggerLevel.trace, `[schema.create] Request Body: ${JSON.stringify({ objects: requestObjects }, null, 2)}`);
3831
+ const res = await this.axiosInstance.post(url, { objects: requestObjects }, {
2528
3832
  headers: {
2529
3833
  Authorization: `${this.accessToken}`,
2530
3834
  'Content-Type': 'application/json'
@@ -2581,12 +3885,13 @@ class Client {
2581
3885
  */
2582
3886
  update: async (params) => {
2583
3887
  const { objects } = params;
3888
+ const requestObjects = normalizeSchemaObjectsForWrite(objects);
2584
3889
  await this.ensureTokenValid();
2585
3890
  const url = `/v1/namespaces/${this.namespace}/objects/batch_update`;
2586
3891
  this.log(LoggerLevel.info, `[schema.update] Updating ${objects.length} object(s)`);
2587
3892
  this.log(LoggerLevel.trace, `[schema.update] Request URL: ${this.axiosInstance.defaults.baseURL}${url}`);
2588
- this.log(LoggerLevel.trace, `[schema.update] Request Body: ${JSON.stringify({ objects }, null, 2)}`);
2589
- const res = await this.axiosInstance.post(url, { objects }, {
3893
+ this.log(LoggerLevel.trace, `[schema.update] Request Body: ${JSON.stringify({ objects: requestObjects }, null, 2)}`);
3894
+ const res = await this.axiosInstance.post(url, { objects: requestObjects }, {
2590
3895
  headers: {
2591
3896
  Authorization: `${this.accessToken}`,
2592
3897
  'Content-Type': 'application/json'
@@ -2630,6 +3935,48 @@ class Client {
2630
3935
  this.log(LoggerLevel.debug, `[schema.delete] Objects deleted: code=${res.data.code}`);
2631
3936
  this.log(LoggerLevel.trace, `[schema.delete] Response: ${JSON.stringify(res.data)}`);
2632
3937
  return res.data;
3938
+ },
3939
+ /**
3940
+ * 校验 schema 写接口响应,覆盖请求级错误、data=null 静默失败、item 级失败。
3941
+ */
3942
+ checkResponse: checkSchemaResponse,
3943
+ /**
3944
+ * 返回 schema 写接口响应校验结果,不抛错。
3945
+ */
3946
+ validateResponse: validateSchemaResponse,
3947
+ /**
3948
+ * 按 schema 单批 10 个对象的限制分批执行。
3949
+ */
3950
+ batchExecute,
3951
+ /**
3952
+ * 创建对象空壳。会移除 fields,并将 display/search 临时指向 _id。
3953
+ */
3954
+ createShells: async (params) => {
3955
+ return createSchemaObjectShells(this, params.objects, params);
3956
+ },
3957
+ /**
3958
+ * 幂等添加字段:先读 metadata,跳过已存在字段,再调用 schema.update。
3959
+ */
3960
+ addFieldsIdempotent: async (params) => {
3961
+ return addFieldsIdempotent(this, params.object_name, params.fields, params);
3962
+ },
3963
+ /**
3964
+ * 三阶段创建对象:空壳 -> 基础字段 -> lookup -> reference_field -> final settings。
3965
+ */
3966
+ createWithStages: async (params) => {
3967
+ return createSchemaObjectsInStages(this, params.objects, params);
3968
+ },
3969
+ /**
3970
+ * 写后验证对象和字段,可选导出 Markdown。
3971
+ */
3972
+ verifyObjects: async (params) => {
3973
+ return verifySchemaObjects(this, params.object_names, params);
3974
+ },
3975
+ /**
3976
+ * 删除全部自定义对象。高风险操作,必须显式传 confirm: true。
3977
+ */
3978
+ deleteAllCustomObjects: async (params) => {
3979
+ return deleteAllCustomObjects(this, params);
2633
3980
  }
2634
3981
  };
2635
3982
  this.clientId = options.clientId;
@@ -2739,24 +4086,56 @@ class Client {
2739
4086
  this.log(LoggerLevel.debug, `[namespace] Current namespace: ${this.namespace}`);
2740
4087
  return this.namespace;
2741
4088
  }
4089
+ authHeaders(includeContentType = false) {
4090
+ return includeContentType
4091
+ ? { Authorization: `${this.accessToken}`, 'Content-Type': 'application/json' }
4092
+ : { Authorization: `${this.accessToken}` };
4093
+ }
2742
4094
  }
2743
4095
  const apaas = {
2744
4096
  Client
2745
4097
  };
2746
4098
 
4099
+ exports.BATCH_UPDATE_REQUIREMENTS = BATCH_UPDATE_REQUIREMENTS;
4100
+ exports.COLUMN_NAME_SEMANTIC_RULES = COLUMN_NAME_SEMANTIC_RULES;
2747
4101
  exports.CREATE_OBJECT_EXAMPLE = CREATE_OBJECT_EXAMPLE;
4102
+ exports.FIELD_SCHEMA_RULES = FIELD_SCHEMA_RULES;
2748
4103
  exports.FIELD_TYPES = FIELD_TYPES;
2749
4104
  exports.LANGUAGE_CODES = LANGUAGE_CODES;
4105
+ exports.OPTION_COLOR_CODE_BY_NAME = OPTION_COLOR_CODE_BY_NAME;
4106
+ exports.OPTION_COLOR_LIST = OPTION_COLOR_LIST;
4107
+ exports.OPTION_COLOR_NAME_BY_CODE = OPTION_COLOR_NAME_BY_CODE;
4108
+ exports.OPTION_COLOR_RULES = OPTION_COLOR_RULES;
4109
+ exports.SCHEMA_BATCH_SIZE = SCHEMA_BATCH_SIZE;
2750
4110
  exports.SCHEMA_GUIDELINES = SCHEMA_GUIDELINES;
4111
+ exports.SCHEMA_TYPE_BY_METADATA_TYPE = SCHEMA_TYPE_BY_METADATA_TYPE;
4112
+ exports.SCHEMA_TYPE_MISMATCHES = SCHEMA_TYPE_MISMATCHES;
4113
+ exports.SQL_CONSTRAINT_TO_SETTINGS = SQL_CONSTRAINT_TO_SETTINGS;
4114
+ exports.SQL_TYPE_TO_SCHEMA_TYPE = SQL_TYPE_TO_SCHEMA_TYPE;
2751
4115
  exports.SYSTEM_FIELDS = SYSTEM_FIELDS;
2752
4116
  exports.UPDATE_OBJECT_ADD_FIELD_EXAMPLE = UPDATE_OBJECT_ADD_FIELD_EXAMPLE;
2753
4117
  exports.UPDATE_OBJECT_REMOVE_FIELD_EXAMPLE = UPDATE_OBJECT_REMOVE_FIELD_EXAMPLE;
2754
4118
  exports.UPDATE_OBJECT_REPLACE_FIELD_EXAMPLE = UPDATE_OBJECT_REPLACE_FIELD_EXAMPLE;
2755
4119
  exports.UPDATE_OBJECT_SETTINGS_EXAMPLE = UPDATE_OBJECT_SETTINGS_EXAMPLE;
4120
+ exports.addFieldsIdempotent = addFieldsIdempotent;
2756
4121
  exports.apaas = apaas;
4122
+ exports.batchExecute = batchExecute;
4123
+ exports.buildFieldRemovalPlan = buildFieldRemovalPlan;
4124
+ exports.checkSchemaResponse = checkSchemaResponse;
2757
4125
  exports.createMultilingualText = createMultilingualText;
4126
+ exports.createSchemaObjectShells = createSchemaObjectShells;
4127
+ exports.createSchemaObjectsInStages = createSchemaObjectsInStages;
4128
+ exports.deleteAllCustomObjects = deleteAllCustomObjects;
2758
4129
  exports.extractMultilingualText = extractMultilingualText;
2759
4130
  exports.getCustomFieldTypes = getCustomFieldTypes;
2760
4131
  exports.getFieldType = getFieldType;
4132
+ exports.getOptionColor = getOptionColor;
4133
+ exports.getOptionColorCode = getOptionColorCode;
2761
4134
  exports.getSystemFields = getSystemFields;
2762
4135
  exports.isSystemField = isSystemField;
4136
+ exports.isSystemSchemaName = isSystemSchemaName;
4137
+ exports.normalizeOptionColorForSchema = normalizeOptionColorForSchema;
4138
+ exports.normalizeSchemaObjectsForWrite = normalizeSchemaObjectsForWrite;
4139
+ exports.splitSchemaFieldsByDependency = splitSchemaFieldsByDependency;
4140
+ exports.validateSchemaResponse = validateSchemaResponse;
4141
+ exports.verifySchemaObjects = verifySchemaObjects;