apaas-oapi-client 0.1.40 → 1.0.1

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
@@ -778,12 +778,34 @@ const OPTION_COLOR_LIST = [
778
778
  'blueMagenta',
779
779
  'grey'
780
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]));
781
794
  function getOptionColor(index) {
782
795
  if (!Number.isInteger(index) || index < 0) {
783
796
  throw new Error('Option color index must be a non-negative integer.');
784
797
  }
785
798
  return OPTION_COLOR_LIST[index % OPTION_COLOR_LIST.length];
786
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
+ }
787
809
  const OPTION_COLOR_RULES = {
788
810
  allowedColors: OPTION_COLOR_LIST,
789
811
  assignment: 'Use OPTION_COLOR_LIST in order and cycle from the beginning when options exceed 10.',
@@ -804,7 +826,10 @@ const OPTION_COLOR_RULES = {
804
826
  typeName: 'enum',
805
827
  optionsPath: 'type.settings.options',
806
828
  colorPath: 'type.settings.options[].color',
807
- sourcePath: 'type.settings.option_source',
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_type',
832
+ customSourceValue: 'local',
808
833
  globalOptionPath: 'type.settings.global_option_api_name'
809
834
  }
810
835
  };
@@ -870,7 +895,7 @@ const FIELD_SCHEMA_RULES = [
870
895
  settingsExample: {
871
896
  required: false,
872
897
  multiple: false,
873
- option_source: 'custom',
898
+ option_type: 'local',
874
899
  global_option_api_name: '',
875
900
  options: [
876
901
  {
@@ -889,7 +914,7 @@ const FIELD_SCHEMA_RULES = [
889
914
  }
890
915
  ]
891
916
  },
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(', ')}.`
917
+ notes: `Do not send create type as \`option\`. Metadata returns optionList/optionSource/globalOptionAPIName; create/update expects options and UI-editable custom options require option_type=local. The SDK also accepts legacy option_source=custom or option_type=custom and sends option_type=local. The SDK accepts metadata color names and sends OpenAPI color codes (${Object.entries(OPTION_COLOR_CODE_BY_NAME).map(([name, code]) => `${name}=${code}`).join(', ')}).`
893
918
  },
894
919
  {
895
920
  metadataType: 'boolean',
@@ -1033,6 +1058,35 @@ const SCHEMA_TYPE_MISMATCHES = FIELD_SCHEMA_RULES
1033
1058
  metadataType: rule.metadataType,
1034
1059
  schemaType: rule.schemaType
1035
1060
  }));
