fireberry-api-client 1.0.2-beta.1.0 → 1.0.2-beta.1.2

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.cjs CHANGED
@@ -174,20 +174,6 @@ function createNetworkError(error) {
174
174
  });
175
175
  }
176
176
 
177
- // src/utils/helpers.ts
178
- function wait(ms) {
179
- return new Promise((resolve) => {
180
- setTimeout(resolve, ms);
181
- });
182
- }
183
- function chunkArray(array, size) {
184
- const result = [];
185
- for (let i = 0; i < array.length; i += size) {
186
- result.push(array.slice(i, i + size));
187
- }
188
- return result;
189
- }
190
-
191
177
  // src/constants/objectIds.ts
192
178
  var OBJECT_ID_MAP = {
193
179
  1: "accountid",
@@ -1073,6 +1059,584 @@ var QueryBuilder = class {
1073
1059
  }
1074
1060
  };
1075
1061
 
1062
+ // src/utils/helpers.ts
1063
+ function wait(ms) {
1064
+ return new Promise((resolve) => {
1065
+ setTimeout(resolve, ms);
1066
+ });
1067
+ }
1068
+ function chunkArray(array, size) {
1069
+ const result = [];
1070
+ for (let i = 0; i < array.length; i += size) {
1071
+ result.push(array.slice(i, i + size));
1072
+ }
1073
+ return result;
1074
+ }
1075
+
1076
+ // src/transport/http.ts
1077
+ var HTTPTransport = class {
1078
+ config;
1079
+ constructor(config) {
1080
+ this.config = {
1081
+ apiKey: config.apiKey,
1082
+ baseUrl: config.baseUrl || "https://api.fireberry.com",
1083
+ timeout: config.timeout || 3e4,
1084
+ retryOn429: config.retryOn429 ?? true,
1085
+ maxRetries: config.maxRetries || 120,
1086
+ retryDelay: config.retryDelay || 1e3
1087
+ };
1088
+ }
1089
+ getType() {
1090
+ return "http";
1091
+ }
1092
+ async request(options) {
1093
+ const { method, endpoint, query: queryParams, body, headers: customHeaders, signal } = options;
1094
+ let url = `${this.config.baseUrl}${endpoint}`;
1095
+ if (queryParams && Object.keys(queryParams).length > 0) {
1096
+ const params = new URLSearchParams();
1097
+ for (const [key, value] of Object.entries(queryParams)) {
1098
+ if (value !== void 0 && value !== null) {
1099
+ params.set(key, String(value));
1100
+ }
1101
+ }
1102
+ url += `?${params.toString()}`;
1103
+ }
1104
+ const headers = {
1105
+ Accept: "application/json",
1106
+ tokenid: this.config.apiKey,
1107
+ ...customHeaders
1108
+ };
1109
+ if (body) {
1110
+ headers["Content-Type"] = "application/json";
1111
+ }
1112
+ const fetchOptions = {
1113
+ method,
1114
+ headers,
1115
+ signal
1116
+ };
1117
+ if (body) {
1118
+ fetchOptions.body = JSON.stringify(body);
1119
+ }
1120
+ return this.executeWithRetry(url, fetchOptions);
1121
+ }
1122
+ async query(options) {
1123
+ const {
1124
+ objectType,
1125
+ fields,
1126
+ query,
1127
+ sortBy = "modifiedon",
1128
+ sortType = "desc",
1129
+ limit,
1130
+ page = 1,
1131
+ pageSize = 500,
1132
+ showRealValue = true,
1133
+ autoPage = true,
1134
+ signal
1135
+ } = options;
1136
+ let fieldsStr;
1137
+ if (Array.isArray(fields)) {
1138
+ fieldsStr = fields.join(",");
1139
+ } else if (typeof fields === "string") {
1140
+ fieldsStr = fields;
1141
+ } else {
1142
+ fieldsStr = "*";
1143
+ }
1144
+ if (autoPage) {
1145
+ return this.queryAllPages({
1146
+ objectType,
1147
+ fields: fieldsStr,
1148
+ query,
1149
+ sortBy,
1150
+ sortType,
1151
+ showRealValue,
1152
+ limit,
1153
+ signal
1154
+ });
1155
+ }
1156
+ const body = {
1157
+ objecttype: objectType,
1158
+ fields: fieldsStr,
1159
+ query: query || "",
1160
+ sort_by: sortBy,
1161
+ sort_type: sortType,
1162
+ page_size: Math.min(pageSize, limit || 500),
1163
+ page_number: page,
1164
+ show_real_value: showRealValue ? 1 : 0
1165
+ };
1166
+ const response = await this.request({
1167
+ method: "POST",
1168
+ endpoint: "/api/query",
1169
+ body,
1170
+ signal
1171
+ });
1172
+ const records = response.data?.Data || [];
1173
+ return {
1174
+ records,
1175
+ total: records.length,
1176
+ success: true
1177
+ };
1178
+ }
1179
+ async createRecord(objectType, data, signal) {
1180
+ const response = await this.request({
1181
+ method: "POST",
1182
+ endpoint: `/api/v2/record/${objectType}`,
1183
+ body: data,
1184
+ signal
1185
+ });
1186
+ return response.record;
1187
+ }
1188
+ async updateRecord(objectType, recordId, data, signal) {
1189
+ const response = await this.request({
1190
+ method: "PUT",
1191
+ endpoint: `/api/v2/record/${objectType}/${recordId}`,
1192
+ body: data,
1193
+ signal
1194
+ });
1195
+ return response.record;
1196
+ }
1197
+ async deleteRecord(objectType, recordId, signal) {
1198
+ await this.request({
1199
+ method: "DELETE",
1200
+ endpoint: `/api/record/${objectType}/${recordId}`,
1201
+ signal
1202
+ });
1203
+ return {
1204
+ success: true,
1205
+ id: recordId
1206
+ };
1207
+ }
1208
+ async batchCreate(objectType, records, signal) {
1209
+ const response = await this.request({
1210
+ method: "POST",
1211
+ endpoint: `/api/v3/record/${objectType}/batch/create`,
1212
+ body: { data: records },
1213
+ signal
1214
+ });
1215
+ const data = response.data || [];
1216
+ const dataArray = Array.isArray(data) ? data : [data];
1217
+ return {
1218
+ success: true,
1219
+ data: dataArray,
1220
+ count: dataArray.length
1221
+ };
1222
+ }
1223
+ async batchUpdate(objectType, records, signal) {
1224
+ const response = await this.request({
1225
+ method: "POST",
1226
+ endpoint: `/api/v3/record/${objectType}/batch/update`,
1227
+ body: { data: records },
1228
+ signal
1229
+ });
1230
+ const data = response.data || [];
1231
+ const dataArray = Array.isArray(data) ? data : [data];
1232
+ return {
1233
+ success: true,
1234
+ data: dataArray,
1235
+ count: dataArray.length
1236
+ };
1237
+ }
1238
+ async batchDelete(objectType, recordIds, signal) {
1239
+ await this.request({
1240
+ method: "POST",
1241
+ endpoint: `/api/v3/record/${objectType}/batch/delete`,
1242
+ body: { data: recordIds },
1243
+ signal
1244
+ });
1245
+ return {
1246
+ success: true,
1247
+ ids: recordIds,
1248
+ count: recordIds.length
1249
+ };
1250
+ }
1251
+ /**
1252
+ * Fetches all pages of a query
1253
+ */
1254
+ async queryAllPages(options) {
1255
+ const { objectType, fields, query, sortBy, sortType, showRealValue, limit, signal } = options;
1256
+ const maxPageSize = 500;
1257
+ const allRecords = [];
1258
+ let currentPage = 1;
1259
+ let hasMore = true;
1260
+ while (hasMore) {
1261
+ if (signal?.aborted) {
1262
+ break;
1263
+ }
1264
+ const body = {
1265
+ objecttype: objectType,
1266
+ fields,
1267
+ query: query || "",
1268
+ sort_by: sortBy,
1269
+ sort_type: sortType,
1270
+ page_size: maxPageSize,
1271
+ page_number: currentPage,
1272
+ show_real_value: showRealValue ? 1 : 0
1273
+ };
1274
+ const response = await this.request({
1275
+ method: "POST",
1276
+ endpoint: "/api/query",
1277
+ body,
1278
+ signal
1279
+ });
1280
+ const pageData = response.data?.Data || [];
1281
+ allRecords.push(...pageData);
1282
+ if (limit && allRecords.length >= limit) {
1283
+ allRecords.splice(limit);
1284
+ break;
1285
+ }
1286
+ if (pageData.length < maxPageSize) {
1287
+ hasMore = false;
1288
+ } else {
1289
+ currentPage++;
1290
+ }
1291
+ }
1292
+ return {
1293
+ records: allRecords,
1294
+ total: allRecords.length,
1295
+ success: true
1296
+ };
1297
+ }
1298
+ /**
1299
+ * Executes a fetch request with retry logic for 429 errors
1300
+ */
1301
+ async executeWithRetry(url, options, retryCount = 0) {
1302
+ try {
1303
+ const timeoutController = new AbortController();
1304
+ const timeoutId = setTimeout(() => {
1305
+ timeoutController.abort();
1306
+ }, this.config.timeout);
1307
+ const combinedSignal = options.signal ? this.combineSignals([options.signal, timeoutController.signal]) : timeoutController.signal;
1308
+ const response = await fetch(url, {
1309
+ ...options,
1310
+ signal: combinedSignal
1311
+ });
1312
+ clearTimeout(timeoutId);
1313
+ if (response.status === 429 && this.config.retryOn429) {
1314
+ if (retryCount < this.config.maxRetries) {
1315
+ await wait(this.config.retryDelay);
1316
+ return this.executeWithRetry(url, options, retryCount + 1);
1317
+ }
1318
+ throw new FireberryError("Rate limit exceeded after max retries", {
1319
+ code: "RATE_LIMITED" /* RATE_LIMITED */,
1320
+ statusCode: 429,
1321
+ context: { retryCount }
1322
+ });
1323
+ }
1324
+ let body;
1325
+ const contentType = response.headers.get("content-type");
1326
+ if (contentType?.includes("application/json")) {
1327
+ body = await response.json();
1328
+ } else {
1329
+ body = await response.text();
1330
+ }
1331
+ if (!response.ok) {
1332
+ throw createErrorFromResponse(response, body);
1333
+ }
1334
+ return body;
1335
+ } catch (error) {
1336
+ if (error instanceof Error && error.name === "AbortError") {
1337
+ throw createNetworkError(error);
1338
+ }
1339
+ if (error instanceof FireberryError) {
1340
+ throw error;
1341
+ }
1342
+ throw createNetworkError(error);
1343
+ }
1344
+ }
1345
+ /**
1346
+ * Combines multiple abort signals into one
1347
+ */
1348
+ combineSignals(signals) {
1349
+ const controller = new AbortController();
1350
+ for (const signal of signals) {
1351
+ if (signal.aborted) {
1352
+ controller.abort();
1353
+ break;
1354
+ }
1355
+ signal.addEventListener("abort", () => controller.abort(), { once: true });
1356
+ }
1357
+ return controller.signal;
1358
+ }
1359
+ };
1360
+
1361
+ // src/transport/sdk.ts
1362
+ var BATCH_SIZE = 20;
1363
+ var SDKTransport = class {
1364
+ sdk;
1365
+ constructor(config) {
1366
+ this.sdk = config.sdk;
1367
+ }
1368
+ getType() {
1369
+ return "sdk";
1370
+ }
1371
+ async request(_options) {
1372
+ throw new FireberryError(
1373
+ "Raw request() is not supported in SDK mode. Use specific methods like query(), createRecord(), etc.",
1374
+ {
1375
+ code: "INVALID_REQUEST" /* INVALID_REQUEST */
1376
+ }
1377
+ );
1378
+ }
1379
+ async query(options) {
1380
+ const {
1381
+ objectType,
1382
+ fields,
1383
+ query,
1384
+ limit,
1385
+ page = 1,
1386
+ pageSize = 500,
1387
+ autoPage = true,
1388
+ signal
1389
+ } = options;
1390
+ let fieldsStr;
1391
+ if (Array.isArray(fields)) {
1392
+ fieldsStr = fields.join(",");
1393
+ } else if (typeof fields === "string") {
1394
+ fieldsStr = fields;
1395
+ } else {
1396
+ fieldsStr = "*";
1397
+ }
1398
+ if (autoPage) {
1399
+ return this.queryAllPages({
1400
+ objectType,
1401
+ fields: fieldsStr,
1402
+ query,
1403
+ limit,
1404
+ pageSize,
1405
+ signal
1406
+ });
1407
+ }
1408
+ const payload = {
1409
+ fields: fieldsStr,
1410
+ query: query || "",
1411
+ page_size: Math.min(pageSize, limit || 500),
1412
+ page_number: page
1413
+ };
1414
+ const response = await this.sdk.api.query(objectType, payload);
1415
+ if (!response.success && response.error) {
1416
+ throw new FireberryError(
1417
+ response.error.data?.Message || response.error.statusText || "SDK query failed",
1418
+ {
1419
+ code: "SERVER_ERROR" /* SERVER_ERROR */,
1420
+ statusCode: response.error.status,
1421
+ context: { response }
1422
+ }
1423
+ );
1424
+ }
1425
+ const pageData = Array.isArray(response.data) ? response.data : [response.data];
1426
+ const records = pageData.filter(
1427
+ (record) => record && typeof record === "object"
1428
+ );
1429
+ return {
1430
+ records,
1431
+ total: records.length,
1432
+ success: true
1433
+ };
1434
+ }
1435
+ async createRecord(objectType, data, signal) {
1436
+ if (signal?.aborted) {
1437
+ throw new FireberryError("Request aborted", {
1438
+ code: "NETWORK_ERROR" /* NETWORK_ERROR */
1439
+ });
1440
+ }
1441
+ const response = await this.sdk.api.create(objectType, data);
1442
+ if (!response.success && response.error) {
1443
+ throw new FireberryError(
1444
+ response.error.data?.Message || response.error.statusText || "SDK create failed",
1445
+ {
1446
+ code: "SERVER_ERROR" /* SERVER_ERROR */,
1447
+ statusCode: response.error.status,
1448
+ context: { response }
1449
+ }
1450
+ );
1451
+ }
1452
+ return response.data;
1453
+ }
1454
+ async updateRecord(objectType, recordId, data, signal) {
1455
+ if (signal?.aborted) {
1456
+ throw new FireberryError("Request aborted", {
1457
+ code: "NETWORK_ERROR" /* NETWORK_ERROR */
1458
+ });
1459
+ }
1460
+ const response = await this.sdk.api.update(objectType, recordId, data);
1461
+ if (!response.success && response.error) {
1462
+ throw new FireberryError(
1463
+ response.error.data?.Message || response.error.statusText || "SDK update failed",
1464
+ {
1465
+ code: "SERVER_ERROR" /* SERVER_ERROR */,
1466
+ statusCode: response.error.status,
1467
+ context: { response }
1468
+ }
1469
+ );
1470
+ }
1471
+ return response.data;
1472
+ }
1473
+ async deleteRecord(objectType, recordId, signal) {
1474
+ if (signal?.aborted) {
1475
+ throw new FireberryError("Request aborted", {
1476
+ code: "NETWORK_ERROR" /* NETWORK_ERROR */
1477
+ });
1478
+ }
1479
+ const response = await this.sdk.api.delete(objectType, recordId);
1480
+ if (!response.success && response.error) {
1481
+ throw new FireberryError(
1482
+ response.error.data?.Message || response.error.statusText || "SDK delete failed",
1483
+ {
1484
+ code: "SERVER_ERROR" /* SERVER_ERROR */,
1485
+ statusCode: response.error.status,
1486
+ context: { response }
1487
+ }
1488
+ );
1489
+ }
1490
+ return {
1491
+ success: true,
1492
+ id: recordId
1493
+ };
1494
+ }
1495
+ async batchCreate(objectType, records, signal) {
1496
+ const batches = chunkArray(records, BATCH_SIZE);
1497
+ const allResponses = [];
1498
+ for (const batch of batches) {
1499
+ if (signal?.aborted) {
1500
+ break;
1501
+ }
1502
+ const batchPromises = batch.map((record) => this.createRecord(objectType, record, signal));
1503
+ const batchResults = await Promise.all(batchPromises);
1504
+ allResponses.push(...batchResults);
1505
+ }
1506
+ return {
1507
+ success: true,
1508
+ data: allResponses,
1509
+ count: allResponses.length
1510
+ };
1511
+ }
1512
+ async batchUpdate(objectType, records, signal) {
1513
+ const batches = chunkArray(records, BATCH_SIZE);
1514
+ const allResponses = [];
1515
+ for (const batch of batches) {
1516
+ if (signal?.aborted) {
1517
+ break;
1518
+ }
1519
+ const batchPromises = batch.map(
1520
+ (item) => this.updateRecord(objectType, item.id, item.record, signal)
1521
+ );
1522
+ const batchResults = await Promise.all(batchPromises);
1523
+ allResponses.push(...batchResults);
1524
+ }
1525
+ return {
1526
+ success: true,
1527
+ data: allResponses,
1528
+ count: allResponses.length
1529
+ };
1530
+ }
1531
+ async batchDelete(objectType, recordIds, signal) {
1532
+ const batches = chunkArray(recordIds, BATCH_SIZE);
1533
+ const allDeletedIds = [];
1534
+ for (const batch of batches) {
1535
+ if (signal?.aborted) {
1536
+ break;
1537
+ }
1538
+ const batchPromises = batch.map((id) => this.deleteRecord(objectType, id, signal));
1539
+ await Promise.all(batchPromises);
1540
+ allDeletedIds.push(...batch);
1541
+ }
1542
+ return {
1543
+ success: true,
1544
+ ids: allDeletedIds,
1545
+ count: allDeletedIds.length
1546
+ };
1547
+ }
1548
+ /**
1549
+ * Fetches all pages of a query using SDK
1550
+ */
1551
+ async queryAllPages(options) {
1552
+ const { objectType, fields, query, limit, pageSize = 500, signal } = options;
1553
+ const maxPageSize = 500;
1554
+ const allRecords = [];
1555
+ let currentPage = 1;
1556
+ let hasMore = true;
1557
+ while (hasMore) {
1558
+ if (signal?.aborted) {
1559
+ break;
1560
+ }
1561
+ const payload = {
1562
+ fields,
1563
+ query: query || "",
1564
+ page_size: Math.min(maxPageSize, pageSize),
1565
+ page_number: currentPage
1566
+ };
1567
+ const response = await this.sdk.api.query(objectType, payload);
1568
+ if (!response.success && response.error) {
1569
+ throw new FireberryError(
1570
+ response.error.data?.Message || response.error.statusText || "SDK query failed",
1571
+ {
1572
+ code: "SERVER_ERROR" /* SERVER_ERROR */,
1573
+ statusCode: response.error.status,
1574
+ context: { response }
1575
+ }
1576
+ );
1577
+ }
1578
+ const pageData = Array.isArray(response.data) ? response.data : [response.data];
1579
+ const validRecords = pageData.filter(
1580
+ (record) => record && typeof record === "object"
1581
+ );
1582
+ allRecords.push(...validRecords);
1583
+ if (limit && allRecords.length >= limit) {
1584
+ allRecords.splice(limit);
1585
+ break;
1586
+ }
1587
+ if (validRecords.length < maxPageSize) {
1588
+ hasMore = false;
1589
+ } else {
1590
+ currentPage++;
1591
+ }
1592
+ }
1593
+ return {
1594
+ records: allRecords,
1595
+ total: allRecords.length,
1596
+ success: true
1597
+ };
1598
+ }
1599
+ };
1600
+
1601
+ // src/utils/transport.ts
1602
+ function createTransport(config) {
1603
+ if (config.sdk) {
1604
+ return new SDKTransport({ sdk: config.sdk });
1605
+ }
1606
+ if (config.apiKey) {
1607
+ return new HTTPTransport({
1608
+ apiKey: config.apiKey,
1609
+ baseUrl: config.baseUrl,
1610
+ timeout: config.timeout,
1611
+ retryOn429: config.retryOn429,
1612
+ maxRetries: config.maxRetries,
1613
+ retryDelay: config.retryDelay
1614
+ });
1615
+ }
1616
+ throw new FireberryError(
1617
+ "Either apiKey or sdk must be provided in FireberryClientConfig",
1618
+ {
1619
+ code: "INVALID_REQUEST" /* INVALID_REQUEST */
1620
+ }
1621
+ );
1622
+ }
1623
+ function createMetadataTransport(config) {
1624
+ if (!config.apiKey) {
1625
+ return null;
1626
+ }
1627
+ return new HTTPTransport({
1628
+ apiKey: config.apiKey,
1629
+ baseUrl: config.baseUrl,
1630
+ timeout: config.timeout,
1631
+ retryOn429: config.retryOn429,
1632
+ maxRetries: config.maxRetries,
1633
+ retryDelay: config.retryDelay
1634
+ });
1635
+ }
1636
+ function isMetadataAvailable(config) {
1637
+ return Boolean(config.apiKey);
1638
+ }
1639
+
1076
1640
  // src/constants/fieldTypes.ts
