fireberry-api-client 1.0.0-beta.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.cjs ADDED
@@ -0,0 +1,1597 @@
1
+ 'use strict';
2
+
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __esm = (fn, res) => function __init() {
6
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
7
+ };
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+
13
+ // src/constants/excludedFields.ts
14
+ var excludedFields_exports = {};
15
+ __export(excludedFields_exports, {
16
+ EXCLUDED_FIELDS_FOR_STAR_QUERY: () => exports.EXCLUDED_FIELDS_FOR_STAR_QUERY,
17
+ getExcludedFieldsForStarQuery: () => getExcludedFieldsForStarQuery,
18
+ isExcludedFromStarQuery: () => isExcludedFromStarQuery
19
+ });
20
+ function isExcludedFromStarQuery(objectType, fieldName) {
21
+ const objectTypeStr = String(objectType);
22
+ const excludedFields = exports.EXCLUDED_FIELDS_FOR_STAR_QUERY[objectTypeStr];
23
+ return excludedFields ? excludedFields.includes(fieldName) : false;
24
+ }
25
+ function getExcludedFieldsForStarQuery(objectType) {
26
+ const objectTypeStr = String(objectType);
27
+ return exports.EXCLUDED_FIELDS_FOR_STAR_QUERY[objectTypeStr] || [];
28
+ }
29
+ exports.EXCLUDED_FIELDS_FOR_STAR_QUERY = void 0;
30
+ var init_excludedFields = __esm({
31
+ "src/constants/excludedFields.ts"() {
32
+ exports.EXCLUDED_FIELDS_FOR_STAR_QUERY = {
33
+ "7": ["deletedon", "deletedby"],
34
+ // Note
35
+ "8": ["deletedon", "deletedby"],
36
+ // Competitor
37
+ "114": ["deletedon", "deletedby"],
38
+ // Calendar Resource
39
+ "115": ["deletedon", "deletedby"],
40
+ // Customer Journey
41
+ "116": ["deletedon", "deletedby"],
42
+ // Profile
43
+ "117": ["deletedon", "deletedby"]
44
+ // Landing Page
45
+ };
46
+ }
47
+ });
48
+
49
+ // src/errors.ts
50
+ var FireberryErrorCode = /* @__PURE__ */ ((FireberryErrorCode2) => {
51
+ FireberryErrorCode2["UNKNOWN"] = "UNKNOWN";
52
+ FireberryErrorCode2["NETWORK_ERROR"] = "NETWORK_ERROR";
53
+ FireberryErrorCode2["TIMEOUT"] = "TIMEOUT";
54
+ FireberryErrorCode2["AUTHENTICATION_FAILED"] = "AUTHENTICATION_FAILED";
55
+ FireberryErrorCode2["AUTHORIZATION_FAILED"] = "AUTHORIZATION_FAILED";
56
+ FireberryErrorCode2["NOT_FOUND"] = "NOT_FOUND";
57
+ FireberryErrorCode2["RATE_LIMITED"] = "RATE_LIMITED";
58
+ FireberryErrorCode2["INVALID_REQUEST"] = "INVALID_REQUEST";
59
+ FireberryErrorCode2["SERVER_ERROR"] = "SERVER_ERROR";
60
+ FireberryErrorCode2["ABORTED"] = "ABORTED";
61
+ FireberryErrorCode2["INVALID_RESPONSE"] = "INVALID_RESPONSE";
62
+ return FireberryErrorCode2;
63
+ })(FireberryErrorCode || {});
64
+ var FireberryError = class _FireberryError extends Error {
65
+ /** Error code */
66
+ code;
67
+ /** HTTP status code if applicable */
68
+ statusCode;
69
+ /** Original error that caused this error */
70
+ cause;
71
+ /** Additional context data */
72
+ context;
73
+ constructor(message, options) {
74
+ super(message);
75
+ this.name = "FireberryError";
76
+ this.code = options.code;
77
+ this.statusCode = options.statusCode;
78
+ this.cause = options.cause;
79
+ this.context = options.context;
80
+ if (Error.captureStackTrace) {
81
+ Error.captureStackTrace(this, _FireberryError);
82
+ }
83
+ }
84
+ /**
85
+ * Creates a string representation of the error
86
+ */
87
+ toString() {
88
+ let str = `${this.name} [${this.code}]: ${this.message}`;
89
+ if (this.statusCode) {
90
+ str += ` (HTTP ${this.statusCode})`;
91
+ }
92
+ return str;
93
+ }
94
+ /**
95
+ * Converts the error to a plain object for logging/serialization
96
+ */
97
+ toJSON() {
98
+ return {
99
+ name: this.name,
100
+ message: this.message,
101
+ code: this.code,
102
+ statusCode: this.statusCode,
103
+ context: this.context,
104
+ stack: this.stack
105
+ };
106
+ }
107
+ };
108
+ function createErrorFromResponse(response, body) {
109
+ const status = response.status;
110
+ let code;
111
+ let message;
112
+ switch (status) {
113
+ case 400:
114
+ code = "INVALID_REQUEST" /* INVALID_REQUEST */;
115
+ message = "Invalid request parameters";
116
+ break;
117
+ case 401:
118
+ code = "AUTHENTICATION_FAILED" /* AUTHENTICATION_FAILED */;
119
+ message = "Authentication failed - invalid or missing API key";
120
+ break;
121
+ case 403:
122
+ code = "AUTHORIZATION_FAILED" /* AUTHORIZATION_FAILED */;
123
+ message = "Authorization failed - insufficient permissions";
124
+ break;
125
+ case 404:
126
+ code = "NOT_FOUND" /* NOT_FOUND */;
127
+ message = "Resource not found";
128
+ break;
129
+ case 429:
130
+ code = "RATE_LIMITED" /* RATE_LIMITED */;
131
+ message = "Rate limit exceeded - too many requests";
132
+ break;
133
+ default:
134
+ if (status >= 500) {
135
+ code = "SERVER_ERROR" /* SERVER_ERROR */;
136
+ message = `Server error (${status})`;
137
+ } else {
138
+ code = "UNKNOWN" /* UNKNOWN */;
139
+ message = `HTTP error ${status}`;
140
+ }
141
+ }
142
+ if (body && typeof body === "object") {
143
+ const bodyObj = body;
144
+ if (typeof bodyObj.message === "string") {
145
+ message = bodyObj.message;
146
+ } else if (typeof bodyObj.error === "string") {
147
+ message = bodyObj.error;
148
+ }
149
+ }
150
+ return new FireberryError(message, {
151
+ code,
152
+ statusCode: status,
153
+ context: { body }
154
+ });
155
+ }
156
+ function createNetworkError(error) {
157
+ if (error.name === "AbortError") {
158
+ return new FireberryError("Request was aborted", {
159
+ code: "ABORTED" /* ABORTED */,
160
+ cause: error
161
+ });
162
+ }
163
+ if (error.name === "TimeoutError" || error.message.includes("timeout")) {
164
+ return new FireberryError("Request timed out", {
165
+ code: "TIMEOUT" /* TIMEOUT */,
166
+ cause: error
167
+ });
168
+ }
169
+ return new FireberryError(`Network error: ${error.message}`, {
170
+ code: "NETWORK_ERROR" /* NETWORK_ERROR */,
171
+ cause: error
172
+ });
173
+ }
174
+
175
+ // src/utils/helpers.ts
176
+ function wait(ms) {
177
+ return new Promise((resolve) => {
178
+ setTimeout(resolve, ms);
179
+ });
180
+ }
181
+ function chunkArray(array, size) {
182
+ const result = [];
183
+ for (let i = 0; i < array.length; i += size) {
184
+ result.push(array.slice(i, i + size));
185
+ }
186
+ return result;
187
+ }
188
+
189
+ // src/utils/queryBuilder.ts
190
+ function escapeQueryValue(value) {
191
+ if (!value) {
192
+ return "";
193
+ }
194
+ let escaped = value.replace(/\\/g, "\\\\");
195
+ escaped = escaped.replace(/\(/g, "\\(");
196
+ escaped = escaped.replace(/\)/g, "\\)");
197
+ escaped = escaped.replace(/\bor\b/gi, "\\or");
198
+ escaped = escaped.replace(/\band\b/gi, "\\and");
199
+ return escaped;
200
+ }
201
+ function sanitizeQuery(query) {
202
+ if (!query) {
203
+ return "";
204
+ }
205
+ query = query.replace(
206
+ /\(\s*([a-zA-Z0-9_]+)\s+(is-null|is-not-null)\s*\)/g,
207
+ "($1 __SPECIAL_OPERATOR__$2)"
208
+ );
209
+ query = query.replace(
210
+ /\(\s*([a-zA-Z0-9_]+)\s+(start-with|not-start-with)\s+([^)]+)\s*\)/g,
211
+ "($1 __TEXT_OPERATOR__$2 $3)"
212
+ );
213
+ query = query.replace(
214
+ /\(\s*([a-zA-Z0-9_]+(?:field|Field|system|System)[0-9]*)\s+(?!__SPECIAL_OPERATOR__|__TEXT_OPERATOR__)([^()<>=!]+)\s*\)/g,
215
+ "($1 = $2)"
216
+ );
217
+ query = query.replace(
218
+ /\(\s*([a-zA-Z0-9_]+)\s+(?!__SPECIAL_OPERATOR__|__TEXT_OPERATOR__|<=|>=|!=|<|>|=\s)([^()<>]+)\s*\)/g,
219
+ "($1 = $2)"
220
+ );
221
+ query = query.replace(/__SPECIAL_OPERATOR__/g, "");
222
+ query = query.replace(/__TEXT_OPERATOR__/g, "");
223
+ query = query.replace(/\(\s*(?:<=|>=|!=|<|>|=)\s*\)/g, "");
224
+ query = query.replace(/\(\s*(?:start-with|not-start-with)\s*\)/gi, "");
225
+ query = query.replace(/\(\s*(?:and|or)\s*\)/gi, "");
226
+ query = query.replace(/\(\s*\)/g, "");
227
+ query = query.replace(/^\s*(and|or)\s*/gi, "");
228
+ query = query.replace(/\s*(and|or)\s*$/gi, "");
229
+ const nestedPattern = /\(\s*\(([^()]+)\)\s*\)/g;
230
+ while (nestedPattern.test(query)) {
231
+ query = query.replace(nestedPattern, "($1)");
232
+ }
233
+ query = query.replace(/\s+/g, " ");
234
+ return query.trim();
235
+ }
236
+ var QueryBuilder = class {
237
+ conditions = [];
238
+ joinOperators = [];
239
+ currentField = null;
240
+ selectedFields = [];
241
+ objectTypeId = null;
242
+ sortByField = null;
243
+ sortDirection = "desc";
244
+ limitValue = null;
245
+ pageNumber = 1;
246
+ showRealValueFlag = true;
247
+ client = null;
248
+ /**
249
+ * Creates a new QueryBuilder
250
+ * @param client - Optional FireberryClient for executing queries
251
+ */
252
+ constructor(client) {
253
+ this.client = client ?? null;
254
+ }
255
+ /**
256
+ * Sets the object type for the query
257
+ * @param objectType - Object type ID (e.g., '1' for Account)
258
+ */
259
+ objectType(objectType) {
260
+ this.objectTypeId = String(objectType);
261
+ return this;
262
+ }
263
+ /**
264
+ * Adds fields to select
265
+ * @param fields - Field names to select
266
+ */
267
+ select(...fields) {
268
+ this.selectedFields.push(...fields);
269
+ return this;
270
+ }
271
+ /**
272
+ * Starts a new WHERE condition
273
+ * @param field - Field name to filter on
274
+ */
275
+ where(field) {
276
+ this.currentField = field;
277
+ return this.createConditionBuilder();
278
+ }
279
+ /**
280
+ * Adds an AND logical operator
281
+ */
282
+ and() {
283
+ if (this.conditions.length > 0) {
284
+ this.joinOperators.push("and");
285
+ }
286
+ return this;
287
+ }
288
+ /**
289
+ * Adds an OR logical operator
290
+ */
291
+ or() {
292
+ if (this.conditions.length > 0) {
293
+ this.joinOperators.push("or");
294
+ }
295
+ return this;
296
+ }
297
+ /**
298
+ * Sets the sort field and direction
299
+ * @param field - Field to sort by
300
+ * @param direction - Sort direction ('asc' or 'desc')
301
+ */
302
+ sortBy(field, direction = "desc") {
303
+ this.sortByField = field;
304
+ this.sortDirection = direction;
305
+ return this;
306
+ }
307
+ /**
308
+ * Sets the maximum number of records to return
309
+ * @param count - Maximum record count
310
+ */
311
+ limit(count) {
312
+ this.limitValue = count;
313
+ return this;
314
+ }
315
+ /**
316
+ * Sets the page number for pagination
317
+ * @param page - Page number (1-based)
318
+ */
319
+ page(page) {
320
+ this.pageNumber = page;
321
+ return this;
322
+ }
323
+ /**
324
+ * Controls whether to show real values (labels) for dropdown fields
325
+ * @param show - Whether to show real values (default: true)
326
+ */
327
+ showRealValue(show) {
328
+ this.showRealValueFlag = show;
329
+ return this;
330
+ }
331
+ /**
332
+ * Builds the query string from conditions
333
+ * @returns The built query string
334
+ */
335
+ build() {
336
+ if (this.conditions.length === 0) {
337
+ return "";
338
+ }
339
+ const parts = [];
340
+ for (let i = 0; i < this.conditions.length; i++) {
341
+ const condition = this.conditions[i];
342
+ let conditionStr;
343
+ if (condition.operator === "is-null" || condition.operator === "is-not-null") {
344
+ conditionStr = `(${condition.field} ${condition.operator})`;
345
+ } else {
346
+ const escapedValue = escapeQueryValue(condition.value || "");
347
+ conditionStr = `(${condition.field} ${condition.operator} ${escapedValue})`;
348
+ }
349
+ parts.push(conditionStr);
350
+ if (i < this.joinOperators.length) {
351
+ parts.push(this.joinOperators[i]);
352
+ }
353
+ }
354
+ return parts.join(" ");
355
+ }
356
+ /**
357
+ * Returns the selected fields array
358
+ * Useful for inspecting the query configuration
359
+ */
360
+ getFields() {
361
+ return [...this.selectedFields];
362
+ }
363
+ /**
364
+ * Converts the query builder state to a payload compatible with @fireberry/sdk
365
+ *
366
+ * @returns Object with `fields` (comma-separated string) and `query` (filter string)
367
+ *
368
+ * @example
369
+ * ```typescript
370
+ * import FireberryClientSDK from '@fireberry/sdk/client';
371
+ * import { QueryBuilder } from 'fireberry-api-client';
372
+ *
373
+ * const sdk = new FireberryClientSDK();
374
+ * await sdk.initializeContext();
375
+ *
376
+ * const payload = new QueryBuilder()
377
+ * .select('accountid', 'accountname')
378
+ * .where('statuscode').equals('1')
379
+ * .toSDKPayload();
380
+ *
381
+ * // Use with SDK
382
+ * const results = await sdk.api.query(1, payload);
383
+ * ```
384
+ */
385
+ toSDKPayload() {
386
+ const payload = {
387
+ fields: this.selectedFields.length > 0 ? this.selectedFields.join(",") : "*",
388
+ query: this.build()
389
+ };
390
+ if (this.limitValue !== null) {
391
+ payload.page_size = this.limitValue;
392
+ }
393
+ if (this.pageNumber > 1) {
394
+ payload.page_number = this.pageNumber;
395
+ }
396
+ return payload;
397
+ }
398
+ /**
399
+ * Executes the query (requires client to be set)
400
+ * @param signal - Optional AbortSignal for cancellation
401
+ * @returns Query results
402
+ */
403
+ async execute(signal) {
404
+ if (!this.client) {
405
+ throw new Error("QueryBuilder requires a client to execute queries. Pass a FireberryClient to the constructor.");
406
+ }
407
+ if (!this.objectTypeId) {
408
+ throw new Error("Object type is required. Use .objectType() before executing.");
409
+ }
410
+ const queryOptions = {
411
+ objectType: this.objectTypeId,
412
+ fields: this.selectedFields.length > 0 ? this.selectedFields : ["*"],
413
+ query: this.build(),
414
+ showRealValue: this.showRealValueFlag
415
+ };
416
+ if (this.sortByField) {
417
+ queryOptions.sortBy = this.sortByField;
418
+ queryOptions.sortType = this.sortDirection;
419
+ }
420
+ if (this.limitValue !== null) {
421
+ queryOptions.limit = this.limitValue;
422
+ }
423
+ if (this.pageNumber > 1) {
424
+ queryOptions.page = this.pageNumber;
425
+ }
426
+ if (signal) {
427
+ queryOptions.signal = signal;
428
+ }
429
+ return this.client.query(queryOptions);
430
+ }
431
+ /**
432
+ * Creates a condition builder for the current field
433
+ */
434
+ createConditionBuilder() {
435
+ const field = this.currentField;
436
+ return {
437
+ equals: (value) => {
438
+ this.addCondition(field, "=", String(value));
439
+ return this;
440
+ },
441
+ notEquals: (value) => {
442
+ this.addCondition(field, "!=", String(value));
443
+ return this;
444
+ },
445
+ lessThan: (value) => {
446
+ this.addCondition(field, "<", String(value));
447
+ return this;
448
+ },
449
+ greaterThan: (value) => {
450
+ this.addCondition(field, ">", String(value));
451
+ return this;
452
+ },
453
+ lessThanOrEqual: (value) => {
454
+ this.addCondition(field, "<=", String(value));
455
+ return this;
456
+ },
457
+ greaterThanOrEqual: (value) => {
458
+ this.addCondition(field, ">=", String(value));
459
+ return this;
460
+ },
461
+ contains: (value) => {
462
+ this.addCondition(field, "start-with", `%${value}`);
463
+ return this;
464
+ },
465
+ notContains: (value) => {
466
+ this.addCondition(field, "not-start-with", `%${value}`);
467
+ return this;
468
+ },
469
+ startsWith: (value) => {
470
+ this.addCondition(field, "start-with", value);
471
+ return this;
472
+ },
473
+ notStartsWith: (value) => {
474
+ this.addCondition(field, "not-start-with", value);
475
+ return this;
476
+ },
477
+ isNull: () => {
478
+ this.addCondition(field, "is-null");
479
+ return this;
480
+ },
481
+ isNotNull: () => {
482
+ this.addCondition(field, "is-not-null");
483
+ return this;
484
+ }
485
+ };
486
+ }
487
+ /**
488
+ * Adds a condition to the query
489
+ */
490
+ addCondition(field, operator, value) {
491
+ this.conditions.push({ field, operator, value });
492
+ this.currentField = null;
493
+ }
494
+ };
495
+
496
+ // src/constants/fieldTypes.ts
497
+ var FIELD_TYPE_IDS = {
498
+ DROPDOWN: "b4919f2e-2996-48e4-a03c-ba39fb64386c",
499
+ LOOKUP: "a8fcdf65-91bc-46fd-82f6-1234758345a1",
500
+ EMAIL: "c713d2f7-8fa9-43c3-8062-f07486eaf567",
501
+ TEXT: "a1e7ed6f-5083-477b-b44c-9943a6181359",
502
+ URL: "c820d32f-44df-4c2a-9c1e-18734e864fd5",
503
+ LONG_TEXT: "80108f9d-1e75-40fa-9fa9-02be4ddc1da1",
504
+ DATETIME: "ce972d02-5013-46d4-9d1d-f09df1ac346a",
505
+ DATE: "83bf530c-e04c-462b-9ffc-a46f750fc072",
506
+ HTML: "ed2ad39d-32fc-4585-8f5b-2e93463f050a",
507
+ TELEPHONE: "3f62f67a-1cee-403a-bec6-aa02a9804edb",
508
+ NUMERIC: "6a34bfe3-fece-4da1-9136-a7b1e5ae3319"
509
+ };
510
+ var FIELD_TYPE_MAPPINGS = {
511
+ [FIELD_TYPE_IDS.DROPDOWN]: "Dropdown",
512
+ [FIELD_TYPE_IDS.EMAIL]: "Email",
513
+ [FIELD_TYPE_IDS.TEXT]: "Text",
514
+ [FIELD_TYPE_IDS.LOOKUP]: "Lookup",
515
+ [FIELD_TYPE_IDS.URL]: "URL",
516
+ [FIELD_TYPE_IDS.LONG_TEXT]: "Long Text",
517
+ [FIELD_TYPE_IDS.DATETIME]: "DateTime",
518
+ [FIELD_TYPE_IDS.DATE]: "Date",
519
+ [FIELD_TYPE_IDS.HTML]: "HTML",
520
+ [FIELD_TYPE_IDS.TELEPHONE]: "Telephone",
521
+ [FIELD_TYPE_IDS.NUMERIC]: "Number"
522
+ };
523
+
524
+ // src/api/metadata.ts
525
+ var MetadataAPI = class {
526
+ constructor(client) {
527
+ this.client = client;
528
+ }
529
+ /**
530
+ * Gets all available objects/entity types from Fireberry
531
+ *
532
+ * @param signal - Optional AbortSignal for cancellation
533
+ * @returns List of all objects
534
+ *
535
+ * @example
536
+ * ```typescript
537
+ * const result = await client.metadata.getObjects();
538
+ * console.log(result.objects); // [{ objectType: 1, name: 'Account', ... }, ...]
539
+ * ```
540
+ */
541
+ async getObjects(signal) {
542
+ const cached = this.client.getCached("objects");
543
+ if (cached) {
544
+ return cached;
545
+ }
546
+ const response = await this.client.request({
547
+ method: "GET",
548
+ endpoint: "/metadata/records",
549
+ signal
550
+ });
551
+ const result = {
552
+ objects: response.data || [],
553
+ total: response.data?.length || 0,
554
+ success: true
555
+ };
556
+ this.client.setCache("objects", result);
557
+ return result;
558
+ }
559
+ /**
560
+ * Gets all fields for a specific object type
561
+ *
562
+ * @param objectType - The object type ID (e.g., '1' for Account)
563
+ * @param signal - Optional AbortSignal for cancellation
564
+ * @returns List of fields with metadata
565
+ *
566
+ * @example
567
+ * ```typescript
568
+ * const result = await client.metadata.getFields('1');
569
+ * console.log(result.fields); // [{ fieldName: 'accountid', label: 'Account ID', ... }, ...]
570
+ * ```
571
+ */
572
+ async getFields(objectType, signal) {
573
+ const objectTypeStr = String(objectType);
574
+ const cached = this.client.getCached("fields", objectTypeStr);
575
+ if (cached) {
576
+ return cached;
577
+ }
578
+ const response = await this.client.request({
579
+ method: "GET",
580
+ endpoint: `/metadata/records/${objectTypeStr}/fields`,
581
+ signal
582
+ });
583
+ const fields = (response.data || []).map((field) => ({
584
+ ...field,
585
+ fieldType: FIELD_TYPE_MAPPINGS[field.systemFieldTypeId] || field.systemFieldTypeId
586
+ }));
587
+ const result = {
588
+ objectTypeId: objectTypeStr,
589
+ fields,
590
+ total: fields.length,
591
+ success: true
592
+ };
593
+ this.client.setCache("fields", objectTypeStr, result);
594
+ return result;
595
+ }
596
+ /**
597
+ * Gets all possible values for a dropdown field
598
+ *
599
+ * @param objectType - The object type ID
600
+ * @param fieldName - The field name
601
+ * @param signal - Optional AbortSignal for cancellation
602
+ * @returns List of dropdown values
603
+ *
604
+ * @example
605
+ * ```typescript
606
+ * const result = await client.metadata.getFieldValues('1', 'statuscode');
607
+ * console.log(result.values); // [{ name: 'Active', value: '1' }, { name: 'Inactive', value: '2' }]
608
+ * ```
609
+ */
610
+ async getFieldValues(objectType, fieldName, signal) {
611
+ const objectTypeStr = String(objectType);
612
+ const cached = this.client.getCached(
613
+ "fieldValues",
614
+ objectTypeStr,
615
+ fieldName
616
+ );
617
+ if (cached) {
618
+ return cached;
619
+ }
620
+ const response = await this.client.request({
621
+ method: "GET",
622
+ endpoint: `/metadata/records/${objectTypeStr}/fields/${fieldName}/values`,
623
+ signal
624
+ });
625
+ const result = {
626
+ objectTypeId: objectTypeStr,
627
+ fieldName,
628
+ values: response.data?.values || [],
629
+ total: response.data?.values?.length || 0,
630
+ success: true
631
+ };
632
+ this.client.setCache("fieldValues", objectTypeStr, fieldName, result);
633
+ return result;
634
+ }
635
+ };
636
+
637
+ // src/constants/objectIds.ts
638
+ var OBJECT_ID_MAP = {
639
+ 1: "accountid",
640
+ 2: "contactid",
641
+ 3: "leadid",
642
+ 4: "opportunityid",
643
+ 5: "casesid",
644
+ 6: "activityid",
645
+ 7: "noteid",
646
+ 8: "competitorid",
647
+ 9: "crmuserid",
648
+ 10: "taskid",
649
+ 13: "crmorderid",
650
+ 14: "productid",
651
+ 17: "crmorderitemid",
652
+ 20: "emailtemplateid",
653
+ 23: "businessunitid",
654
+ 27: "printtemplateid",
655
+ 28: "contractid",
656
+ 33: "accountproductid",
657
+ 46: "projectid",
658
+ 67: "campaignid",
659
+ 76: "articleid",
660
+ 86: "invoiceid",
661
+ 101: "attendanceclockid",
662
+ 102: "activitylogid",
663
+ 104: "conversationid",
664
+ 114: "calendarresourceid"
665
+ };
666
+ function getObjectIdFieldName(objectTypeId) {
667
+ const objectTypeNum = typeof objectTypeId === "string" ? parseInt(objectTypeId, 10) : objectTypeId;
668
+ if (OBJECT_ID_MAP[objectTypeNum]) {
669
+ return OBJECT_ID_MAP[objectTypeNum];
670
+ }
671
+ if (objectTypeNum >= 1e3) {
672
+ return `customobject${objectTypeNum}id`;
673
+ }
674
+ return "id";
675
+ }
676
+
677
+ // src/api/records.ts
678
+ var RecordsAPI = class {
679
+ constructor(client) {
680
+ this.client = client;
681
+ }
682
+ /**
683
+ * Creates a new record in Fireberry
684
+ *
685
+ * @param objectType - The object type ID (e.g., '1' for Account)
686
+ * @param data - Record data to create
687
+ * @param options - Optional settings
688
+ * @returns Created record data
689
+ *
690
+ * @example
691
+ * ```typescript
692
+ * const result = await client.records.create('1', {
693
+ * accountname: 'New Account',
694
+ * emailaddress1: 'contact@example.com',
695
+ * });
696
+ * ```
697
+ */
698
+ async create(objectType, data, options) {
699
+ const response = await this.client.request({
700
+ method: "POST",
701
+ endpoint: `/api/v2/record/${objectType}`,
702
+ body: data,
703
+ signal: options?.signal
704
+ });
705
+ return response.record;
706
+ }
707
+ /**
708
+ * Updates an existing record in Fireberry
709
+ *
710
+ * @param objectType - The object type ID
711
+ * @param recordId - The record ID to update
712
+ * @param data - Record data to update
713
+ * @param options - Optional settings
714
+ * @returns Updated record data
715
+ *
716
+ * @example
717
+ * ```typescript
718
+ * const result = await client.records.update('1', 'abc123', {
719
+ * accountname: 'Updated Account Name',
720
+ * });
721
+ * ```
722
+ */
723
+ async update(objectType, recordId, data, options) {
724
+ const response = await this.client.request({
725
+ method: "PUT",
726
+ endpoint: `/api/v2/record/${objectType}/${recordId}`,
727
+ body: data,
728
+ signal: options?.signal
729
+ });
730
+ return response.record;
731
+ }
732
+ /**
733
+ * Deletes a record from Fireberry
734
+ *
735
+ * @param objectType - The object type ID
736
+ * @param recordId - The record ID to delete
737
+ * @param options - Optional settings
738
+ * @returns Success status
739
+ *
740
+ * @example
741
+ * ```typescript
742
+ * await client.records.delete('1', 'abc123');
743
+ * ```
744
+ */
745
+ async delete(objectType, recordId, options) {
746
+ await this.client.request({
747
+ method: "DELETE",
748
+ endpoint: `/api/record/${objectType}/${recordId}`,
749
+ signal: options?.signal
750
+ });
751
+ return {
752
+ success: true,
753
+ id: recordId
754
+ };
755
+ }
756
+ /**
757
+ * Upserts a record (creates if not exists, updates if exists)
758
+ *
759
+ * @param objectType - The object type ID
760
+ * @param keyFields - Fields to use for matching existing records
761
+ * @param data - Record data to upsert
762
+ * @param options - Optional settings
763
+ * @returns Upsert result with operation type and record data
764
+ *
765
+ * @example
766
+ * ```typescript
767
+ * const result = await client.records.upsert('1', ['emailaddress1'], {
768
+ * accountname: 'Acme Corp',
769
+ * emailaddress1: 'contact@acme.com',
770
+ * });
771
+ * console.log(result.operationType); // 'create' or 'update'
772
+ * ```
773
+ */
774
+ async upsert(objectType, keyFields, data, options) {
775
+ const objectTypeStr = String(objectType);
776
+ const upsertKeyValues = {};
777
+ for (const key of keyFields) {
778
+ if (!(key in data)) {
779
+ throw new Error(`Missing value for upsert key field: ${key}`);
780
+ }
781
+ upsertKeyValues[key] = data[key];
782
+ }
783
+ const queryConditions = keyFields.map((key) => `(${key} = ${data[key]})`);
784
+ const queryString = queryConditions.join(" and ");
785
+ const queryResult = await this.client.query({
786
+ objectType: objectTypeStr,
787
+ fields: "*",
788
+ query: queryString,
789
+ limit: 1,
790
+ showRealValue: true,
791
+ signal: options?.signal
792
+ });
793
+ const existingRecords = queryResult.records;
794
+ if (existingRecords.length > 0) {
795
+ const existingRecord = existingRecords[0];
796
+ const idFieldName = getObjectIdFieldName(objectTypeStr);
797
+ const recordId = String(existingRecord[idFieldName]);
798
+ const updatedRecord = await this.update(objectTypeStr, recordId, data, options);
799
+ return {
800
+ success: true,
801
+ operationType: "update",
802
+ upsertKeys: keyFields,
803
+ upsertKeyValues,
804
+ oldRecord: existingRecord,
805
+ newRecord: updatedRecord
806
+ };
807
+ } else {
808
+ const createdRecord = await this.create(objectTypeStr, data, options);
809
+ return {
810
+ success: true,
811
+ operationType: "create",
812
+ upsertKeys: keyFields,
813
+ upsertKeyValues,
814
+ oldRecord: null,
815
+ newRecord: createdRecord
816
+ };
817
+ }
818
+ }
819
+ };
820
+
821
+ // src/api/batch.ts
822
+ var BATCH_SIZE = 20;
823
+ var BatchAPI = class {
824
+ constructor(client) {
825
+ this.client = client;
826
+ }
827
+ /**
828
+ * Creates multiple records in batch
829
+ * Automatically chunks into batches of 20 records
830
+ *
831
+ * @param objectType - The object type ID
832
+ * @param records - Array of records to create
833
+ * @param options - Optional settings
834
+ * @returns Batch result with all created records
835
+ *
836
+ * @example
837
+ * ```typescript
838
+ * const result = await client.batch.create('1', [
839
+ * { accountname: 'Account 1' },
840
+ * { accountname: 'Account 2' },
841
+ * ]);
842
+ * console.log(result.count); // 2
843
+ * ```
844
+ */
845
+ async create(objectType, records, options) {
846
+ const objectTypeStr = String(objectType);
847
+ const batches = chunkArray(records, BATCH_SIZE);
848
+ const allResponses = [];
849
+ for (const batch of batches) {
850
+ if (options?.signal?.aborted) {
851
+ break;
852
+ }
853
+ const response = await this.client.request({
854
+ method: "POST",
855
+ endpoint: `/api/v3/record/${objectTypeStr}/batch/create`,
856
+ body: { data: batch },
857
+ signal: options?.signal
858
+ });
859
+ if (response.data) {
860
+ if (Array.isArray(response.data)) {
861
+ allResponses.push(...response.data);
862
+ } else {
863
+ allResponses.push(response.data);
864
+ }
865
+ }
866
+ }
867
+ return {
868
+ success: true,
869
+ data: allResponses,
870
+ count: allResponses.length
871
+ };
872
+ }
873
+ /**
874
+ * Updates multiple records in batch
875
+ * Automatically chunks into batches of 20 records
876
+ *
877
+ * @param objectType - The object type ID
878
+ * @param records - Array of records with ID and data to update
879
+ * @param options - Optional settings
880
+ * @returns Batch result with all updated records
881
+ *
882
+ * @example
883
+ * ```typescript
884
+ * const result = await client.batch.update('1', [
885
+ * { id: 'abc123', record: { accountname: 'Updated 1' } },
886
+ * { id: 'def456', record: { accountname: 'Updated 2' } },
887
+ * ]);
888
+ * ```
889
+ */
890
+ async update(objectType, records, options) {
891
+ const objectTypeStr = String(objectType);
892
+ const batches = chunkArray(records, BATCH_SIZE);
893
+ const allResponses = [];
894
+ for (const batch of batches) {
895
+ if (options?.signal?.aborted) {
896
+ break;
897
+ }
898
+ const response = await this.client.request({
899
+ method: "POST",
900
+ endpoint: `/api/v3/record/${objectTypeStr}/batch/update`,
901
+ body: { data: batch },
902
+ signal: options?.signal
903
+ });
904
+ if (response.data) {
905
+ if (Array.isArray(response.data)) {
906
+ allResponses.push(...response.data);
907
+ } else {
908
+ allResponses.push(response.data);
909
+ }
910
+ }
911
+ }
912
+ return {
913
+ success: true,
914
+ data: allResponses,
915
+ count: allResponses.length
916
+ };
917
+ }
918
+ /**
919
+ * Deletes multiple records in batch
920
+ * Automatically chunks into batches of 20 records
921
+ *
922
+ * @param objectType - The object type ID
923
+ * @param recordIds - Array of record IDs to delete
924
+ * @param options - Optional settings
925
+ * @returns Batch delete result with deleted IDs
926
+ *
927
+ * @example
928
+ * ```typescript
929
+ * const result = await client.batch.delete('1', ['abc123', 'def456']);
930
+ * console.log(result.ids); // ['abc123', 'def456']
931
+ * ```
932
+ */
933
+ async delete(objectType, recordIds, options) {
934
+ const objectTypeStr = String(objectType);
935
+ const batches = chunkArray(recordIds, BATCH_SIZE);
936
+ const allDeletedIds = [];
937
+ for (const batch of batches) {
938
+ if (options?.signal?.aborted) {
939
+ break;
940
+ }
941
+ await this.client.request({
942
+ method: "POST",
943
+ endpoint: `/api/v3/record/${objectTypeStr}/batch/delete`,
944
+ body: { data: batch },
945
+ signal: options?.signal
946
+ });
947
+ allDeletedIds.push(...batch);
948
+ }
949
+ return {
950
+ success: true,
951
+ ids: allDeletedIds,
952
+ count: allDeletedIds.length
953
+ };
954
+ }
955
+ };
956
+
957
+ // src/api/fields.ts
958
+ var FIELD_TYPE_ENDPOINTS = {
959
+ text: "text",
960
+ email: "email",
961
+ url: "url",
962
+ phone: "phone",
963
+ number: "number",
964
+ textarea: "textarea",
965
+ html: "html",
966
+ date: "date",
967
+ datetime: "datetime",
968
+ lookup: "lookup",
969
+ summary: "summary",
970
+ formula: "formula",
971
+ picklist: "picklist"
972
+ };
973
+ var FieldsAPI = class {
974
+ constructor(client) {
975
+ this.client = client;
976
+ }
977
+ /**
978
+ * Creates a new custom field in a Fireberry object
979
+ *
980
+ * @param objectType - The object type ID
981
+ * @param options - Field creation options
982
+ * @returns Created field result
983
+ *
984
+ * @example
985
+ * ```typescript
986
+ * // Create a text field
987
+ * const result = await client.fields.create('1', {
988
+ * type: 'text',
989
+ * fieldName: 'pcf_custom_field',
990
+ * label: 'Custom Field',
991
+ * maxLength: 100,
992
+ * });
993
+ *
994
+ * // Create a picklist field
995
+ * const result = await client.fields.create('1', {
996
+ * type: 'picklist',
997
+ * fieldName: 'pcf_status',
998
+ * label: 'Status',
999
+ * values: [
1000
+ * { name: 'Active', value: '1' },
1001
+ * { name: 'Inactive', value: '2' },
1002
+ * ],
1003
+ * });
1004
+ *
1005
+ * // Create a lookup field
1006
+ * const result = await client.fields.create('2', {
1007
+ * type: 'lookup',
1008
+ * fieldName: 'pcf_related_account',
1009
+ * label: 'Related Account',
1010
+ * relatedObjectId: '1',
1011
+ * });
1012
+ * ```
1013
+ */
1014
+ async create(objectType, options) {
1015
+ const objectTypeStr = String(objectType);
1016
+ const { type, fieldName, label, defaultValue, follow, autoComplete } = options;
1017
+ const fieldData = {
1018
+ fieldName,
1019
+ label
1020
+ };
1021
+ if (defaultValue !== void 0) {
1022
+ fieldData.defaultValue = defaultValue;
1023
+ }
1024
+ if (follow !== void 0) {
1025
+ fieldData.follow = follow;
1026
+ }
1027
+ if (autoComplete !== void 0 && ["text", "email", "url", "phone", "number"].includes(type)) {
1028
+ fieldData.autoComplete = autoComplete;
1029
+ }
1030
+ switch (type) {
1031
+ case "text":
1032
+ case "email":
1033
+ case "url": {
1034
+ const opts = options;
1035
+ if (opts.maxLength !== void 0 && opts.maxLength > 0) {
1036
+ fieldData.maxLength = opts.maxLength;
1037
+ }
1038
+ break;
1039
+ }
1040
+ case "number": {
1041
+ const opts = options;
1042
+ if (opts.precision !== void 0) {
1043
+ fieldData.precision = opts.precision;
1044
+ }
1045
+ break;
1046
+ }
1047
+ case "lookup": {
1048
+ const opts = options;
1049
+ fieldData.relatedObjectId = opts.relatedObjectId;
1050
+ break;
1051
+ }
1052
+ case "picklist": {
1053
+ const opts = options;
1054
+ fieldData.values = opts.values;
1055
+ break;
1056
+ }
1057
+ case "summary": {
1058
+ const opts = options;
1059
+ fieldData.summaryType = opts.summaryType;
1060
+ fieldData.relatedObjectId = opts.relatedObjectId;
1061
+ if (opts.summaryField) {
1062
+ fieldData.summaryField = opts.summaryField;
1063
+ }
1064
+ break;
1065
+ }
1066
+ case "formula": {
1067
+ const opts = options;
1068
+ fieldData.formula = opts.formula;
1069
+ fieldData.fieldType = opts.formulaFieldType;
1070
+ if (opts.formulaFieldType === "number" && opts.formulaPrecision !== void 0) {
1071
+ fieldData.precision = opts.formulaPrecision;
1072
+ }
1073
+ break;
1074
+ }
1075
+ }
1076
+ const endpoint = FIELD_TYPE_ENDPOINTS[type];
1077
+ if (!endpoint) {
1078
+ throw new Error(`Unsupported field type: ${type}`);
1079
+ }
1080
+ const response = await this.client.request({
1081
+ method: "POST",
1082
+ endpoint: `/api/v2/system-field/${objectTypeStr}/${endpoint}`,
1083
+ body: fieldData
1084
+ });
1085
+ return {
1086
+ objectTypeId: objectTypeStr,
1087
+ fieldType: type,
1088
+ fieldData: response,
1089
+ success: true
1090
+ };
1091
+ }
1092
+ };
1093
+
1094
+ // src/api/files.ts
1095
+ var FilesAPI = class {
1096
+ constructor(client) {
1097
+ this.client = client;
1098
+ }
1099
+ /**
1100
+ * Uploads a file attachment to a Fireberry record
1101
+ *
1102
+ * @param objectType - The object type ID
1103
+ * @param recordId - The record ID to attach the file to
1104
+ * @param options - File upload options
1105
+ * @param signal - Optional AbortSignal for cancellation
1106
+ * @returns Upload result
1107
+ *
1108
+ * @example
1109
+ * ```typescript
1110
+ * import { readFileSync } from 'fs';
1111
+ *
1112
+ * const fileBuffer = readFileSync('document.pdf');
1113
+ * const result = await client.files.upload('1', 'abc123', {
1114
+ * buffer: fileBuffer,
1115
+ * filename: 'document.pdf',
1116
+ * mimeType: 'application/pdf',
1117
+ * });
1118
+ * ```
1119
+ */
1120
+ async upload(objectType, recordId, options, signal) {
1121
+ const objectTypeStr = String(objectType);
1122
+ const { buffer, filename, mimeType } = options;
1123
+ const config = this.client.getConfig();
1124
+ const url = `${config.baseUrl}/api/v2/record/${objectTypeStr}/${recordId}/files`;
1125
+ const boundary = `----FormBoundary${Date.now()}`;
1126
+ const formParts = [];
1127
+ const fileHeader = [
1128
+ `--${boundary}`,
1129
+ `Content-Disposition: form-data; name="file"; filename="${filename}"`,
1130
+ `Content-Type: ${mimeType}`,
1131
+ "",
1132
+ ""
1133
+ ].join("\r\n");
1134
+ formParts.push(Buffer.from(fileHeader));
1135
+ formParts.push(buffer);
1136
+ formParts.push(Buffer.from(`\r
1137
+ --${boundary}--\r
1138
+ `));
1139
+ const body = Buffer.concat(formParts);
1140
+ try {
1141
+ const response = await fetch(url, {
1142
+ method: "POST",
1143
+ headers: {
1144
+ Accept: "application/json",
1145
+ "Content-Type": `multipart/form-data; boundary=${boundary}`,
1146
+ tokenid: config.apiKey
1147
+ },
1148
+ body,
1149
+ signal
1150
+ });
1151
+ if (!response.ok) {
1152
+ throw new FireberryError(`File upload failed: ${response.statusText}`, {
1153
+ code: "INVALID_REQUEST" /* INVALID_REQUEST */,
1154
+ statusCode: response.status
1155
+ });
1156
+ }
1157
+ const responseData = await response.json();
1158
+ return {
1159
+ success: true,
1160
+ objectType: objectTypeStr,
1161
+ recordId,
1162
+ fileName: filename,
1163
+ mimeType,
1164
+ fileSize: buffer.length,
1165
+ response: responseData
1166
+ };
1167
+ } catch (error) {
1168
+ if (error instanceof FireberryError) {
1169
+ throw error;
1170
+ }
1171
+ throw new FireberryError(`File upload failed: ${error.message}`, {
1172
+ code: "NETWORK_ERROR" /* NETWORK_ERROR */,
1173
+ cause: error
1174
+ });
1175
+ }
1176
+ }
1177
+ };
1178
+
1179
+ // src/client.ts
1180
+ var FireberryClient = class {
1181
+ config;
1182
+ cacheStore;
1183
+ /** Metadata API operations */
1184
+ metadata;
1185
+ /** Records CRUD operations */
1186
+ records;
1187
+ /** Batch operations */
1188
+ batch;
1189
+ /** Field management operations */
1190
+ fields;
1191
+ /** File operations */
1192
+ files;
1193
+ /**
1194
+ * Creates a new FireberryClient instance
1195
+ */
1196
+ constructor(config) {
1197
+ this.config = {
1198
+ apiKey: config.apiKey,
1199
+ baseUrl: config.baseUrl || "https://api.fireberry.com",
1200
+ timeout: config.timeout || 3e4,
1201
+ retryOn429: config.retryOn429 ?? true,
1202
+ maxRetries: config.maxRetries || 120,
1203
+ retryDelay: config.retryDelay || 1e3,
1204
+ cacheMetadata: config.cacheMetadata || false,
1205
+ cacheTTL: config.cacheTTL || 3e5
1206
+ // 5 minutes default
1207
+ };
1208
+ this.cacheStore = {
1209
+ fields: /* @__PURE__ */ new Map(),
1210
+ fieldValues: /* @__PURE__ */ new Map()
1211
+ };
1212
+ this.metadata = new MetadataAPI(this);
1213
+ this.records = new RecordsAPI(this);
1214
+ this.batch = new BatchAPI(this);
1215
+ this.fields = new FieldsAPI(this);
1216
+ this.files = new FilesAPI(this);
1217
+ }
1218
+ /**
1219
+ * Gets the client configuration
1220
+ */
1221
+ getConfig() {
1222
+ return this.config;
1223
+ }
1224
+ /**
1225
+ * Cache control methods
1226
+ */
1227
+ cache = {
1228
+ clear: () => {
1229
+ this.cacheStore.objects = void 0;
1230
+ this.cacheStore.fields.clear();
1231
+ this.cacheStore.fieldValues.clear();
1232
+ },
1233
+ clearObjects: () => {
1234
+ this.cacheStore.objects = void 0;
1235
+ },
1236
+ clearFields: (objectType) => {
1237
+ this.cacheStore.fields.delete(objectType);
1238
+ },
1239
+ clearFieldValues: (objectType, fieldName) => {
1240
+ if (fieldName) {
1241
+ this.cacheStore.fieldValues.delete(`${objectType}:${fieldName}`);
1242
+ } else {
1243
+ for (const key of this.cacheStore.fieldValues.keys()) {
1244
+ if (key.startsWith(`${objectType}:`)) {
1245
+ this.cacheStore.fieldValues.delete(key);
1246
+ }
1247
+ }
1248
+ }
1249
+ }
1250
+ };
1251
+ getCached(type, objectType, fieldName) {
1252
+ if (!this.config.cacheMetadata) {
1253
+ return void 0;
1254
+ }
1255
+ const now = Date.now();
1256
+ if (type === "objects") {
1257
+ const cached = this.cacheStore.objects;
1258
+ if (cached && now - cached.timestamp < this.config.cacheTTL) {
1259
+ return cached.data;
1260
+ }
1261
+ } else if (type === "fields" && objectType) {
1262
+ const cached = this.cacheStore.fields.get(objectType);
1263
+ if (cached && now - cached.timestamp < this.config.cacheTTL) {
1264
+ return cached.data;
1265
+ }
1266
+ } else if (type === "fieldValues" && objectType && fieldName) {
1267
+ const key = `${objectType}:${fieldName}`;
1268
+ const cached = this.cacheStore.fieldValues.get(key);
1269
+ if (cached && now - cached.timestamp < this.config.cacheTTL) {
1270
+ return cached.data;
1271
+ }
1272
+ }
1273
+ return void 0;
1274
+ }
1275
+ setCache(type, objectTypeOrData, fieldNameOrData, data) {
1276
+ if (!this.config.cacheMetadata) {
1277
+ return;
1278
+ }
1279
+ const now = Date.now();
1280
+ if (type === "objects") {
1281
+ this.cacheStore.objects = { data: objectTypeOrData, timestamp: now };
1282
+ } else if (type === "fields" && typeof objectTypeOrData === "string") {
1283
+ this.cacheStore.fields.set(objectTypeOrData, {
1284
+ data: fieldNameOrData,
1285
+ timestamp: now
1286
+ });
1287
+ } else if (type === "fieldValues" && typeof objectTypeOrData === "string" && typeof fieldNameOrData === "string") {
1288
+ const key = `${objectTypeOrData}:${fieldNameOrData}`;
1289
+ this.cacheStore.fieldValues.set(key, { data, timestamp: now });
1290
+ }
1291
+ }
1292
+ /**
1293
+ * Creates a new QueryBuilder instance
1294
+ */
1295
+ queryBuilder() {
1296
+ return new QueryBuilder(this);
1297
+ }
1298
+ /**
1299
+ * Executes a query against the Fireberry API
1300
+ */
1301
+ async query(options) {
1302
+ const {
1303
+ objectType,
1304
+ fields,
1305
+ query,
1306
+ sortBy = "modifiedon",
1307
+ sortType = "desc",
1308
+ limit,
1309
+ page = 1,
1310
+ pageSize = 500,
1311
+ showRealValue = true,
1312
+ autoPage = true,
1313
+ signal
1314
+ } = options;
1315
+ let fieldsStr;
1316
+ if (Array.isArray(fields)) {
1317
+ fieldsStr = fields.join(",");
1318
+ } else if (typeof fields === "string") {
1319
+ fieldsStr = fields;
1320
+ } else {
1321
+ fieldsStr = "*";
1322
+ }
1323
+ if (fieldsStr === "*") {
1324
+ fieldsStr = await this.expandStarFields(objectType, signal);
1325
+ }
1326
+ if (autoPage) {
1327
+ return this.queryAllPages({
1328
+ objectType,
1329
+ fields: fieldsStr,
1330
+ query,
1331
+ sortBy,
1332
+ sortType,
1333
+ showRealValue,
1334
+ limit,
1335
+ signal
1336
+ });
1337
+ }
1338
+ const body = {
1339
+ objecttype: objectType,
1340
+ fields: fieldsStr,
1341
+ query: query || "",
1342
+ sort_by: sortBy,
1343
+ sort_type: sortType,
1344
+ page_size: Math.min(pageSize, limit || 500),
1345
+ page_number: page,
1346
+ show_real_value: showRealValue ? 1 : 0
1347
+ };
1348
+ const response = await this.request({
1349
+ method: "POST",
1350
+ endpoint: "/api/query",
1351
+ body,
1352
+ signal
1353
+ });
1354
+ const records = response.data?.Data || [];
1355
+ return {
1356
+ records,
1357
+ total: records.length,
1358
+ success: true
1359
+ };
1360
+ }
1361
+ /**
1362
+ * Fetches all pages of a query
1363
+ */
1364
+ async queryAllPages(options) {
1365
+ const { objectType, fields, query, sortBy, sortType, showRealValue, limit, signal } = options;
1366
+ const maxPageSize = 500;
1367
+ const allRecords = [];
1368
+ let currentPage = 1;
1369
+ let hasMore = true;
1370
+ while (hasMore) {
1371
+ if (signal?.aborted) {
1372
+ break;
1373
+ }
1374
+ const body = {
1375
+ objecttype: objectType,
1376
+ fields,
1377
+ query: query || "",
1378
+ sort_by: sortBy,
1379
+ sort_type: sortType,
1380
+ page_size: maxPageSize,
1381
+ page_number: currentPage,
1382
+ show_real_value: showRealValue ? 1 : 0
1383
+ };
1384
+ const response = await this.request({
1385
+ method: "POST",
1386
+ endpoint: "/api/query",
1387
+ body,
1388
+ signal
1389
+ });
1390
+ const pageData = response.data?.Data || [];
1391
+ allRecords.push(...pageData);
1392
+ if (limit && allRecords.length >= limit) {
1393
+ allRecords.splice(limit);
1394
+ break;
1395
+ }
1396
+ if (pageData.length < maxPageSize) {
1397
+ hasMore = false;
1398
+ } else {
1399
+ currentPage++;
1400
+ }
1401
+ }
1402
+ return {
1403
+ records: allRecords,
1404
+ total: allRecords.length,
1405
+ success: true
1406
+ };
1407
+ }
1408
+ /**
1409
+ * Expands '*' fields to actual field names, excluding problematic fields for specific object types
1410
+ */
1411
+ async expandStarFields(objectType, signal) {
1412
+ const { getExcludedFieldsForStarQuery: getExcludedFieldsForStarQuery2 } = await Promise.resolve().then(() => (init_excludedFields(), excludedFields_exports));
1413
+ const excludedFields = getExcludedFieldsForStarQuery2(objectType);
1414
+ if (excludedFields.length === 0) {
1415
+ return "*";
1416
+ }
1417
+ const fieldsResult = await this.metadata.getFields(objectType, signal);
1418
+ const allFieldNames = fieldsResult.fields.map((f) => f.fieldName);
1419
+ const filteredFields = allFieldNames.filter(
1420
+ (fieldName) => !excludedFields.includes(fieldName)
1421
+ );
1422
+ return filteredFields.join(",");
1423
+ }
1424
+ /**
1425
+ * Makes a raw API request to the Fireberry API
1426
+ */
1427
+ async request(options) {
1428
+ const {
1429
+ method,
1430
+ endpoint,
1431
+ query: queryParams,
1432
+ body,
1433
+ headers: customHeaders,
1434
+ signal
1435
+ } = options;
1436
+ let url = `${this.config.baseUrl}${endpoint}`;
1437
+ if (queryParams && Object.keys(queryParams).length > 0) {
1438
+ const params = new URLSearchParams();
1439
+ for (const [key, value] of Object.entries(queryParams)) {
1440
+ if (value !== void 0 && value !== null) {
1441
+ params.set(key, String(value));
1442
+ }
1443
+ }
1444
+ url += `?${params.toString()}`;
1445
+ }
1446
+ const headers = {
1447
+ Accept: "application/json",
1448
+ tokenid: this.config.apiKey,
1449
+ ...customHeaders
1450
+ };
1451
+ if (body) {
1452
+ headers["Content-Type"] = "application/json";
1453
+ }
1454
+ const fetchOptions = {
1455
+ method,
1456
+ headers,
1457
+ signal
1458
+ };
1459
+ if (body) {
1460
+ fetchOptions.body = JSON.stringify(body);
1461
+ }
1462
+ return this.executeWithRetry(url, fetchOptions);
1463
+ }
1464
+ /**
1465
+ * Executes a fetch request with retry logic for 429 errors
1466
+ */
1467
+ async executeWithRetry(url, options, retryCount = 0) {
1468
+ try {
1469
+ const timeoutController = new AbortController();
1470
+ const timeoutId = setTimeout(() => {
1471
+ timeoutController.abort();
1472
+ }, this.config.timeout);
1473
+ const combinedSignal = options.signal ? this.combineSignals([options.signal, timeoutController.signal]) : timeoutController.signal;
1474
+ const response = await fetch(url, {
1475
+ ...options,
1476
+ signal: combinedSignal
1477
+ });
1478
+ clearTimeout(timeoutId);
1479
+ if (response.status === 429 && this.config.retryOn429) {
1480
+ if (retryCount < this.config.maxRetries) {
1481
+ await wait(this.config.retryDelay);
1482
+ return this.executeWithRetry(url, options, retryCount + 1);
1483
+ }
1484
+ throw new FireberryError("Rate limit exceeded after max retries", {
1485
+ code: "RATE_LIMITED" /* RATE_LIMITED */,
1486
+ statusCode: 429,
1487
+ context: { retryCount }
1488
+ });
1489
+ }
1490
+ let body;
1491
+ const contentType = response.headers.get("content-type");
1492
+ if (contentType?.includes("application/json")) {
1493
+ body = await response.json();
1494
+ } else {
1495
+ body = await response.text();
1496
+ }
1497
+ if (!response.ok) {
1498
+ throw createErrorFromResponse(response, body);
1499
+ }
1500
+ return body;
1501
+ } catch (error) {
1502
+ if (error instanceof Error && error.name === "AbortError") {
1503
+ throw createNetworkError(error);
1504
+ }
1505
+ if (error instanceof FireberryError) {
1506
+ throw error;
1507
+ }
1508
+ throw createNetworkError(error);
1509
+ }
1510
+ }
1511
+ /**
1512
+ * Combines multiple abort signals into one
1513
+ */
1514
+ combineSignals(signals) {
1515
+ const controller = new AbortController();
1516
+ for (const signal of signals) {
1517
+ if (signal.aborted) {
1518
+ controller.abort();
1519
+ break;
1520
+ }
1521
+ signal.addEventListener("abort", () => controller.abort(), { once: true });
1522
+ }
1523
+ return controller.signal;
1524
+ }
1525
+ };
1526
+
1527
+ // src/constants/objectNames.ts
1528
+ var OBJECT_NAME_MAP = {
1529
+ 1: "accountname",
1530
+ // Account
1531
+ 2: "fullname",
1532
+ // Contact
1533
+ 3: "fullname",
1534
+ // Lead
1535
+ 4: "name",
1536
+ // Opportunity
1537
+ 5: "title",
1538
+ // Case
1539
+ 6: "subject",
1540
+ // Activity
1541
+ 7: "subject",
1542
+ // Note
1543
+ 8: "name",
1544
+ // Competitor
1545
+ 9: "fullname",
1546
+ // CRM User
1547
+ 10: "subject",
1548
+ // Task
1549
+ 13: "name",
1550
+ // CRM Order
1551
+ 14: "productname",
1552
+ // Product
1553
+ 17: "productname",
1554
+ // CRM Order Item
1555
+ 20: "name",
1556
+ // Email Template
1557
+ 23: "name",
1558
+ // Business Unit
1559
+ 27: "name",
1560
+ // Print Template
1561
+ 28: "name",
1562
+ // Contract
1563
+ 33: "productname",
1564
+ // Account Product
1565
+ 46: "name",
1566
+ // Project
1567
+ 67: "name",
1568
+ // Campaign
1569
+ 76: "title",
1570
+ // Article
1571
+ 86: "name",
1572
+ // Invoice
1573
+ 101: "name",
1574
+ // Attendance Clock
1575
+ 102: "subject",
1576
+ // Activity Log
1577
+ 104: "subject",
1578
+ // Conversation
1579
+ 114: "name"
1580
+ // Calendar Resource
1581
+ };
1582
+
1583
+ // src/constants/index.ts
1584
+ init_excludedFields();
1585
+
1586
+ exports.FIELD_TYPE_IDS = FIELD_TYPE_IDS;
1587
+ exports.FIELD_TYPE_MAPPINGS = FIELD_TYPE_MAPPINGS;
1588
+ exports.FireberryClient = FireberryClient;
1589
+ exports.FireberryError = FireberryError;
1590
+ exports.FireberryErrorCode = FireberryErrorCode;
1591
+ exports.OBJECT_ID_MAP = OBJECT_ID_MAP;
1592
+ exports.OBJECT_NAME_MAP = OBJECT_NAME_MAP;
1593
+ exports.QueryBuilder = QueryBuilder;
1594
+ exports.escapeQueryValue = escapeQueryValue;
1595
+ exports.sanitizeQuery = sanitizeQuery;
1596
+ //# sourceMappingURL=index.cjs.map
1597
+ //# sourceMappingURL=index.cjs.map