1061
+ const SQL_TYPE_TO_SCHEMA_TYPE = [
1062
+ { sqlPattern: 'VARCHAR\\(\\d+\\)|CHAR\\(\\d+\\)', schemaType: 'text', settingsMapping: 'max_length from (n), multiline: false' },
1063
+ { sqlPattern: 'TEXT|LONGTEXT|MEDIUMTEXT|TINYTEXT|CLOB', schemaType: 'text', settingsMapping: 'multiline: true, max_length: 100000' },
1064
+ { sqlPattern: 'INT|INTEGER|BIGINT|SMALLINT|TINYINT(?!\\(1\\))|MEDIUMINT|SERIAL', schemaType: 'bigint', settingsMapping: 'required/unique from constraints' },
1065
+ { sqlPattern: 'FLOAT|DOUBLE|REAL', schemaType: 'float', settingsMapping: 'decimal_places_number: 2' },
1066
+ { sqlPattern: 'DECIMAL\\(\\d+,\\d+\\)|NUMERIC\\(\\d+,\\d+\\)', schemaType: 'decimal', settingsMapping: 'decimal_places from scale (s)' },
1067
+ { sqlPattern: 'DATE', schemaType: 'date', settingsMapping: 'required from constraints' },
1068
+ { sqlPattern: 'DATETIME|TIMESTAMP', schemaType: 'datetime', settingsMapping: 'required from constraints' },
1069
+ { sqlPattern: 'BOOLEAN|BOOL|TINYINT\\(1\\)|BIT', schemaType: 'boolean', settingsMapping: 'default_value from DEFAULT' },
1070
+ { sqlPattern: 'ENUM\\(.*\\)', schemaType: 'enum', settingsMapping: 'options from enum values, colors auto-assigned with getOptionColor(index)' },
1071
+ { sqlPattern: 'BLOB|BINARY|VARBINARY|LONGBLOB|MEDIUMBLOB', schemaType: 'attachment', settingsMapping: 'any_type: true' },
1072
+ { sqlPattern: 'JSON', schemaType: 'richText', settingsMapping: 'only when JSON stores rich text content' }
1073
+ ];
1074
+ const COLUMN_NAME_SEMANTIC_RULES = [
1075
+ { columnPattern: '(^|_)(e?mail)(s?$|_)', schemaType: 'email', notes: 'Column name contains email/mail' },
1076
+ { columnPattern: '(^|_)(phone|mobile|tel)(s?$|_)', schemaType: 'phone', notes: 'Column name contains phone/mobile/tel' },
1077
+ { columnPattern: '(^|_)(avatar|logo|profile_image)(s?$|_)', schemaType: 'avatar', notes: 'Column name contains avatar/logo' },
1078
+ { columnPattern: '(^|_)(region|province|city|district|address)(s?$|_)', schemaType: 'region', notes: 'Column name implies geographic data' }
1079
+ ];
1080
+ const SQL_CONSTRAINT_TO_SETTINGS = [
1081
+ { sqlConstraint: 'NOT NULL', settingsField: 'required', settingsValue: 'true', notes: 'Maps to required: true' },
1082
+ { sqlConstraint: 'UNIQUE', settingsField: 'unique', settingsValue: 'true', notes: 'Maps to unique: true' },
1083
+ { sqlConstraint: 'PRIMARY KEY', settingsField: '-', settingsValue: '-', notes: 'Ignored: aPaaS uses system _id' },
1084
+ { sqlConstraint: 'AUTO_INCREMENT', settingsField: '-', settingsValue: '-', notes: 'Ignored: aPaaS _id auto-increments. For business serial numbers, use auto_number' },
1085
+ { sqlConstraint: 'FOREIGN KEY', settingsField: 'referenced_object_api_name', settingsValue: '(target table)', notes: 'Convert to lookup field' },
1086
+ { sqlConstraint: 'DEFAULT', settingsField: 'default_value', settingsValue: '(value)', notes: 'Only boolean type supports default_value in aPaaS' },
1087
+ { sqlConstraint: 'CHECK', settingsField: '-', settingsValue: '-', notes: 'Not supported in aPaaS, handle in application logic' },
1088
+ { sqlConstraint: 'INDEX', settingsField: '-', settingsValue: '-', notes: 'Not applicable, aPaaS manages indexing automatically' }
1089
+ ];
1036
1090
  const BATCH_UPDATE_REQUIREMENTS = {
1037
1091
  add: 'Use operator=add with full field definition.',
1038
1092
  replace: 'Use operator=replace and include full `type` (name + settings). Label-only replace fails.',
@@ -1044,6 +1098,493 @@ const BATCH_UPDATE_REQUIREMENTS = {
1044
1098
  referenceFieldConstraint: 'reference_field only works with single lookup (`multiple: false`).'
1045
1099
  };
1046
1100
 
1101
+ const SCHEMA_BATCH_SIZE = 10;
1102
+ function isSystemSchemaName(apiName) {
1103
+ return apiName.startsWith('_');
1104
+ }
1105
+ function normalizeSchemaObjectsForWrite(objects) {
1106
+ return objects.map((object) => {
1107
+ if (!object.fields) {
1108
+ return { ...object };
1109
+ }
1110
+ return {
1111
+ ...object,
1112
+ fields: object.fields.map((field) => normalizeSchemaFieldForWrite(field))
1113
+ };
1114
+ });
1115
+ }
1116
+ function normalizeSchemaFieldForWrite(field) {
1117
+ var _a, _b;
1118
+ const settings = (_a = field.type) === null || _a === void 0 ? void 0 : _a.settings;
1119
+ if (((_b = field.type) === null || _b === void 0 ? void 0 : _b.name) !== 'enum' || !Array.isArray(settings === null || settings === void 0 ? void 0 : settings.options)) {
1120
+ return { ...field };
1121
+ }
1122
+ const normalizedSettings = { ...settings };
1123
+ if (normalizedSettings.option_type === 'custom' || normalizedSettings.option_source === 'custom') {
1124
+ normalizedSettings.option_type = 'local';
1125
+ delete normalizedSettings.option_source;
1126
+ }
1127
+ return {
1128
+ ...field,
1129
+ type: {
1130
+ ...field.type,
1131
+ settings: {
1132
+ ...normalizedSettings,
1133
+ options: normalizedSettings.options.map((option) => {
1134
+ if (!option || typeof option !== 'object' || !('color' in option)) {
1135
+ return option;
1136
+ }
1137
+ return {
1138
+ ...option,
1139
+ color: normalizeOptionColorForSchema(option.color)
1140
+ };
1141
+ })
1142
+ }
1143
+ }
1144
+ };
1145
+ }
1146
+ function validateSchemaResponse(result, context = 'schema', options = {}) {
1147
+ var _a, _b, _c, _d;
1148
+ const failures = [];
1149
+ if (!result) {
1150
+ failures.push({
1151
+ layer: 'request',
1152
+ context,
1153
+ code: 'NO_RESULT',
1154
+ message: `${context} request failed: empty response`
1155
+ });
1156
+ return { ok: false, result, failures };
1157
+ }
1158
+ if (String(result.code) !== '0') {
1159
+ failures.push({
1160
+ layer: 'request',
1161
+ context,
1162
+ code: result.code === undefined ? 'undefined' : String(result.code),
1163
+ message: `${context} request failed: ${result.code} ${result.msg || result.message || ''}`.trim()
1164
+ });
1165
+ }
1166
+ if (result.data === null && !options.allowDataNull) {
1167
+ failures.push({
1168
+ layer: 'silent',
1169
+ context,
1170
+ code: 'DATA_NULL',
1171
+ message: `${context} silently failed: result.code is 0 but result.data is null`
1172
+ });
1173
+ }
1174
+ for (const item of ((_a = result.data) === null || _a === void 0 ? void 0 : _a.items) || []) {
1175
+ const itemCode = (_b = item.status) === null || _b === void 0 ? void 0 : _b.code;
1176
+ if (itemCode === undefined && options.requireItemStatus) {
1177
+ failures.push({
1178
+ layer: 'item',
1179
+ context,
1180
+ code: 'MISSING_STATUS',
1181
+ apiName: getItemApiName(item),
1182
+ item,
1183
+ message: `${context} item status is missing: ${getItemApiName(item) || 'unknown'}`
1184
+ });
1185
+ continue;
1186
+ }
1187
+ if (itemCode !== undefined && String(itemCode) !== '0') {
1188
+ failures.push({
1189
+ layer: 'item',
1190
+ context,
1191
+ code: String(itemCode),
1192
+ apiName: getItemApiName(item),
1193
+ item,
1194
+ 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()
1195
+ });
1196
+ }
1197
+ }
1198
+ return { ok: failures.length === 0, result, failures };
1199
+ }
1200
+ function checkSchemaResponse(result, context = 'schema', options) {
1201
+ const validation = validateSchemaResponse(result, context, options);
1202
+ if (!validation.ok) {
1203
+ const error = new Error(validation.failures.map((failure) => failure.message).join('; '));
1204
+ error.failures = validation.failures;
1205
+ throw error;
1206
+ }
1207
+ return result;
1208
+ }
1209
+ async function batchExecute(items, fn, options = {}) {
1210
+ var _a;
1211
+ const batchSize = (_a = options.batchSize) !== null && _a !== void 0 ? _a : SCHEMA_BATCH_SIZE;
1212
+ if (!Number.isInteger(batchSize) || batchSize <= 0) {
1213
+ throw new Error('batchSize must be a positive integer.');
1214
+ }
1215
+ const batchCount = Math.ceil(items.length / batchSize);
1216
+ const results = [];
1217
+ const failures = [];
1218
+ for (let start = 0; start < items.length; start += batchSize) {
1219
+ const batch = items.slice(start, start + batchSize);
1220
+ const batchIndex = Math.floor(start / batchSize) + 1;
1221
+ const info = {
1222
+ batchIndex,
1223
+ batchCount,
1224
+ start,
1225
+ end: start + batch.length,
1226
+ batchSize: batch.length
1227
+ };
1228
+ const context = `${options.context || 'schema batch'} [${batchIndex}/${batchCount}]`;
1229
+ try {
1230
+ const result = await fn(batch, info);
1231
+ results.push(result);
1232
+ if (options.checkResponse !== false) {
1233
+ const validation = validateSchemaResponse(result, context, options.responseOptions);
1234
+ failures.push(...validation.failures);
1235
+ if (!validation.ok && !options.continueOnError) {
1236
+ const error = new Error(validation.failures.map((failure) => failure.message).join('; '));
1237
+ error.failures = validation.failures;
1238
+ throw error;
1239
+ }
1240
+ }
1241
+ }
1242
+ catch (error) {
1243
+ if (!options.continueOnError) {
1244
+ throw error;
1245
+ }
1246
+ failures.push({
1247
+ layer: 'request',
1248
+ context,
1249
+ code: 'EXCEPTION',
1250
+ message: error instanceof Error ? error.message : String(error)
1251
+ });
1252
+ }
1253
+ }
1254
+ return {
1255
+ ok: failures.length === 0,
1256
+ total: items.length,
1257
+ batchSize,
1258
+ batchCount,
1259
+ results,
1260
+ failures
1261
+ };
1262
+ }
1263
+ function splitSchemaFieldsByDependency(fields) {
1264
+ const baseFields = [];
1265
+ const lookupFields = [];
1266
+ const referenceFields = [];
1267
+ for (const field of fields) {
1268
+ const typeName = getSchemaFieldTypeName(field);
1269
+ if (typeName === 'reference_field' || typeName === 'referenceField') {
1270
+ referenceFields.push(field);
1271
+ }
1272
+ else if (typeName === 'lookup' || typeName === 'lookup_multi') {
1273
+ lookupFields.push(field);
1274
+ }
1275
+ else {
1276
+ baseFields.push(field);
1277
+ }
1278
+ }
1279
+ return { baseFields, lookupFields, referenceFields };
1280
+ }
1281
+ async function createSchemaObjectShells(client, objects, options = {}) {
1282
+ assertNoSystemObjects(objects.map((object) => object.api_name), 'createSchemaObjectShells');
1283
+ const existingNames = options.skipExisting === false
1284
+ ? new Set()
1285
+ : await getExistingObjectNames(client);
1286
+ const shells = objects
1287
+ .filter((object) => !existingNames.has(object.api_name))
1288
+ .map(toShellObject);
1289
+ const result = {
1290
+ requested: objects.map((object) => object.api_name),
1291
+ created: shells.map((object) => object.api_name),
1292
+ skippedExisting: objects
1293
+ .filter((object) => existingNames.has(object.api_name))
1294
+ .map((object) => object.api_name)
1295
+ };
1296
+ if (shells.length === 0) {
1297
+ return result;
1298
+ }
1299
+ result.batches = await batchExecute(shells, (batch) => client.schema.create({ objects: batch }), {
1300
+ batchSize: options.batchSize,
1301
+ context: options.context || 'schema.createShells',
1302
+ continueOnError: options.continueOnError,
1303
+ checkResponse: options.checkResponse,
1304
+ responseOptions: options.responseOptions
1305
+ });
1306
+ return result;
1307
+ }
1308
+ async function addFieldsIdempotent(client, objectName, fieldsToAdd, options = {}) {
1309
+ assertNoSystemObjects([objectName], 'addFieldsIdempotent');
1310
+ assertNoSystemFields(fieldsToAdd.map((field) => field.api_name), `addFieldsIdempotent(${objectName})`);
1311
+ const existingNames = options.skipExisting === false
1312
+ ? new Set()
1313
+ : await getExistingFieldNames(client, objectName);
1314
+ const newFields = fieldsToAdd.filter((field) => !existingNames.has(field.api_name)).map(toAddField);
1315
+ const skippedFields = fieldsToAdd
1316
+ .filter((field) => existingNames.has(field.api_name))
1317
+ .map((field) => field.api_name);
1318
+ if (newFields.length === 0) {
1319
+ return {
1320
+ objectName,
1321
+ addedFields: [],
1322
+ skippedFields
1323
+ };
1324
+ }
1325
+ const result = await client.schema.update({
1326
+ objects: [{ api_name: objectName, fields: newFields }]
1327
+ });
1328
+ if (options.checkResponse !== false) {
1329
+ checkSchemaResponse(result, options.context || `schema.addFieldsIdempotent(${objectName})`, options.responseOptions);
1330
+ }
1331
+ return {
1332
+ objectName,
1333
+ addedFields: newFields.map((field) => field.api_name),
1334
+ skippedFields,
1335
+ result
1336
+ };
1337
+ }
1338
+ async function createSchemaObjectsInStages(client, objects, options = {}) {
1339
+ const context = options.context || 'schema.createWithStages';
1340
+ const shells = await createSchemaObjectShells(client, objects, {
1341
+ ...options,
1342
+ context: `${context}.shells`
1343
+ });
1344
+ const baseFields = [];
1345
+ const lookupFields = [];
1346
+ const referenceFields = [];
1347
+ for (const object of objects) {
1348
+ assertNoSystemFields((object.fields || []).map((field) => field.api_name), `${context}(${object.api_name})`);
1349
+ const split = splitSchemaFieldsByDependency(object.fields || []);
1350
+ if (split.baseFields.length > 0) {
1351
+ baseFields.push(await addFieldsIdempotent(client, object.api_name, split.baseFields, {
1352
+ context: `${context}.baseFields(${object.api_name})`,
1353
+ skipExisting: options.skipExisting,
1354
+ checkResponse: options.checkResponse,
1355
+ responseOptions: options.responseOptions
1356
+ }));
1357
+ }
1358
+ if (split.lookupFields.length > 0) {
1359
+ lookupFields.push(await addFieldsIdempotent(client, object.api_name, split.lookupFields, {
1360
+ context: `${context}.lookupFields(${object.api_name})`,
1361
+ skipExisting: options.skipExisting,
1362
+ checkResponse: options.checkResponse,
1363
+ responseOptions: options.responseOptions
1364
+ }));
1365
+ }
1366
+ if (split.referenceFields.length > 0) {
1367
+ referenceFields.push(await addFieldsIdempotent(client, object.api_name, split.referenceFields, {
1368
+ context: `${context}.referenceFields(${object.api_name})`,
1369
+ skipExisting: options.skipExisting,
1370
+ checkResponse: options.checkResponse,
1371
+ responseOptions: options.responseOptions
1372
+ }));
1373
+ }
1374
+ }
1375
+ const result = {
1376
+ shells,
1377
+ baseFields,
1378
+ lookupFields,
1379
+ referenceFields
1380
+ };
1381
+ const finalSettings = options.updateFinalSettings === false
1382
+ ? []
1383
+ : objects
1384
+ .filter((object) => object.settings && Object.keys(object.settings).length > 0)
1385
+ .map((object) => ({ api_name: object.api_name, settings: object.settings }));
1386
+ if (finalSettings.length > 0) {
1387
+ result.finalSettings = await batchExecute(finalSettings, (batch) => client.schema.update({ objects: batch }), {
1388
+ batchSize: options.batchSize,
1389
+ context: `${context}.finalSettings`,
1390
+ continueOnError: options.continueOnError,
1391
+ checkResponse: options.checkResponse,
1392
+ responseOptions: {
1393
+ ...options.responseOptions,
1394
+ allowDataNull: true
1395
+ }
1396
+ });
1397
+ }
1398
+ if (options.verify !== false) {
1399
+ result.verification = await verifySchemaObjects(client, objects.map((object) => object.api_name), { includeMarkdown: options.includeMarkdown });
1400
+ }
1401
+ return result;
1402
+ }
1403
+ async function verifySchemaObjects(client, objectNames, options = {}) {
1404
+ var _a, _b;
1405
+ const allObjects = await client.object.listWithIterator();
1406
+ const objects = [];
1407
+ for (const objectName of objectNames) {
1408
+ const exists = Boolean((_a = allObjects.items) === null || _a === void 0 ? void 0 : _a.find((object) => object.apiName === objectName || object.api_name === objectName));
1409
+ if (!exists) {
1410
+ objects.push({
1411
+ objectName,
1412
+ exists: false,
1413
+ fields: [],
1414
+ customFields: []
1415
+ });
1416
+ continue;
1417
+ }
1418
+ const fieldResult = await client.object.metadata.fields({ object_name: objectName });
1419
+ if (fieldResult.code !== undefined && String(fieldResult.code) !== '0') {
1420
+ throw new Error(`verifySchemaObjects(${objectName}) failed: ${fieldResult.code} ${fieldResult.msg || fieldResult.message || ''}`.trim());
1421
+ }
1422
+ const fields = ((_b = fieldResult.data) === null || _b === void 0 ? void 0 : _b.fields) || [];
1423
+ objects.push({
1424
+ objectName,
1425
+ exists: true,
1426
+ fields,
1427
+ customFields: fields.filter((field) => !isSystemSchemaName(field.apiName || field.api_name || ''))
1428
+ });
1429
+ }
1430
+ const result = { objects };
1431
+ if (options.includeMarkdown && client.object.metadata.export2markdown) {
1432
+ result.markdown = await client.object.metadata.export2markdown({ object_names: objectNames });
1433
+ }
1434
+ return result;
1435
+ }
1436
+ function buildFieldRemovalPlan(fieldsByObject) {
1437
+ const referenceFieldObjects = new Map();
1438
+ const lookupObjects = new Map();
1439
+ const otherFieldObjects = new Map();
1440
+ for (const [objectName, fields] of Object.entries(fieldsByObject)) {
1441
+ for (const field of fields) {
1442
+ const apiName = field.api_name || field.apiName;
1443
+ if (!apiName || isSystemSchemaName(apiName)) {
1444
+ continue;
1445
+ }
1446
+ const target = getRemovalGroup(field, referenceFieldObjects, lookupObjects, otherFieldObjects);
1447
+ const existing = target.get(objectName) || [];
1448
+ existing.push({ operator: 'remove', api_name: apiName });
1449
+ target.set(objectName, existing);
1450
+ }
1451
+ }
1452
+ return {
1453
+ referenceFieldObjects: mapRemovalObjects(referenceFieldObjects),
1454
+ lookupObjects: mapRemovalObjects(lookupObjects),
1455
+ otherFieldObjects: mapRemovalObjects(otherFieldObjects)
1456
+ };
1457
+ }
1458
+ async function deleteAllCustomObjects(client, options = {}) {
1459
+ var _a;
1460
+ if (options.confirm !== true) {
1461
+ throw new Error('deleteAllCustomObjects requires { confirm: true }.');
1462
+ }
1463
+ const context = options.context || 'schema.deleteAllCustomObjects';
1464
+ const allObjects = await client.object.listWithIterator();
1465
+ const customObjects = (allObjects.items || []).filter((object) => !isSystemSchemaName(object.apiName || object.api_name || ''));
1466
+ const objectNames = customObjects.map((object) => object.apiName || object.api_name);
1467
+ const fieldsByObject = {};
1468
+ for (const objectName of objectNames) {
1469
+ const fieldResult = await client.object.metadata.fields({ object_name: objectName });
1470
+ if (fieldResult.code !== undefined && String(fieldResult.code) !== '0') {
1471
+ throw new Error(`${context}.readFields(${objectName}) failed: ${fieldResult.code} ${fieldResult.msg || fieldResult.message || ''}`.trim());
1472
+ }
1473
+ fieldsByObject[objectName] = ((_a = fieldResult.data) === null || _a === void 0 ? void 0 : _a.fields) || [];
1474
+ }
1475
+ const removalPlan = buildFieldRemovalPlan(fieldsByObject);
1476
+ const fieldRemovalResults = [];
1477
+ for (const [phase, updateObjects] of [
1478
+ ['referenceFields', removalPlan.referenceFieldObjects],
1479
+ ['lookupFields', removalPlan.lookupObjects],
1480
+ ['otherFields', options.removeOtherFields ? removalPlan.otherFieldObjects : []]
1481
+ ]) {
1482
+ if (updateObjects.length === 0) {
1483
+ continue;
1484
+ }
1485
+ fieldRemovalResults.push(await batchExecute(updateObjects, (batch) => client.schema.update({ objects: batch }), {
1486
+ batchSize: options.batchSize,
1487
+ context: `${context}.${phase}`,
1488
+ continueOnError: options.continueOnError,
1489
+ checkResponse: options.checkResponse,
1490
+ responseOptions: options.responseOptions
1491
+ }));
1492
+ }
1493
+ const result = {
1494
+ deletedObjects: objectNames,
1495
+ removalPlan,
1496
+ fieldRemovalResults
1497
+ };
1498
+ if (objectNames.length > 0) {
1499
+ result.deleteResults = await batchExecute(objectNames, (batch) => client.schema.delete({ api_names: batch }), {
1500
+ batchSize: options.batchSize,
1501
+ context: `${context}.deleteObjects`,
1502
+ continueOnError: options.continueOnError,
1503
+ checkResponse: options.checkResponse,
1504
+ responseOptions: {
1505
+ ...options.responseOptions,
1506
+ allowDataNull: true
1507
+ }
1508
+ });
1509
+ }
1510
+ if (options.verify !== false) {
1511
+ const after = await client.object.listWithIterator();
1512
+ result.remainingObjects = (after.items || [])
1513
+ .filter((object) => objectNames.includes(object.apiName || object.api_name))
1514
+ .map((object) => object.apiName || object.api_name);
1515
+ if (result.remainingObjects.length > 0) {
1516
+ throw new Error(`${context} verification failed: remaining objects ${result.remainingObjects.join(', ')}`);
1517
+ }
1518
+ }
1519
+ return result;
1520
+ }
1521
+ function getItemApiName(item) {
1522
+ return item.api_name || item.apiName || item.name;
1523
+ }
1524
+ function getSchemaFieldTypeName(field) {
1525
+ const type = field.type;
1526
+ if (typeof type === 'string') {
1527
+ return type;
1528
+ }
1529
+ return type === null || type === void 0 ? void 0 : type.name;
1530
+ }
1531
+ function toAddField(field) {
1532
+ return {
1533
+ ...field,
1534
+ operator: 'add'
1535
+ };
1536
+ }
1537
+ function toShellObject(object) {
1538
+ const { fields: _fields, ...shell } = object;
1539
+ const originalSettings = object.settings || {};
1540
+ return {
1541
+ ...shell,
1542
+ settings: {
1543
+ ...originalSettings,
1544
+ display_name: '_id',
1545
+ allow_search_fields: ['_id'],
1546
+ search_layout: []
1547
+ }
1548
+ };
1549
+ }
1550
+ async function getExistingObjectNames(client) {
1551
+ const result = await client.object.listWithIterator();
1552
+ return new Set((result.items || []).map((object) => object.apiName || object.api_name).filter(Boolean));
1553
+ }
1554
+ async function getExistingFieldNames(client, objectName) {
1555
+ var _a;
1556
+ const result = await client.object.metadata.fields({ object_name: objectName });
1557
+ if (result.code !== undefined && String(result.code) !== '0') {
1558
+ throw new Error(`getExistingFieldNames(${objectName}) failed: ${result.code} ${result.msg || result.message || ''}`.trim());
1559
+ }
1560
+ return new Set((((_a = result.data) === null || _a === void 0 ? void 0 : _a.fields) || []).map((field) => field.apiName || field.api_name).filter(Boolean));
1561
+ }
1562
+ function assertNoSystemObjects(apiNames, context) {
1563
+ const systemNames = apiNames.filter(isSystemSchemaName);
1564
+ if (systemNames.length > 0) {
1565
+ throw new Error(`${context} cannot modify system objects: ${systemNames.join(', ')}`);
1566
+ }
1567
+ }
1568
+ function assertNoSystemFields(apiNames, context) {
1569
+ const systemNames = apiNames.filter(isSystemSchemaName);
1570
+ if (systemNames.length > 0) {
1571
+ throw new Error(`${context} cannot modify system fields: ${systemNames.join(', ')}`);
1572
+ }
1573
+ }
1574
+ function getRemovalGroup(field, referenceFieldObjects, lookupObjects, otherFieldObjects) {
1575
+ const typeName = getSchemaFieldTypeName(field);
1576
+ if (typeName === 'reference_field' || typeName === 'referenceField') {
1577
+ return referenceFieldObjects;
1578
+ }
1579
+ if (typeName === 'lookup' || typeName === 'lookup_multi') {
1580
+ return lookupObjects;
1581
+ }
1582
+ return otherFieldObjects;
1583
+ }
1584
+ function mapRemovalObjects(fieldsByObject) {
1585
+ return Array.from(fieldsByObject.entries()).map(([api_name, fields]) => ({ api_name, fields }));
1586
+ }
1587
+
1047
1588
  /**
1048
1589
  * 判断错误是否可重试
1049
1590
  */
@@ -1690,8 +2231,70 @@ class Client {
1690
2231
  total: total,
1691
2232
  msg: res.msg
1692
2233
  };
2234
+ },
2235
+ /**
2236
+ * 跨对象搜索记录
2237
+ * @description 在最多 5 个对象中按搜索词查询记录
2238
+ * @param params 请求体,包含 q、search_objects、page_token、page_size、metadata 等
2239
+ */
2240
+ recordsAcrossObjects: async (params) => {
2241
+ await this.ensureTokenValid();
2242
+ const url = `/v1/namespaces/${this.namespace}/objects/records/search`;
2243
+ this.log(LoggerLevel.info, `[object.search.recordsAcrossObjects] Searching records: q=${params.q}`);
2244
+ const res = await this.axiosInstance.post(url, params, {
2245
+ headers: this.authHeaders(true)
2246
+ });
2247
+ this.log(LoggerLevel.debug, `[object.search.recordsAcrossObjects] Records searched: code=${res.data.code}`);
2248
+ this.log(LoggerLevel.trace, `[object.search.recordsAcrossObjects] Response: ${JSON.stringify(res.data)}`);
2249
+ return res.data;
2250
+ },
2251
+ /**
2252
+ * 跨对象搜索记录 - 自动 page_token 分页
2253
+ */
2254
+ recordsAcrossObjectsWithIterator: async (params) => {
2255
+ var _a, _b, _c, _d, _e, _f;
2256
+ const pageSize = params.page_size || 20;
2257
+ let pageToken = params.page_token || '';
2258
+ let hasMore = true;
2259
+ const items = [];
2260
+ let lastResponse = null;
2261
+ while (hasMore) {
2262
+ const res = await this.object.search.recordsAcrossObjects({
2263
+ ...params,
2264
+ page_size: pageSize,
2265
+ page_token: pageToken
2266
+ });
2267
+ if (res.code !== '0') {
2268
+ this.log(LoggerLevel.error, `[object.search.recordsAcrossObjectsWithIterator] Error searching records: code=${res.code}, msg=${res.msg}`);
2269
+ throw new Error(res.msg || `Search failed with code ${res.code}`);
2270
+ }
2271
+ 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) || [];
2272
+ if (Array.isArray(pageItems)) {
2273
+ items.push(...pageItems);
2274
+ }
2275
+ lastResponse = res;
2276
+ 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) || '';
2277
+ hasMore = Boolean(((_f = res.data) === null || _f === void 0 ? void 0 : _f.has_more) && nextPageToken);
2278
+ pageToken = nextPageToken;
2279
+ }
2280
+ return { items, lastResponse };
1693
2281
  }