1077
1641
  var FIELD_TYPE_IDS = {
1078
1642
  DROPDOWN: "b4919f2e-2996-48e4-a03c-ba39fb64386c",
@@ -1113,6 +1677,23 @@ var MetadataAPI = class {
1113
1677
  constructor(client) {
1114
1678
  this.client = client;
1115
1679
  }
1680
+ /**
1681
+ * Checks if metadata operations are available and throws error if not
1682
+ * @private
1683
+ */
1684
+ ensureMetadataAvailable() {
1685
+ if (!this.client.isMetadataAvailable()) {
1686
+ throw new FireberryError(
1687
+ "Metadata operations are not available in SDK-only mode. Please provide an API key in the FireberryClient configuration to use metadata features.",
1688
+ {
1689
+ code: "INVALID_REQUEST" /* INVALID_REQUEST */,
1690
+ context: {
1691
+ hint: "Fireberry SDK does not yet support metadata operations. Use API key mode or hybrid mode (both SDK + API key) to access metadata."
1692
+ }
1693
+ }
1694
+ );
1695
+ }
1696
+ }
1116
1697
  /**
1117
1698
  * Gets all available objects/entity types from Fireberry
1118
1699
  *
@@ -1126,6 +1707,7 @@ var MetadataAPI = class {
1126
1707
  * ```
1127
1708
  */
1128
1709
  async getObjects(signal) {
1710
+ this.ensureMetadataAvailable();
1129
1711
  const cached = this.client.getCached("objects");
1130
1712
  if (cached) {
1131
1713
  return cached;
@@ -1163,6 +1745,7 @@ var MetadataAPI = class {
1163
1745
  * ```
1164
1746
  */
1165
1747
  async getFields(objectType, options) {
1748
+ this.ensureMetadataAvailable();
1166
1749
  const objectTypeStr = String(objectType);
1167
1750
  const opts = options instanceof AbortSignal ? { signal: options, includeLookupRelations: true } : { signal: options?.signal, includeLookupRelations: options?.includeLookupRelations ?? true };
1168
1751
  const cached = this.client.getCached("fields", objectTypeStr);
@@ -1219,24 +1802,27 @@ var MetadataAPI = class {
1219
1802
  if (queryableFields.length === 0) {
1220
1803
  return relations;
1221
1804
  }
1222
- const response = await this.client.request({
1223
- method: "POST",
1224
- endpoint: ENDPOINTS.QUERY,
1225
- body: {
1226
- objecttype: objectType,
1227
- fields: queryableFields.join(","),
1228
- query: "",
1229
- page_size: 1,
1230
- page_number: 1,
1231
- show_real_value: 0
1232
- },
1233
- signal
1234
- });
1235
- const columns = response.data?.Columns || [];
1236
- for (const column of columns) {
1237
- if (column.fieldobjecttype !== null && column.fieldobjecttype !== void 0) {
1238
- relations.set(column.fieldname, column.fieldobjecttype);
1805
+ try {
1806
+ const response = await this.client.request({
1807
+ method: "POST",
1808
+ endpoint: ENDPOINTS.QUERY,
1809
+ body: {
1810
+ objecttype: objectType,
1811
+ fields: queryableFields.join(","),
1812
+ query: "",
1813
+ page_size: 1,
1814
+ page_number: 1,
1815
+ show_real_value: 0
1816
+ },
1817
+ signal
1818
+ });
1819
+ const columns = response.data?.Columns || [];
1820
+ for (const column of columns) {
1821
+ if (column.fieldobjecttype !== null && column.fieldobjecttype !== void 0) {
1822
+ relations.set(column.fieldname, column.fieldobjecttype);
1823
+ }
1239
1824
  }
1825
+ } catch (error) {
1240
1826
  }
1241
1827
  return relations;
1242
1828
  }
@@ -1255,6 +1841,7 @@ var MetadataAPI = class {
1255
1841
  * ```
1256
1842
  */
1257
1843
  async getFieldValues(objectType, fieldName, signal) {
1844
+ this.ensureMetadataAvailable();
1258
1845
  const objectTypeStr = String(objectType);
1259
1846
  const cached = this.client.getCached(
1260
1847
  "fieldValues",
@@ -1304,14 +1891,10 @@ var RecordsAPI = class {
1304
1891
  */
1305
1892
  async create(objectType, data, options) {
1306
1893
  const objectTypeStr = String(objectType);
1307
- const response = await this.client.request({
1308
- method: "POST",
1309
- endpoint: `/api/v2/record/${objectTypeStr}`,
1310
- body: data,
1311
- signal: options?.signal
1312
- });
1894
+ const transport = this.client.getTransport();
1895
+ const record = await transport.createRecord(objectTypeStr, data, options?.signal);
1313
1896
  this.client.invalidateCacheForMutation(objectTypeStr);
1314
- return response.record;
1897
+ return record;
1315
1898
  }
1316
1899
  /**
1317
1900
  * Updates an existing record in Fireberry
@@ -1331,14 +1914,10 @@ var RecordsAPI = class {
1331
1914
  */
1332
1915
  async update(objectType, recordId, data, options) {
1333
1916
  const objectTypeStr = String(objectType);
1334
- const response = await this.client.request({
1335
- method: "PUT",
1336
- endpoint: `/api/v2/record/${objectTypeStr}/${recordId}`,
1337
- body: data,
1338
- signal: options?.signal
1339
- });
1917
+ const transport = this.client.getTransport();
1918
+ const record = await transport.updateRecord(objectTypeStr, recordId, data, options?.signal);
1340
1919
  this.client.invalidateCacheForMutation(objectTypeStr);
1341
- return response.record;
1920
+ return record;
1342
1921
  }
1343
1922
  /**
1344
1923
  * Deletes a record from Fireberry
@@ -1355,16 +1934,10 @@ var RecordsAPI = class {
1355
1934
  */
1356
1935
  async delete(objectType, recordId, options) {
1357
1936
  const objectTypeStr = String(objectType);
1358
- await this.client.request({
1359
- method: "DELETE",
1360
- endpoint: `/api/record/${objectTypeStr}/${recordId}`,
1361
- signal: options?.signal
1362
- });
1937
+ const transport = this.client.getTransport();
1938
+ const result = await transport.deleteRecord(objectTypeStr, recordId, options?.signal);
1363
1939
  this.client.invalidateCacheForMutation(objectTypeStr);
1364
- return {
1365
- success: true,
1366
- id: recordId
1367
- };
1940
+ return result;
1368
1941
  }
1369
1942
  /**
1370
1943
  * Upserts a record (creates if not exists, updates if exists)
@@ -1432,7 +2005,7 @@ var RecordsAPI = class {
1432
2005
  };
1433
2006
 
1434
2007
  // src/api/batch.ts
1435
- var BATCH_SIZE = 20;
2008
+ var BATCH_SIZE2 = 20;
1436
2009
  var BatchAPI = class {
1437
2010
  constructor(client) {
1438
2011
  this.client = client;
@@ -1457,25 +2030,15 @@ var BatchAPI = class {
1457
2030
  */
1458
2031
  async create(objectType, records, options) {
1459
2032
  const objectTypeStr = String(objectType);
1460
- const batches = chunkArray(records, BATCH_SIZE);
2033
+ const transport = this.client.getTransport();
2034
+ const batches = chunkArray(records, BATCH_SIZE2);
1461
2035
  const allResponses = [];
1462
2036
  for (const batch of batches) {
1463
2037
  if (options?.signal?.aborted) {
1464
2038
  break;
1465
2039
  }
1466
- const response = await this.client.request({
1467
- method: "POST",
1468
- endpoint: `/api/v3/record/${objectTypeStr}/batch/create`,
1469
- body: { data: batch },
1470
- signal: options?.signal
1471
- });
1472
- if (response.data) {
1473
- if (Array.isArray(response.data)) {
1474
- allResponses.push(...response.data);
1475
- } else {
1476
- allResponses.push(response.data);
1477
- }
1478
- }
2040
+ const result = await transport.batchCreate(objectTypeStr, batch, options?.signal);
2041
+ allResponses.push(...result.data);
1479
2042
  }
1480
2043
  this.client.invalidateCacheForMutation(objectTypeStr);
1481
2044
  return {
@@ -1503,25 +2066,15 @@ var BatchAPI = class {
1503
2066
  */
1504
2067
  async update(objectType, records, options) {
1505
2068
  const objectTypeStr = String(objectType);
1506
- const batches = chunkArray(records, BATCH_SIZE);
2069
+ const transport = this.client.getTransport();
2070
+ const batches = chunkArray(records, BATCH_SIZE2);
1507
2071
  const allResponses = [];
1508
2072
  for (const batch of batches) {
1509
2073
  if (options?.signal?.aborted) {
1510
2074
  break;
1511
2075
  }
1512
- const response = await this.client.request({
1513
- method: "POST",
1514
- endpoint: `/api/v3/record/${objectTypeStr}/batch/update`,
1515
- body: { data: batch },
1516
- signal: options?.signal
1517
- });
1518
- if (response.data) {
1519
- if (Array.isArray(response.data)) {
1520
- allResponses.push(...response.data);
1521
- } else {
1522
- allResponses.push(response.data);
1523
- }
1524
- }
2076
+ const result = await transport.batchUpdate(objectTypeStr, batch, options?.signal);
2077
+ allResponses.push(...result.data);
1525
2078
  }
1526
2079
  this.client.invalidateCacheForMutation(objectTypeStr);
1527
2080
  return {
@@ -1547,19 +2100,15 @@ var BatchAPI = class {
1547
2100
  */
1548
2101
  async delete(objectType, recordIds, options) {
1549
2102
  const objectTypeStr = String(objectType);
1550
- const batches = chunkArray(recordIds, BATCH_SIZE);
2103
+ const transport = this.client.getTransport();
2104
+ const batches = chunkArray(recordIds, BATCH_SIZE2);
1551
2105
  const allDeletedIds = [];
1552
2106
  for (const batch of batches) {
1553
2107
  if (options?.signal?.aborted) {
1554
2108
  break;
1555
2109
  }
1556
- await this.client.request({
1557
- method: "POST",
1558
- endpoint: `/api/v3/record/${objectTypeStr}/batch/delete`,
1559
- body: { data: batch },
1560
- signal: options?.signal
1561
- });
1562
- allDeletedIds.push(...batch);
2110
+ const result = await transport.batchDelete(objectTypeStr, batch, options?.signal);
2111
+ allDeletedIds.push(...result.ids);
1563
2112
  }
1564
2113
  this.client.invalidateCacheForMutation(objectTypeStr);
1565
2114
  return {
@@ -1737,7 +2286,15 @@ var FilesAPI = class {
1737
2286
  const objectTypeStr = String(objectType);
1738
2287
  const { buffer, filename, mimeType } = options;
1739
2288
  const config = this.client.getConfig();
1740
- const url = `${config.baseUrl}/api/v2/record/${objectTypeStr}/${recordId}/files`;
2289
+ if (!config.apiKey) {
2290
+ throw new FireberryError(
2291
+ "File upload requires an API key. SDK mode does not support file uploads.",
2292
+ {
2293
+ code: "INVALID_REQUEST" /* INVALID_REQUEST */
2294
+ }
2295
+ );
2296
+ }
2297
+ const url = `${config.baseUrl || "https://api.fireberry.com"}/api/v2/record/${objectTypeStr}/${recordId}/files`;
1741
2298
  const boundary = `----FormBoundary${Date.now()}`;
1742
2299
  const formParts = [];
1743
2300
  const fileHeader = [
@@ -1810,9 +2367,12 @@ function generateQueryCacheKey(options) {
1810
2367
  }
1811
2368
  var FireberryClient = class {
1812
2369
  config;
2370
+ normalizedConfig;
1813
2371
  cacheStore;
1814
2372
  inFlightQueries = /* @__PURE__ */ new Map();
1815
2373
  queryCache = /* @__PURE__ */ new Map();
2374
+ transport;
2375
+ metadataTransport;
1816
2376
  /** Metadata API operations */
1817
2377
  metadata;
1818
2378
  /** Records CRUD operations */
@@ -1827,8 +2387,8 @@ var FireberryClient = class {
1827
2387
  * Creates a new FireberryClient instance
1828
2388
  */
1829
2389
  constructor(config) {
1830
- this.config = {
1831
- apiKey: config.apiKey,
2390
+ this.config = config;
2391
+ this.normalizedConfig = {
1832
2392
  baseUrl: config.baseUrl || "https://api.fireberry.com",
1833
2393
  timeout: config.timeout || 3e4,
1834
2394
  retryOn429: config.retryOn429 ?? true,
@@ -1847,6 +2407,8 @@ var FireberryClient = class {
1847
2407
  fields: /* @__PURE__ */ new Map(),
1848
2408
  fieldValues: /* @__PURE__ */ new Map()
1849
2409
  };
2410
+ this.transport = createTransport(config);
2411
+ this.metadataTransport = createMetadataTransport(config);
1850
2412
  this.metadata = new MetadataAPI(this);
1851
2413
  this.records = new RecordsAPI(this);
1852
2414
  this.batch = new BatchAPI(this);
@@ -1854,10 +2416,34 @@ var FireberryClient = class {
1854
2416
  this.files = new FilesAPI(this);
1855
2417
  }
1856
2418
  /**
1857
- * Gets the client configuration
2419
+ * Gets the client configuration with all defaults applied
1858
2420
  */
1859
2421
  getConfig() {
1860
- return this.config;
2422
+ return {
2423
+ ...this.config,
2424
+ ...this.normalizedConfig
2425
+ };
2426
+ }
2427
+ /**
2428
+ * Gets the transport instance (for internal use by API modules)
2429
+ * @internal
2430
+ */
2431
+ getTransport() {
2432
+ return this.transport;
2433
+ }
2434
+ /**
2435
+ * Gets the metadata transport instance (for internal use by MetadataAPI)
2436
+ * Returns null if metadata is not available (SDK-only mode without API key)
2437
+ * @internal
2438
+ */
2439
+ getMetadataTransport() {
2440
+ return this.metadataTransport;
2441
+ }
2442
+ /**
2443
+ * Checks if metadata operations are available
2444
+ */
2445
+ isMetadataAvailable() {
2446
+ return isMetadataAvailable(this.config);
1861
2447
  }
1862
2448
  /**
1863
2449
  * Cache control methods
@@ -1906,7 +2492,7 @@ var FireberryClient = class {
1906
2492
  * @internal
1907
2493
  */
1908
2494
  invalidateCacheForMutation(objectType) {
1909
- if (this.config.invalidateCacheOnMutation) {
2495
+ if (this.normalizedConfig.invalidateCacheOnMutation) {
1910
2496
  this.cache.clearQueryResultsForObject(objectType);
1911
2497
  }
1912
2498
  }
@@ -1915,55 +2501,55 @@ var FireberryClient = class {
1915
2501
  */
1916
2502
  cleanupExpiredCacheEntries() {
1917
2503
  const now = Date.now();
1918
- if (this.config.cacheQueryResults) {
2504
+ if (this.normalizedConfig.cacheQueryResults) {
1919
2505
  for (const [key, entry] of this.queryCache) {
1920
- if (now - entry.timestamp >= this.config.queryResultCacheTTL) {
2506
+ if (now - entry.timestamp >= this.normalizedConfig.queryResultCacheTTL) {
1921
2507
  this.queryCache.delete(key);
1922
2508
  }
1923
2509
  }
1924
2510
  }
1925
- if (this.config.cacheMetadata) {
1926
- if (this.cacheStore.objects && now - this.cacheStore.objects.timestamp >= this.config.cacheTTL) {
2511
+ if (this.normalizedConfig.cacheMetadata) {
2512
+ if (this.cacheStore.objects && now - this.cacheStore.objects.timestamp >= this.normalizedConfig.cacheTTL) {
1927
2513
  this.cacheStore.objects = void 0;
1928
2514
  }
1929
2515
  for (const [key, entry] of this.cacheStore.fields) {
1930
- if (now - entry.timestamp >= this.config.cacheTTL) {
2516
+ if (now - entry.timestamp >= this.normalizedConfig.cacheTTL) {
1931
2517
  this.cacheStore.fields.delete(key);
1932
2518
  }
1933
2519
  }
1934
2520
  for (const [key, entry] of this.cacheStore.fieldValues) {
1935
- if (now - entry.timestamp >= this.config.cacheTTL) {
2521
+ if (now - entry.timestamp >= this.normalizedConfig.cacheTTL) {
1936
2522
  this.cacheStore.fieldValues.delete(key);
1937
2523
  }
1938
2524
  }
1939
2525
  }
1940
2526
  }
1941
2527
  getCached(type, objectType, fieldName) {
1942
- if (!this.config.cacheMetadata) {
2528
+ if (!this.normalizedConfig.cacheMetadata) {
1943
2529
  return void 0;
1944
2530
  }
1945
2531
  const now = Date.now();
1946
2532
  if (type === "objects") {
1947
2533
  const cached = this.cacheStore.objects;
1948
- if (cached && now - cached.timestamp < this.config.cacheTTL) {
2534
+ if (cached && now - cached.timestamp < this.normalizedConfig.cacheTTL) {
1949
2535
  return cached.data;
1950
2536
  }
1951
2537
  } else if (type === "fields" && objectType) {
1952
2538
  const cached = this.cacheStore.fields.get(objectType);
1953
- if (cached && now - cached.timestamp < this.config.cacheTTL) {
2539
+ if (cached && now - cached.timestamp < this.normalizedConfig.cacheTTL) {
1954
2540
  return cached.data;
1955
2541
  }
1956
2542
  } else if (type === "fieldValues" && objectType && fieldName) {
1957
2543
  const key = `${objectType}:${fieldName}`;
1958
2544
  const cached = this.cacheStore.fieldValues.get(key);
1959
- if (cached && now - cached.timestamp < this.config.cacheTTL) {
2545
+ if (cached && now - cached.timestamp < this.normalizedConfig.cacheTTL) {
1960
2546
  return cached.data;
1961
2547
  }
1962
2548
  }
1963
2549
  return void 0;
1964
2550
  }
1965
2551
  setCache(type, objectTypeOrData, fieldNameOrData, data) {
1966
- if (!this.config.cacheMetadata) {
2552
+ if (!this.normalizedConfig.cacheMetadata) {
1967
2553
  return;
1968
2554
  }
1969
2555
  this.cleanupExpiredCacheEntries();
@@ -2116,9 +2702,9 @@ var FireberryClient = class {
2116
2702
  */
2117
2703
  async query(options) {
2118
2704
  const cacheKey = generateQueryCacheKey(options);
2119
- if (this.config.cacheQueryResults) {
2705
+ if (this.normalizedConfig.cacheQueryResults) {
2120
2706
  const cached = this.queryCache.get(cacheKey);
2121
- if (cached && Date.now() - cached.timestamp < this.config.queryResultCacheTTL) {
2707
+ if (cached && Date.now() - cached.timestamp < this.normalizedConfig.queryResultCacheTTL) {
2122
2708
  return cached.data;
2123
2709
  }
2124
2710
  }
@@ -2130,7 +2716,7 @@ var FireberryClient = class {
2130
2716
  this.inFlightQueries.set(cacheKey, queryPromise);
2131
2717
  try {
2132
2718
  const result = await queryPromise;
2133
- if (this.config.cacheQueryResults) {
2719
+ if (this.normalizedConfig.cacheQueryResults) {
2134
2720
  this.cleanupExpiredCacheEntries();
2135
2721
  this.queryCache.set(cacheKey, {
2136
2722
  data: result,
@@ -2149,14 +2735,6 @@ var FireberryClient = class {
2149
2735
  const {
2150
2736
  objectType,
2151
2737
  fields,
2152
- query,
2153
- sortBy = "modifiedon",
2154
- sortType = "desc",
2155
- limit,
2156
- page = 1,
2157
- pageSize = 500,
2158
- showRealValue = true,
2159
- autoPage = true,
2160
2738
  signal
2161
2739
  } = options;
2162
2740
  let fieldsStr;
@@ -2170,87 +2748,10 @@ var FireberryClient = class {
2170
2748
  if (fieldsStr === "*") {
2171
2749
  fieldsStr = await this.expandStarFields(objectType, signal);
2172
2750
  }
2173
- if (autoPage) {
2174
- return this.queryAllPages({
2175
- objectType,
2176
- fields: fieldsStr,
2177
- query,
2178
- sortBy,
2179
- sortType,
2180
- showRealValue,
2181
- limit,
2182
- signal
2183
- });
2184
- }
2185
- const body = {
2186
- objecttype: objectType,
2187
- fields: fieldsStr,
2188
- query: query || "",
2189
- sort_by: sortBy,
2190
- sort_type: sortType,
2191
- page_size: Math.min(pageSize, limit || 500),
2192
- page_number: page,
2193
- show_real_value: showRealValue ? 1 : 0
2194
- };
2195
- const response = await this.request({
2196
- method: "POST",
2197
- endpoint: "/api/query",
2198
- body,
2199
- signal
2751
+ return this.transport.query({
2752
+ ...options,
2753
+ fields: fieldsStr
2200
2754
  });
2201
- const records = response.data?.Data || [];
2202
- return {
2203
- records,
2204
- total: records.length,
2205
- success: true
2206
- };
2207
- }
2208
- /**
2209
- * Fetches all pages of a query
2210
- */
2211
- async queryAllPages(options) {
2212
- const { objectType, fields, query, sortBy, sortType, showRealValue, limit, signal } = options;
2213
- const maxPageSize = 500;
2214
- const allRecords = [];
2215
- let currentPage = 1;
2216
- let hasMore = true;
2217
- while (hasMore) {
2218
- if (signal?.aborted) {
2219
- break;
2220
- }
2221
- const body = {
2222
- objecttype: objectType,
2223
- fields,
2224
- query: query || "",
2225
- sort_by: sortBy,
2226
- sort_type: sortType,
2227
- page_size: maxPageSize,
2228
- page_number: currentPage,
2229
- show_real_value: showRealValue ? 1 : 0
2230
- };
2231
- const response = await this.request({
2232
- method: "POST",
2233
- endpoint: "/api/query",
2234
- body,
2235
- signal
2236
- });
2237
- const pageData = response.data?.Data || [];
2238
- allRecords.push(...pageData);
2239
- if (limit && allRecords.length >= limit) {
2240
- allRecords.splice(limit);
2241
- break;
2242
- }
2243
- if (pageData.length < maxPageSize) {
2244
- hasMore = false;
2245
- } else {
2246
- currentPage++;
2247
- }
2248
- }
2249
- return {
2250
- records: allRecords,
2251
- total: allRecords.length,
2252
- success: true
2253
- };
2254
2755
  }
2255
2756
  /**
2256
2757
  * Expands '*' fields to actual field names, excluding problematic fields for specific object types
@@ -2270,104 +2771,20 @@ var FireberryClient = class {
2270
2771
  }
2271
2772
  /**
2272
2773
  * Makes a raw API request to the Fireberry API
2774
+ * @deprecated Use getTransport() or getMetadataTransport() for new code
2775
+ * @internal This method is kept for backwards compatibility with API modules
2273
2776
  */
2274
2777
  async request(options) {
2275
- const {
2276
- method,
2277
- endpoint,
2278
- query: queryParams,
2279
- body,
2280
- headers: customHeaders,
2281
- signal
2282
- } = options;
2283
- let url = `${this.config.baseUrl}${endpoint}`;
2284
- if (queryParams && Object.keys(queryParams).length > 0) {
2285
- const params = new URLSearchParams();
2286
- for (const [key, value] of Object.entries(queryParams)) {
2287
- if (value !== void 0 && value !== null) {
2288
- params.set(key, String(value));
2289
- }
2290
- }
2291
- url += `?${params.toString()}`;
2292
- }
2293
- const headers = {
2294
- Accept: "application/json",
2295
- tokenid: this.config.apiKey,
2296
- ...customHeaders
2297
- };
2298
- if (body) {
2299
- headers["Content-Type"] = "application/json";
2300
- }
2301
- const fetchOptions = {
2302
- method,
2303
- headers,
2304
- signal
2305
- };
2306
- if (body) {
2307
- fetchOptions.body = JSON.stringify(body);
2308
- }
2309
- return this.executeWithRetry(url, fetchOptions);
2310
- }
2311
- /**
2312
- * Executes a fetch request with retry logic for 429 errors
2313
- */
2314
- async executeWithRetry(url, options, retryCount = 0) {
2315
- try {
2316
- const timeoutController = new AbortController();
2317
- const timeoutId = setTimeout(() => {
2318
- timeoutController.abort();
2319
- }, this.config.timeout);
2320
- const combinedSignal = options.signal ? this.combineSignals([options.signal, timeoutController.signal]) : timeoutController.signal;
2321
- const response = await fetch(url, {
2322
- ...options,
2323
- signal: combinedSignal
2324
- });
2325
- clearTimeout(timeoutId);
2326
- if (response.status === 429 && this.config.retryOn429) {
2327
- if (retryCount < this.config.maxRetries) {
2328
- await wait(this.config.retryDelay);
2329
- return this.executeWithRetry(url, options, retryCount + 1);
2330
- }
2331
- throw new FireberryError("Rate limit exceeded after max retries", {
2332
- code: "RATE_LIMITED" /* RATE_LIMITED */,
2333
- statusCode: 429,
2334
- context: { retryCount }
2335
- });
2336
- }
2337
- let body;
2338
- const contentType = response.headers.get("content-type");
2339
- if (contentType?.includes("application/json")) {
2340
- body = await response.json();
2341
- } else {
2342
- body = await response.text();
2343
- }
2344
- if (!response.ok) {
2345
- throw createErrorFromResponse(response, body);
2346
- }
2347
- return body;
2348
- } catch (error) {
2349
- if (error instanceof Error && error.name === "AbortError") {
2350
- throw createNetworkError(error);
2351
- }
2352
- if (error instanceof FireberryError) {
2353
- throw error;
2354
- }
2355
- throw createNetworkError(error);
2356
- }
2357
- }
2358
- /**
2359
- * Combines multiple abort signals into one
2360
- */
2361
- combineSignals(signals) {
2362
- const controller = new AbortController();
2363
- for (const signal of signals) {
2364
- if (signal.aborted) {
2365
- controller.abort();
2366
- break;
2778
+ const transport = this.metadataTransport || this.transport;
2779
+ if (transport instanceof HTTPTransport) {
2780
+ return transport.request(options);
2781
+ }
2782
+ throw new FireberryError(
2783
+ "Raw request() is not supported in SDK mode. Use specific methods like query(), createRecord(), etc.",
2784
+ {
2785
+ code: "INVALID_REQUEST" /* INVALID_REQUEST */
2367
2786
  }
2368
- signal.addEventListener("abort", () => controller.abort(), { once: true });
2369
- }
2370
- return controller.signal;
2787
+ );
2371
2788
  }
2372
2789
  };
2373
2790
 
@@ -2852,6 +3269,14 @@ function getRelatedFieldInfo(fieldName) {
2852
3269
  return { ...parsed, ...resolved };
2853
3270
  }
2854
3271
 
3272
+ // src/types/transport.ts
3273
+ function isHTTPTransportConfig(config) {
3274
+ return "apiKey" in config;
3275
+ }
3276
+ function isSDKTransportConfig(config) {
3277
+ return "sdk" in config;
3278
+ }
3279
+
2855
3280
  // src/constants/objectNames.ts
2856
3281
  var OBJECT_NAME_MAP = {
2857
3282
  1: "accountname",
@@ -2981,6 +3406,8 @@ exports.escapeQueryValue = escapeQueryValue;
2981
3406
  exports.expandRelatedFields = expandRelatedFields;
2982
3407
  exports.getObjectTypeFromReferenceField = getObjectTypeFromReferenceField;
2983
3408
  exports.getRelatedFieldInfo = getRelatedFieldInfo;
3409
+ exports.isHTTPTransportConfig = isHTTPTransportConfig;
3410
+ exports.isSDKTransportConfig = isSDKTransportConfig;
2984
3411
  exports.parseRelatedField = parseRelatedField;
2985
3412
  exports.resolveRelatedField = resolveRelatedField;
2986
3413
  exports.sanitizeQuery = sanitizeQuery;