1694
2282
  },
2283
+ /**
2284
+ * 执行 OQL
2285
+ * @description 执行对象查询语言,支持匿名参数 args 和具名参数 namedArgs
2286
+ */
2287
+ oql: async (params) => {
2288
+ await this.ensureTokenValid();
2289
+ const url = `/api/data/v1/namespaces/${this.namespace}/records/query`;
2290
+ this.log(LoggerLevel.info, '[object.oql] Executing OQL');
2291
+ const res = await this.axiosInstance.post(url, params, {
2292
+ headers: this.authHeaders(true)
2293
+ });
2294
+ this.log(LoggerLevel.debug, `[object.oql] OQL executed: code=${res.data.code}`);
2295
+ this.log(LoggerLevel.trace, `[object.oql] Response: ${JSON.stringify(res.data)}`);
2296
+ return res.data;
2297
+ },
1695
2298
  create: {
1696
2299
  /**
1697
2300
  * 单条记录创建
@@ -2069,6 +2672,73 @@ class Client {
2069
2672
  }
2070
2673
  }
2071
2674
  };
2675
+ /**
2676
+ * 常量对象模块
2677
+ */
2678
+ this.constant = {
2679
+ /**
2680
+ * 查询常量对象列表。object_name 可取 _currency、_country、_timeZone。
2681
+ */
2682
+ records: async (params) => {
2683
+ const { object_name, data = {} } = params;
2684
+ await this.ensureTokenValid();
2685
+ const url = `/api/data/v1/namespaces/${this.namespace}/objects/${object_name}/records`;
2686
+ this.log(LoggerLevel.info, `[constant.records] Fetching constant records: ${object_name}`);
2687
+ const res = await this.axiosInstance.post(url, data, {
2688
+ headers: this.authHeaders(true)
2689
+ });
2690
+ this.log(LoggerLevel.debug, `[constant.records] Constant records fetched: ${object_name}, code=${res.data.code}`);
2691
+ this.log(LoggerLevel.trace, `[constant.records] Response: ${JSON.stringify(res.data)}`);
2692
+ return res.data;
2693
+ },
2694
+ /**
2695
+ * 查询常量对象单条记录详情。
2696
+ */
2697
+ record: async (params) => {
2698
+ const { object_name, record_id } = params;
2699
+ await this.ensureTokenValid();
2700
+ const url = `/api/data/v1/namespaces/${this.namespace}/objects/${object_name}/${record_id}`;
2701
+ this.log(LoggerLevel.info, `[constant.record] Fetching constant record: ${object_name}.${record_id}`);
2702
+ const res = await this.axiosInstance.get(url, {
2703
+ headers: this.authHeaders()
2704
+ });
2705
+ this.log(LoggerLevel.debug, `[constant.record] Constant record fetched: ${object_name}.${record_id}, code=${res.data.code}`);
2706
+ this.log(LoggerLevel.trace, `[constant.record] Response: ${JSON.stringify(res.data)}`);
2707
+ return res.data;
2708
+ },
2709
+ metadata: {
2710
+ /**
2711
+ * 获取常量对象元数据。
2712
+ */
2713
+ fields: async (params) => {
2714
+ const { object_name } = params;
2715
+ await this.ensureTokenValid();
2716
+ const url = `/api/data/v1/namespaces/${this.namespace}/meta/objects/${object_name}`;
2717
+ this.log(LoggerLevel.info, `[constant.metadata.fields] Fetching constant metadata: ${object_name}`);
2718
+ const res = await this.axiosInstance.get(url, {
2719
+ headers: this.authHeaders()
2720
+ });
2721
+ this.log(LoggerLevel.debug, `[constant.metadata.fields] Constant metadata fetched: ${object_name}, code=${res.data.code}`);
2722
+ this.log(LoggerLevel.trace, `[constant.metadata.fields] Response: ${JSON.stringify(res.data)}`);
2723
+ return res.data;
2724
+ },
2725
+ /**
2726
+ * 获取常量对象字段详情。
2727
+ */
2728
+ field: async (params) => {
2729
+ const { object_name, field_name } = params;
2730
+ await this.ensureTokenValid();
2731
+ const url = `/api/data/v1/namespaces/${this.namespace}/meta/objects/${object_name}/fields/${field_name}`;
2732
+ this.log(LoggerLevel.info, `[constant.metadata.field] Fetching constant field metadata: ${object_name}.${field_name}`);
2733
+ const res = await this.axiosInstance.get(url, {
2734
+ headers: this.authHeaders()
2735
+ });
2736
+ this.log(LoggerLevel.debug, `[constant.metadata.field] Constant field metadata fetched: ${object_name}.${field_name}, code=${res.data.code}`);
2737
+ this.log(LoggerLevel.trace, `[constant.metadata.field] Response: ${JSON.stringify(res.data)}`);
2738
+ return res.data;
2739
+ }
2740
+ }
2741
+ };
2072
2742
  /**
2073
2743
  * 部门 ID 交换模块
2074
2744
  */
@@ -2295,6 +2965,71 @@ class Client {
2295
2965
  return result;
2296
2966
  }
2297
2967
  };
2968
+ /**
2969
+ * 集成模块
2970
+ */
2971
+ this.integration = {
2972
+ lark: {
2973
+ /**
2974
+ * 获取默认飞书集成 tenantAccessToken。
2975
+ */
2976
+ defaultTenantAccessToken: async () => {
2977
+ await this.ensureTokenValid();
2978
+ const url = `/api/integration/v1/namespaces/${this.namespace}/defaultLark/tenantAccessToken`;
2979
+ this.log(LoggerLevel.info, '[integration.lark.defaultTenantAccessToken] Fetching default tenant token');
2980
+ const res = await this.axiosInstance.get(url, {
2981
+ headers: this.authHeaders()
2982
+ });
2983
+ this.log(LoggerLevel.debug, `[integration.lark.defaultTenantAccessToken] Token fetched: code=${res.data.code}`);
2984
+ this.log(LoggerLevel.trace, `[integration.lark.defaultTenantAccessToken] Response: ${JSON.stringify(res.data)}`);
2985
+ return res.data;
2986
+ },
2987
+ /**
2988
+ * 获取默认飞书集成 appAccessToken。
2989
+ */
2990
+ defaultAppAccessToken: async () => {
2991
+ await this.ensureTokenValid();
2992
+ const url = `/api/integration/v1/namespaces/${this.namespace}/defaultLark/appAccessToken`;
2993
+ this.log(LoggerLevel.info, '[integration.lark.defaultAppAccessToken] Fetching default app token');
2994
+ const res = await this.axiosInstance.get(url, {
2995
+ headers: this.authHeaders()
2996
+ });
2997
+ this.log(LoggerLevel.debug, `[integration.lark.defaultAppAccessToken] Token fetched: code=${res.data.code}`);
2998
+ this.log(LoggerLevel.trace, `[integration.lark.defaultAppAccessToken] Response: ${JSON.stringify(res.data)}`);
2999
+ return res.data;
3000
+ },
3001
+ /**
3002
+ * 获取自定义飞书集成 tenantAccessToken。
3003
+ */
3004
+ tenantAccessToken: async (params) => {
3005
+ const { lark_integration_api_name } = params;
3006
+ await this.ensureTokenValid();
3007
+ const url = `/api/integration/v1/namespaces/${this.namespace}/lark/tenantAccessToken/${lark_integration_api_name}`;
3008
+ this.log(LoggerLevel.info, `[integration.lark.tenantAccessToken] Fetching tenant token: ${lark_integration_api_name}`);
3009
+ const res = await this.axiosInstance.get(url, {
3010
+ headers: this.authHeaders()
3011
+ });
3012
+ this.log(LoggerLevel.debug, `[integration.lark.tenantAccessToken] Token fetched: ${lark_integration_api_name}, code=${res.data.code}`);
3013
+ this.log(LoggerLevel.trace, `[integration.lark.tenantAccessToken] Response: ${JSON.stringify(res.data)}`);
3014
+ return res.data;
3015
+ },
3016
+ /**
3017
+ * 获取自定义飞书集成 appAccessToken。
3018
+ */
3019
+ appAccessToken: async (params) => {
3020
+ const { lark_integration_api_name } = params;
3021
+ await this.ensureTokenValid();
3022
+ const url = `/api/integration/v1/namespaces/${this.namespace}/lark/appAccessToken/${lark_integration_api_name}`;
3023
+ this.log(LoggerLevel.info, `[integration.lark.appAccessToken] Fetching app token: ${lark_integration_api_name}`);
3024
+ const res = await this.axiosInstance.get(url, {
3025
+ headers: this.authHeaders()
3026
+ });
3027
+ this.log(LoggerLevel.debug, `[integration.lark.appAccessToken] Token fetched: ${lark_integration_api_name}, code=${res.data.code}`);
3028
+ this.log(LoggerLevel.trace, `[integration.lark.appAccessToken] Response: ${JSON.stringify(res.data)}`);
3029
+ return res.data;
3030
+ }
3031
+ }
3032
+ };
2298
3033
  /**
2299
3034
  * 云函数模块
2300
3035
  */
@@ -2748,6 +3483,59 @@ class Client {
2748
3483
  }
2749
3484
  }
2750
3485
  };
3486
+ /**
3487
+ * 数据集模块
3488
+ */
3489
+ this.dataset = {
3490
+ /**
3491
+ * 查询数据集列表。
3492
+ */
3493
+ list: async (params) => {
3494
+ await this.ensureTokenValid();
3495
+ const url = `/v1/namespaces/${this.namespace}/datasets`;
3496
+ const requestData = params || {};
3497
+ this.log(LoggerLevel.info, '[dataset.list] Fetching datasets');
3498
+ const res = await this.axiosInstance.post(url, requestData, {
3499
+ headers: this.authHeaders(true)
3500
+ });
3501
+ this.log(LoggerLevel.debug, `[dataset.list] Datasets fetched: code=${res.data.code}`);
3502
+ this.log(LoggerLevel.trace, `[dataset.list] Response: ${JSON.stringify(res.data)}`);
3503
+ return res.data;
3504
+ },
3505
+ /**
3506
+ * 查询全部数据集 - 默认使用 cursor 分页。
3507
+ */
3508
+ listWithIterator: async (params) => {
3509
+ var _a, _b, _c, _d, _e, _f, _g;
3510
+ const pageSize = (params === null || params === void 0 ? void 0 : params.page_size) || 100;
3511
+ let pageToken = '';
3512
+ let hasMore = true;
3513
+ let total = 0;
3514
+ const datasets = [];
3515
+ let lastResponse = null;
3516
+ while (hasMore) {
3517
+ const res = await this.dataset.list({
3518
+ ...params,
3519
+ page_type: 'cursor',
3520
+ page_size: pageSize,
3521
+ page_token: pageToken
3522
+ });
3523
+ if (res.code !== '0') {
3524
+ this.log(LoggerLevel.error, `[dataset.listWithIterator] Error fetching datasets: code=${res.code}, msg=${res.msg}`);
3525
+ throw new Error(res.msg || `Fetch failed with code ${res.code}`);
3526
+ }
3527
+ const pageDatasets = ((_a = res.data) === null || _a === void 0 ? void 0 : _a.datasets) || ((_b = res.data) === null || _b === void 0 ? void 0 : _b.items) || [];
3528
+ if (Array.isArray(pageDatasets)) {
3529
+ datasets.push(...pageDatasets);
3530
+ }
3531
+ total = (_d = (_c = res.data) === null || _c === void 0 ? void 0 : _c.total) !== null && _d !== void 0 ? _d : total;
3532
+ lastResponse = res;
3533
+ 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) || '';
3534
+ hasMore = Boolean(((_g = res.data) === null || _g === void 0 ? void 0 : _g.has_more) && pageToken);
3535
+ }
3536
+ return { total, datasets, lastResponse };
3537
+ }
3538
+ };
2751
3539
  /**
2752
3540
  * 自动化流程模块
2753
3541
  */
@@ -2816,6 +3604,218 @@ class Client {
2816
3604
  }
2817
3605
  }
2818
3606
  };
3607
+ /**
3608
+ * 流程模块
3609
+ */
3610
+ this.workflow = {
3611
+ execution: {
3612
+ /**
3613
+ * 查询异步流程状态。
3614
+ */
3615
+ status: async (params) => {
3616
+ const { execution_id } = params;
3617
+ await this.ensureTokenValid();
3618
+ const url = `/api/v2/workflow/namespaces/${this.namespace}/open_api/execution`;
3619
+ this.log(LoggerLevel.info, `[workflow.execution.status] Fetching execution status: ${execution_id}`);
3620
+ const res = await this.axiosInstance.get(url, {
3621
+ headers: this.authHeaders(true),
3622
+ params: { executionId: execution_id }
3623
+ });
3624
+ this.log(LoggerLevel.debug, `[workflow.execution.status] Execution status fetched: code=${res.data.code}`);
3625
+ this.log(LoggerLevel.trace, `[workflow.execution.status] Response: ${JSON.stringify(res.data)}`);
3626
+ return res.data;
3627
+ }
3628
+ },
3629
+ definition: {
3630
+ /**
3631
+ * 获取流程定义详情。
3632
+ */
3633
+ detail: async (params) => {
3634
+ const { flow_api_name } = params;
3635
+ await this.ensureTokenValid();
3636
+ const url = `/api/flow/v1/namespaces/${this.namespace}/flow/${flow_api_name}`;
3637
+ this.log(LoggerLevel.info, `[workflow.definition.detail] Fetching flow definition: ${flow_api_name}`);
3638
+ const res = await this.axiosInstance.get(url, {
3639
+ headers: this.authHeaders()
3640
+ });
3641
+ this.log(LoggerLevel.debug, `[workflow.definition.detail] Flow definition fetched: ${flow_api_name}, code=${res.data.code}`);
3642
+ this.log(LoggerLevel.trace, `[workflow.definition.detail] Response: ${JSON.stringify(res.data)}`);
3643
+ return res.data;
3644
+ }
3645
+ },
3646
+ userTask: {
3647
+ /**
3648
+ * 获取包含人工任务的流程列表。
3649
+ */
3650
+ flows: async (params) => {
3651
+ await this.ensureTokenValid();
3652
+ const url = `/api/flow/v1/namespaces/${this.namespace}/user_task/flow_list`;
3653
+ this.log(LoggerLevel.info, '[workflow.userTask.flows] Fetching user-task flows');
3654
+ const res = await this.axiosInstance.post(url, params, {
3655
+ headers: this.authHeaders(true)
3656
+ });
3657
+ this.log(LoggerLevel.debug, `[workflow.userTask.flows] User-task flows fetched: code=${res.data.code}`);
3658
+ this.log(LoggerLevel.trace, `[workflow.userTask.flows] Response: ${JSON.stringify(res.data)}`);
3659
+ return res.data;
3660
+ },
3661
+ /**
3662
+ * 获取包含人工任务的流程实例 ID 列表。
3663
+ */
3664
+ instanceIds: async (params) => {
3665
+ const { page_size, page_token, start_time, end_time, api_ids } = params;
3666
+ await this.ensureTokenValid();
3667
+ const url = `/api/flow/v1/namespaces/${this.namespace}/instances/listids`;
3668
+ const data = {};
3669
+ if (start_time !== undefined)
3670
+ data.start_time = String(start_time);
3671
+ if (end_time !== undefined)
3672
+ data.end_time = String(end_time);
3673
+ if (api_ids !== undefined)
3674
+ data.api_ids = api_ids;
3675
+ this.log(LoggerLevel.info, '[workflow.userTask.instanceIds] Fetching workflow instance IDs');
3676
+ const res = await this.axiosInstance.get(url, {
3677
+ headers: this.authHeaders(true),
3678
+ params: { page_size, page_token },
3679
+ data
3680
+ });
3681
+ this.log(LoggerLevel.debug, `[workflow.userTask.instanceIds] Workflow instance IDs fetched: code=${res.data.code}`);
3682
+ this.log(LoggerLevel.trace, `[workflow.userTask.instanceIds] Response: ${JSON.stringify(res.data)}`);
3683
+ return res.data;
3684
+ },
3685
+ /**
3686
+ * 获取流程实例详情。
3687
+ */
3688
+ instanceDetail: async (params) => {
3689
+ const { approval_instance_id, includes } = params;
3690
+ await this.ensureTokenValid();
3691
+ const url = `/api/flow/v1/namespaces/${this.namespace}/instances/${approval_instance_id}`;
3692
+ this.log(LoggerLevel.info, `[workflow.userTask.instanceDetail] Fetching workflow instance: ${approval_instance_id}`);
3693
+ const res = await this.axiosInstance.get(url, {
3694
+ headers: this.authHeaders(),
3695
+ params: includes ? { includes } : undefined
3696
+ });
3697
+ this.log(LoggerLevel.debug, `[workflow.userTask.instanceDetail] Workflow instance fetched: ${approval_instance_id}, code=${res.data.code}`);
3698
+ this.log(LoggerLevel.trace, `[workflow.userTask.instanceDetail] Response: ${JSON.stringify(res.data)}`);
3699
+ return res.data;
3700
+ },
3701
+ /**
3702
+ * 批量获取流程实例的任务列表。
3703
+ */
3704
+ instanceTasks: async (params) => {
3705
+ const { approval_instance_ids, task_status } = params;
3706
+ await this.ensureTokenValid();
3707
+ const url = `/api/flow/v1/namespaces/${this.namespace}/instances/usertasks`;
3708
+ this.log(LoggerLevel.info, '[workflow.userTask.instanceTasks] Fetching workflow instance tasks');
3709
+ const res = await this.axiosInstance.get(url, {
3710
+ headers: this.authHeaders(true),
3711
+ params: task_status ? { task_status } : undefined,
3712
+ data: { approval_instance_ids }
3713
+ });
3714
+ this.log(LoggerLevel.debug, `[workflow.userTask.instanceTasks] Workflow instance tasks fetched: code=${res.data.code}`);
3715
+ this.log(LoggerLevel.trace, `[workflow.userTask.instanceTasks] Response: ${JSON.stringify(res.data)}`);
3716
+ return res.data;
3717
+ },
3718
+ /**
3719
+ * 获取任务列表。
3720
+ */
3721
+ tasks: async (params) => {
3722
+ await this.ensureTokenValid();
3723
+ const url = `/api/flow/v1/namespaces/${this.namespace}/user_tasks`;
3724
+ this.log(LoggerLevel.info, '[workflow.userTask.tasks] Fetching user tasks');
3725
+ const res = await this.axiosInstance.post(url, params, {
3726
+ headers: this.authHeaders(true)
3727
+ });
3728
+ this.log(LoggerLevel.debug, `[workflow.userTask.tasks] User tasks fetched: code=${res.data.code}`);
3729
+ this.log(LoggerLevel.trace, `[workflow.userTask.tasks] Response: ${JSON.stringify(res.data)}`);
3730
+ return res.data;
3731
+ },
3732
+ /**
3733
+ * 获取任务详情。
3734
+ */
3735
+ detail: async (params) => {
3736
+ const { task_id } = params;
3737
+ await this.ensureTokenValid();
3738
+ const url = `/api/flow/v1/namespaces/${this.namespace}/user_task/${task_id}/detail`;
3739
+ this.log(LoggerLevel.info, `[workflow.userTask.detail] Fetching user task: ${task_id}`);
3740
+ const res = await this.axiosInstance.get(url, {
3741
+ headers: this.authHeaders()
3742
+ });
3743
+ this.log(LoggerLevel.debug, `[workflow.userTask.detail] User task fetched: ${task_id}, code=${res.data.code}`);
3744
+ this.log(LoggerLevel.trace, `[workflow.userTask.detail] Response: ${JSON.stringify(res.data)}`);
3745
+ return res.data;
3746
+ },
3747
+ agree: async (params) => {
3748
+ const { approval_task_id, ...data } = params;
3749
+ await this.ensureTokenValid();
3750
+ const url = `/api/flow/v1/namespaces/${this.namespace}/user_task/${approval_task_id}/agree`;
3751
+ const res = await this.axiosInstance.post(url, data, { headers: this.authHeaders(true) });
3752
+ return res.data;
3753
+ },
3754
+ reject: async (params) => {
3755
+ const { approval_task_id, ...data } = params;
3756
+ await this.ensureTokenValid();
3757
+ const url = `/api/flow/v1/namespaces/${this.namespace}/user_task/${approval_task_id}/reject`;
3758
+ const res = await this.axiosInstance.post(url, data, { headers: this.authHeaders(true) });
3759
+ return res.data;
3760
+ },
3761
+ transfer: async (params) => {
3762
+ const { approval_task_id, ...data } = params;
3763
+ await this.ensureTokenValid();
3764
+ const url = `/api/flow/v1/namespaces/${this.namespace}/user_task/${approval_task_id}/trans`;
3765
+ const res = await this.axiosInstance.post(url, data, { headers: this.authHeaders(true) });
3766
+ return res.data;
3767
+ },
3768
+ addAssignee: async (params) => {
3769
+ const { approval_task_id, ...data } = params;
3770
+ await this.ensureTokenValid();
3771
+ const url = `/api/flow/v1/namespaces/${this.namespace}/user_task/${approval_task_id}/add_assignee`;
3772
+ const res = await this.axiosInstance.post(url, data, { headers: this.authHeaders(true) });
3773
+ return res.data;
3774
+ },
3775
+ cc: async (params) => {
3776
+ const { task_id, ...data } = params;
3777
+ await this.ensureTokenValid();
3778
+ const url = `/api/flow/v1/namespaces/${this.namespace}/user_task/${task_id}/cc`;
3779
+ const res = await this.axiosInstance.post(url, data, { headers: this.authHeaders(true) });
3780
+ return res.data;
3781
+ },
3782
+ expedite: async (params) => {
3783
+ const { task_id, ...data } = params;
3784
+ await this.ensureTokenValid();
3785
+ const url = `/api/flow/v1/namespaces/${this.namespace}/user_task/${task_id}/expediting`;
3786
+ const res = await this.axiosInstance.post(url, data, { headers: this.authHeaders(true) });
3787
+ return res.data;
3788
+ },
3789
+ cancelInstance: async (params) => {
3790
+ const { approval_instance_id, ...data } = params;
3791
+ await this.ensureTokenValid();
3792
+ const url = `/api/flow/v1/namespaces/${this.namespace}/instance/${approval_instance_id}/cancel`;
3793
+ const res = await this.axiosInstance.post(url, data, { headers: this.authHeaders(true) });
3794
+ return res.data;
3795
+ },
3796
+ rollbackPoints: async (params) => {
3797
+ const { task_id, ...data } = params;
3798
+ await this.ensureTokenValid();
3799
+ const url = `/api/flow/v1/namespaces/${this.namespace}/user_task/${task_id}/rollback_points`;
3800
+ const res = await this.axiosInstance.post(url, data, { headers: this.authHeaders(true) });
3801
+ return res.data;
3802
+ },
3803
+ rollback: async (params) => {
3804
+ const { task_id, ...data } = params;
3805
+ await this.ensureTokenValid();
3806
+ const url = `/api/flow/v1/namespaces/${this.namespace}/user_task/${task_id}/rollback`;
3807
+ const res = await this.axiosInstance.post(url, data, { headers: this.authHeaders(true) });
3808
+ return res.data;
3809
+ },
3810
+ startChat: async (params) => {
3811
+ const { task_id, ...data } = params;
3812
+ await this.ensureTokenValid();
3813
+ const url = `/api/flow/v1/namespaces/${this.namespace}/user_task/${task_id}/chat`;
3814
+ const res = await this.axiosInstance.post(url, data, { headers: this.authHeaders(true) });
3815
+ return res.data;
3816
+ }
3817
+ }
3818
+ };
2819
3819
  /**
2820
3820
  * 数据对象结构管理模块(Schema)
2821
3821
  */
@@ -2828,12 +3828,13 @@ class Client {
2828
3828
  */
2829
3829
  create: async (params) => {
2830
3830
  const { objects } = params;
3831
+ const requestObjects = normalizeSchemaObjectsForWrite(objects);
2831
3832
  await this.ensureTokenValid();
2832
3833
  const url = `/v1/namespaces/${this.namespace}/objects/batch_create`;
2833
3834
  this.log(LoggerLevel.info, `[schema.create] Creating ${objects.length} object(s)`);
2834
3835
  this.log(LoggerLevel.trace, `[schema.create] Request URL: ${this.axiosInstance.defaults.baseURL}${url}`);
2835
- this.log(LoggerLevel.trace, `[schema.create] Request Body: ${JSON.stringify({ objects }, null, 2)}`);
2836
- const res = await this.axiosInstance.post(url, { objects }, {
3836
+ this.log(LoggerLevel.trace, `[schema.create] Request Body: ${JSON.stringify({ objects: requestObjects }, null, 2)}`);
3837
+ const res = await this.axiosInstance.post(url, { objects: requestObjects }, {
2837
3838
  headers: {
2838
3839
  Authorization: `${this.accessToken}`,
2839
3840
  'Content-Type': 'application/json'
@@ -2890,12 +3891,13 @@ class Client {
2890
3891
  */
2891
3892
  update: async (params) => {
2892
3893
  const { objects } = params;
3894
+ const requestObjects = normalizeSchemaObjectsForWrite(objects);
2893
3895
  await this.ensureTokenValid();
2894
3896
  const url = `/v1/namespaces/${this.namespace}/objects/batch_update`;
2895
3897
  this.log(LoggerLevel.info, `[schema.update] Updating ${objects.length} object(s)`);
2896
3898
  this.log(LoggerLevel.trace, `[schema.update] Request URL: ${this.axiosInstance.defaults.baseURL}${url}`);
2897
- this.log(LoggerLevel.trace, `[schema.update] Request Body: ${JSON.stringify({ objects }, null, 2)}`);
2898
- const res = await this.axiosInstance.post(url, { objects }, {
3899
+ this.log(LoggerLevel.trace, `[schema.update] Request Body: ${JSON.stringify({ objects: requestObjects }, null, 2)}`);
3900
+ const res = await this.axiosInstance.post(url, { objects: requestObjects }, {
2899
3901
  headers: {
2900
3902
  Authorization: `${this.accessToken}`,
2901
3903
  'Content-Type': 'application/json'
@@ -2939,6 +3941,48 @@ class Client {
2939
3941
  this.log(LoggerLevel.debug, `[schema.delete] Objects deleted: code=${res.data.code}`);
2940
3942
  this.log(LoggerLevel.trace, `[schema.delete] Response: ${JSON.stringify(res.data)}`);
2941
3943
  return res.data;
3944
+ },
3945
+ /**
3946
+ * 校验 schema 写接口响应,覆盖请求级错误、data=null 静默失败、item 级失败。
3947
+ */
3948
+ checkResponse: checkSchemaResponse,
3949
+ /**
3950
+ * 返回 schema 写接口响应校验结果,不抛错。
3951
+ */
3952
+ validateResponse: validateSchemaResponse,
3953
+ /**
3954
+ * 按 schema 单批 10 个对象的限制分批执行。
3955
+ */
3956
+ batchExecute,
3957
+ /**
3958
+ * 创建对象空壳。会移除 fields,并将 display/search 临时指向 _id。
3959
+ */
3960
+ createShells: async (params) => {
3961
+ return createSchemaObjectShells(this, params.objects, params);
3962
+ },
3963
+ /**
3964
+ * 幂等添加字段:先读 metadata,跳过已存在字段,再调用 schema.update。
3965
+ */
3966
+ addFieldsIdempotent: async (params) => {
3967
+ return addFieldsIdempotent(this, params.object_name, params.fields, params);
3968
+ },
3969
+ /**
3970
+ * 三阶段创建对象:空壳 -> 基础字段 -> lookup -> reference_field -> final settings。
3971
+ */
3972
+ createWithStages: async (params) => {
3973
+ return createSchemaObjectsInStages(this, params.objects, params);
3974
+ },
3975
+ /**
3976
+ * 写后验证对象和字段,可选导出 Markdown。
3977
+ */
3978
+ verifyObjects: async (params) => {
3979
+ return verifySchemaObjects(this, params.object_names, params);
3980
+ },
3981
+ /**
3982
+ * 删除全部自定义对象。高风险操作,必须显式传 confirm: true。
3983
+ */
3984
+ deleteAllCustomObjects: async (params) => {
3985
+ return deleteAllCustomObjects(this, params);
2942
3986
  }
2943
3987
  };
2944
3988
  this.clientId = options.clientId;
@@ -3048,31 +4092,56 @@ class Client {
3048
4092
  this.log(LoggerLevel.debug, `[namespace] Current namespace: ${this.namespace}`);
3049
4093
  return this.namespace;
3050
4094
  }
4095
+ authHeaders(includeContentType = false) {
4096
+ return includeContentType
4097
+ ? { Authorization: `${this.accessToken}`, 'Content-Type': 'application/json' }
4098
+ : { Authorization: `${this.accessToken}` };
4099
+ }
3051
4100
  }
3052
4101
  const apaas = {
3053
4102
  Client
3054
4103
  };
3055
4104
 
3056
4105
  exports.BATCH_UPDATE_REQUIREMENTS = BATCH_UPDATE_REQUIREMENTS;
4106
+ exports.COLUMN_NAME_SEMANTIC_RULES = COLUMN_NAME_SEMANTIC_RULES;
3057
4107
  exports.CREATE_OBJECT_EXAMPLE = CREATE_OBJECT_EXAMPLE;
3058
4108
  exports.FIELD_SCHEMA_RULES = FIELD_SCHEMA_RULES;
3059
4109
  exports.FIELD_TYPES = FIELD_TYPES;
3060
4110
  exports.LANGUAGE_CODES = LANGUAGE_CODES;
4111
+ exports.OPTION_COLOR_CODE_BY_NAME = OPTION_COLOR_CODE_BY_NAME;
3061
4112
  exports.OPTION_COLOR_LIST = OPTION_COLOR_LIST;
4113
+ exports.OPTION_COLOR_NAME_BY_CODE = OPTION_COLOR_NAME_BY_CODE;
3062
4114
  exports.OPTION_COLOR_RULES = OPTION_COLOR_RULES;
4115
+ exports.SCHEMA_BATCH_SIZE = SCHEMA_BATCH_SIZE;
3063
4116
  exports.SCHEMA_GUIDELINES = SCHEMA_GUIDELINES;
3064
4117
  exports.SCHEMA_TYPE_BY_METADATA_TYPE = SCHEMA_TYPE_BY_METADATA_TYPE;
3065
4118
  exports.SCHEMA_TYPE_MISMATCHES = SCHEMA_TYPE_MISMATCHES;
4119
+ exports.SQL_CONSTRAINT_TO_SETTINGS = SQL_CONSTRAINT_TO_SETTINGS;
4120
+ exports.SQL_TYPE_TO_SCHEMA_TYPE = SQL_TYPE_TO_SCHEMA_TYPE;
3066
4121
  exports.SYSTEM_FIELDS = SYSTEM_FIELDS;
3067
4122
  exports.UPDATE_OBJECT_ADD_FIELD_EXAMPLE = UPDATE_OBJECT_ADD_FIELD_EXAMPLE;
3068
4123
  exports.UPDATE_OBJECT_REMOVE_FIELD_EXAMPLE = UPDATE_OBJECT_REMOVE_FIELD_EXAMPLE;
3069
4124
  exports.UPDATE_OBJECT_REPLACE_FIELD_EXAMPLE = UPDATE_OBJECT_REPLACE_FIELD_EXAMPLE;
3070
4125
  exports.UPDATE_OBJECT_SETTINGS_EXAMPLE = UPDATE_OBJECT_SETTINGS_EXAMPLE;
4126
+ exports.addFieldsIdempotent = addFieldsIdempotent;
3071
4127
  exports.apaas = apaas;
4128
+ exports.batchExecute = batchExecute;
4129
+ exports.buildFieldRemovalPlan = buildFieldRemovalPlan;
4130
+ exports.checkSchemaResponse = checkSchemaResponse;
3072
4131
  exports.createMultilingualText = createMultilingualText;
4132
+ exports.createSchemaObjectShells = createSchemaObjectShells;
4133
+ exports.createSchemaObjectsInStages = createSchemaObjectsInStages;
4134
+ exports.deleteAllCustomObjects = deleteAllCustomObjects;
3073
4135
  exports.extractMultilingualText = extractMultilingualText;
3074
4136
  exports.getCustomFieldTypes = getCustomFieldTypes;
3075
4137
  exports.getFieldType = getFieldType;
3076
4138
  exports.getOptionColor = getOptionColor;
4139
+ exports.getOptionColorCode = getOptionColorCode;
3077
4140
  exports.getSystemFields = getSystemFields;
3078
4141
  exports.isSystemField = isSystemField;
4142
+ exports.isSystemSchemaName = isSystemSchemaName;
4143
+ exports.normalizeOptionColorForSchema = normalizeOptionColorForSchema;
4144
+ exports.normalizeSchemaObjectsForWrite = normalizeSchemaObjectsForWrite;
4145
+ exports.splitSchemaFieldsByDependency = splitSchemaFieldsByDependency;
4146
+ exports.validateSchemaResponse = validateSchemaResponse;
4147
+ exports.verifySchemaObjects = verifySchemaObjects;