quickbase-js 2.3.0 → 2.4.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.
Files changed (62) hide show
  1. package/README.md +145 -1107
  2. package/dist/auth/ticket.d.ts.map +1 -1
  3. package/dist/auth/ticket.js +4 -3
  4. package/dist/auth/ticket.js.map +1 -1
  5. package/dist/client/request.d.ts +4 -0
  6. package/dist/client/request.d.ts.map +1 -1
  7. package/dist/client/request.js +15 -3
  8. package/dist/client/request.js.map +1 -1
  9. package/dist/generated/client.d.ts +2 -0
  10. package/dist/generated/client.d.ts.map +1 -1
  11. package/dist/generated/client.js +134 -113
  12. package/dist/generated/client.js.map +1 -1
  13. package/dist/generated/types.d.ts +2700 -2256
  14. package/dist/generated/types.d.ts.map +1 -1
  15. package/dist/generated/types.js +0 -2
  16. package/dist/generated/types.js.map +1 -1
  17. package/dist/quickbase.esm.js +3 -3
  18. package/dist/quickbase.esm.js.map +3 -3
  19. package/dist/quickbase.min.js +3 -3
  20. package/dist/quickbase.min.js.map +3 -3
  21. package/package.json +16 -13
  22. package/src/auth/index.ts +55 -0
  23. package/src/auth/sso-token.ts +143 -0
  24. package/src/auth/temp-token.ts +160 -0
  25. package/src/auth/ticket.ts +329 -0
  26. package/src/auth/token-cache.ts +97 -0
  27. package/src/auth/types.ts +50 -0
  28. package/src/auth/user-token.ts +38 -0
  29. package/src/cli/schema.ts +512 -0
  30. package/src/client/index.ts +36 -0
  31. package/src/client/pagination.ts +337 -0
  32. package/src/client/request.ts +544 -0
  33. package/src/client/retry.ts +169 -0
  34. package/src/client/throttle.ts +105 -0
  35. package/src/core/config.ts +160 -0
  36. package/src/core/dates.ts +63 -0
  37. package/src/core/errors.ts +349 -0
  38. package/src/core/index.ts +50 -0
  39. package/src/core/logger.ts +113 -0
  40. package/src/core/schema.ts +213 -0
  41. package/src/core/transform.ts +295 -0
  42. package/src/core/types.ts +337 -0
  43. package/src/generated/client.ts +1785 -0
  44. package/src/generated/operations.json +672 -0
  45. package/src/generated/types.ts +4884 -0
  46. package/src/index.ts +259 -0
  47. package/src/xml/codepages.ts +119 -0
  48. package/src/xml/dbvars.ts +75 -0
  49. package/src/xml/errors.ts +133 -0
  50. package/src/xml/fields.ts +121 -0
  51. package/src/xml/groups.ts +560 -0
  52. package/src/xml/html.ts +152 -0
  53. package/src/xml/index.ts +1196 -0
  54. package/src/xml/metadata.ts +111 -0
  55. package/src/xml/records.ts +301 -0
  56. package/src/xml/request.ts +238 -0
  57. package/src/xml/roles.ts +321 -0
  58. package/src/xml/schema.ts +436 -0
  59. package/src/xml/transform.ts +381 -0
  60. package/src/xml/types.ts +602 -0
  61. package/src/xml/users.ts +221 -0
  62. package/src/xml/webhooks.ts +260 -0
@@ -0,0 +1,436 @@
1
+ /**
2
+ * XML API Schema and App Discovery
3
+ *
4
+ * Endpoints for discovering apps/tables and getting schema information.
5
+ *
6
+ * XML-API: Remove this file when QuickBase discontinues the XML API.
7
+ * To find all XML API code, search for: XML-API
8
+ */
9
+
10
+ import type {
11
+ XmlCaller,
12
+ DatabaseInfo,
13
+ DBInfo,
14
+ GetSchemaResult,
15
+ GrantedDBsResult,
16
+ FindDBByNameResult,
17
+ SchemaTable,
18
+ SchemaOriginal,
19
+ SchemaField,
20
+ SchemaQuery,
21
+ SchemaVariable,
22
+ SchemaChildTable,
23
+ } from './types.js';
24
+ import { checkError } from './errors.js';
25
+ import {
26
+ buildRequest,
27
+ escapeXml,
28
+ parseBaseResponse,
29
+ getTagContent,
30
+ getAttribute,
31
+ getAllElements,
32
+ parseXmlNumber,
33
+ parseXmlBool,
34
+ } from './request.js';
35
+
36
+ /**
37
+ * List all apps/tables the user can access.
38
+ *
39
+ * Returns apps across all domains by default. Use realmAppsOnly to limit
40
+ * to the current realm.
41
+ *
42
+ * @see https://help.quickbase.com/docs/api-granteddbs
43
+ *
44
+ * @example
45
+ * ```typescript
46
+ * const result = await grantedDBs(caller, { parentsOnly: true });
47
+ * for (const db of result.databases) {
48
+ * console.log(`${db.name} (${db.dbid})`);
49
+ * }
50
+ * ```
51
+ */
52
+ export async function grantedDBs(
53
+ caller: XmlCaller,
54
+ options?: {
55
+ parentsOnly?: boolean;
56
+ excludeParents?: boolean;
57
+ adminOnly?: boolean;
58
+ includeAncestors?: boolean;
59
+ withEmbeddedTables?: boolean;
60
+ realmAppsOnly?: boolean;
61
+ }
62
+ ): Promise<GrantedDBsResult> {
63
+ let inner = '';
64
+ if (options?.excludeParents) {
65
+ inner += '<excludeparents>1</excludeparents>';
66
+ }
67
+ if (options?.parentsOnly) {
68
+ inner += '<Withembeddedtables>0</Withembeddedtables>';
69
+ }
70
+ if (options?.adminOnly) {
71
+ inner += '<adminOnly>true</adminOnly>';
72
+ }
73
+ if (options?.includeAncestors) {
74
+ inner += '<includeancestors>1</includeancestors>';
75
+ }
76
+ if (options?.withEmbeddedTables !== undefined) {
77
+ inner += `<withembeddedtables>${options.withEmbeddedTables ? '1' : '0'}</withembeddedtables>`;
78
+ }
79
+ if (options?.realmAppsOnly) {
80
+ inner += '<realmAppsOnly>true</realmAppsOnly>';
81
+ }
82
+
83
+ const body = buildRequest(inner);
84
+ const xml = await caller.doXml('main', 'API_GrantedDBs', body);
85
+
86
+ const base = parseBaseResponse(xml);
87
+ checkError(base);
88
+
89
+ // Parse databases
90
+ const dbElements = getAllElements(xml, 'dbinfo');
91
+ const databases: DatabaseInfo[] = dbElements.map((dbXml) => {
92
+ const info: DatabaseInfo = {
93
+ dbid: getTagContent(dbXml, 'dbid'),
94
+ name: getTagContent(dbXml, 'dbname'),
95
+ };
96
+
97
+ const ancestorAppId = getTagContent(dbXml, 'ancestorappid');
98
+ if (ancestorAppId) {
99
+ info.ancestorAppId = ancestorAppId;
100
+ }
101
+
102
+ const oldestAncestorAppId = getTagContent(dbXml, 'oldestancestorappid');
103
+ if (oldestAncestorAppId) {
104
+ info.oldestAncestorAppId = oldestAncestorAppId;
105
+ }
106
+
107
+ return info;
108
+ });
109
+
110
+ return { databases };
111
+ }
112
+
113
+ /**
114
+ * Find an app by name.
115
+ *
116
+ * Searches only apps you have access to. May return multiple results
117
+ * if multiple apps share the same name.
118
+ *
119
+ * @see https://help.quickbase.com/docs/api-finddbbyname
120
+ *
121
+ * @example
122
+ * ```typescript
123
+ * const result = await findDBByName(caller, 'My App', true);
124
+ * console.log(`Found app: ${result.dbid}`);
125
+ * ```
126
+ */
127
+ export async function findDBByName(
128
+ caller: XmlCaller,
129
+ name: string,
130
+ parentsOnly?: boolean
131
+ ): Promise<FindDBByNameResult> {
132
+ let inner = `<dbname>${escapeXml(name)}</dbname>`;
133
+ if (parentsOnly) {
134
+ inner += '<ParentsOnly>1</ParentsOnly>';
135
+ }
136
+
137
+ const body = buildRequest(inner);
138
+ const xml = await caller.doXml('main', 'API_FindDBByName', body);
139
+
140
+ const base = parseBaseResponse(xml);
141
+ checkError(base);
142
+
143
+ const dbid = getTagContent(xml, 'dbid');
144
+ if (!dbid) {
145
+ throw new Error('Invalid API_FindDBByName response: no dbid element');
146
+ }
147
+
148
+ return { dbid };
149
+ }
150
+
151
+ /**
152
+ * Get app/table metadata.
153
+ *
154
+ * Returns information like last modified time, record count, manager info.
155
+ *
156
+ * @see https://help.quickbase.com/docs/api-getdbinfo
157
+ *
158
+ * @example
159
+ * ```typescript
160
+ * const info = await getDBInfo(caller, 'bqxyz123');
161
+ * console.log(`${info.name}: ${info.numRecords} records`);
162
+ * console.log(`Manager: ${info.managerName}`);
163
+ * ```
164
+ */
165
+ export async function getDBInfo(
166
+ caller: XmlCaller,
167
+ dbid: string
168
+ ): Promise<DBInfo> {
169
+ const body = buildRequest('');
170
+ const xml = await caller.doXml(dbid, 'API_GetDBInfo', body);
171
+
172
+ const base = parseBaseResponse(xml);
173
+ checkError(base);
174
+
175
+ return {
176
+ name: getTagContent(xml, 'dbname'),
177
+ lastRecModTime: getTagContent(xml, 'lastRecModTime') || undefined,
178
+ lastModifiedTime: getTagContent(xml, 'lastModifiedTime') || undefined,
179
+ createdTime: getTagContent(xml, 'createdTime') || undefined,
180
+ numRecords: parseXmlNumber(getTagContent(xml, 'numRecords')),
181
+ managerId: getTagContent(xml, 'mgrID') || undefined,
182
+ managerName: getTagContent(xml, 'mgrName') || undefined,
183
+ version: getTagContent(xml, 'version') || undefined,
184
+ timeZone: getTagContent(xml, 'time_zone') || undefined,
185
+ };
186
+ }
187
+
188
+ /**
189
+ * Get total record count for a table.
190
+ *
191
+ * @see https://help.quickbase.com/docs/api-getnumrecords
192
+ *
193
+ * @example
194
+ * ```typescript
195
+ * const count = await getNumRecords(caller, 'bqtable123');
196
+ * console.log(`Table has ${count} records`);
197
+ * ```
198
+ */
199
+ export async function getNumRecords(
200
+ caller: XmlCaller,
201
+ tableId: string
202
+ ): Promise<number> {
203
+ const body = buildRequest('');
204
+ const xml = await caller.doXml(tableId, 'API_GetNumRecords', body);
205
+
206
+ const base = parseBaseResponse(xml);
207
+ checkError(base);
208
+
209
+ return parseXmlNumber(getTagContent(xml, 'num_records'));
210
+ }
211
+
212
+ /**
213
+ * Parse a field element from GetSchema response
214
+ */
215
+ function parseSchemaField(fieldXml: string): SchemaField {
216
+ const field: SchemaField = {
217
+ id: parseXmlNumber(getAttribute(fieldXml, 'id')),
218
+ fieldType: getAttribute(fieldXml, 'field_type'),
219
+ baseType: getAttribute(fieldXml, 'base_type'),
220
+ label: getTagContent(fieldXml, 'label'),
221
+ };
222
+
223
+ const mode = getAttribute(fieldXml, 'mode');
224
+ if (mode) field.mode = mode;
225
+
226
+ const nowrap = getTagContent(fieldXml, 'nowrap');
227
+ if (nowrap) field.nowrap = parseXmlBool(nowrap);
228
+
229
+ const bold = getTagContent(fieldXml, 'bold');
230
+ if (bold) field.bold = parseXmlBool(bold);
231
+
232
+ const required = getTagContent(fieldXml, 'required');
233
+ if (required) field.required = parseXmlBool(required);
234
+
235
+ const unique = getTagContent(fieldXml, 'unique');
236
+ if (unique) field.unique = parseXmlBool(unique);
237
+
238
+ const fieldHelp = getTagContent(fieldXml, 'fieldhelp');
239
+ if (fieldHelp) field.fieldHelp = fieldHelp;
240
+
241
+ const appearsByDefault = getTagContent(fieldXml, 'appears_by_default');
242
+ if (appearsByDefault) field.appearsByDefault = parseXmlBool(appearsByDefault);
243
+
244
+ const findEnabled = getTagContent(fieldXml, 'find_enabled');
245
+ if (findEnabled) field.findEnabled = parseXmlBool(findEnabled);
246
+
247
+ const allowNewChoices = getTagContent(fieldXml, 'allow_new_choices');
248
+ if (allowNewChoices) field.allowNewChoices = parseXmlBool(allowNewChoices);
249
+
250
+ const sortAsGiven = getTagContent(fieldXml, 'sort_as_given');
251
+ if (sortAsGiven) field.sortAsGiven = parseXmlBool(sortAsGiven);
252
+
253
+ const carryChoices = getTagContent(fieldXml, 'carrychoices');
254
+ if (carryChoices) field.carryChoices = parseXmlBool(carryChoices);
255
+
256
+ const foreignKey = getTagContent(fieldXml, 'foreignkey');
257
+ if (foreignKey) field.foreignKey = parseXmlBool(foreignKey);
258
+
259
+ const doesTotal = getTagContent(fieldXml, 'does_total');
260
+ if (doesTotal) field.doesTotal = parseXmlBool(doesTotal);
261
+
262
+ const doesAverage = getTagContent(fieldXml, 'does_average');
263
+ if (doesAverage) field.doesAverage = parseXmlBool(doesAverage);
264
+
265
+ const defaultKind = getTagContent(fieldXml, 'default_kind');
266
+ if (defaultKind) field.defaultKind = defaultKind;
267
+
268
+ const defaultValue = getTagContent(fieldXml, 'default_value');
269
+ if (defaultValue) field.defaultValue = defaultValue;
270
+
271
+ // Parse choices if present
272
+ const choicesMatch = fieldXml.match(/<choices>([\s\S]*?)<\/choices>/);
273
+ if (choicesMatch && choicesMatch[1]) {
274
+ const choiceElements = getAllElements(choicesMatch[1], 'choice');
275
+ field.choices = choiceElements.map((c) => getTagContent(c, 'choice') || c.replace(/<\/?choice>/g, ''));
276
+ }
277
+
278
+ // Parse summary fields
279
+ const summaryReferenceFid = getTagContent(fieldXml, 'summaryReferenceFid');
280
+ if (summaryReferenceFid) field.summaryReferenceFid = parseXmlNumber(summaryReferenceFid);
281
+
282
+ const summaryTargetFid = getTagContent(fieldXml, 'summaryTargetFid');
283
+ if (summaryTargetFid) field.summaryTargetFid = parseXmlNumber(summaryTargetFid);
284
+
285
+ const summaryFunction = getTagContent(fieldXml, 'summaryFunction');
286
+ if (summaryFunction) field.summaryFunction = summaryFunction;
287
+
288
+ return field;
289
+ }
290
+
291
+ /**
292
+ * Parse a query element from GetSchema response
293
+ */
294
+ function parseSchemaQuery(queryXml: string): SchemaQuery {
295
+ const query: SchemaQuery = {
296
+ id: parseXmlNumber(getAttribute(queryXml, 'id')),
297
+ name: getTagContent(queryXml, 'qyname') || getTagContent(queryXml, 'qname'),
298
+ type: getTagContent(queryXml, 'qytype'),
299
+ };
300
+
301
+ const desc = getTagContent(queryXml, 'qydesc');
302
+ if (desc) query.description = desc;
303
+
304
+ const crit = getTagContent(queryXml, 'qycrit');
305
+ if (crit) query.criteria = crit;
306
+
307
+ const clst = getTagContent(queryXml, 'qyclst');
308
+ if (clst) query.columnList = clst;
309
+
310
+ const slst = getTagContent(queryXml, 'qyslst');
311
+ if (slst) query.sortList = slst;
312
+
313
+ const opts = getTagContent(queryXml, 'qyopts');
314
+ if (opts) query.options = opts;
315
+
316
+ const calst = getTagContent(queryXml, 'qycalst');
317
+ if (calst) query.calcFields = calst;
318
+
319
+ return query;
320
+ }
321
+
322
+ /**
323
+ * Get comprehensive app/table schema information.
324
+ *
325
+ * When called on an app dbid: returns DBVars and child table dbids.
326
+ * When called on a table dbid: returns fields, queries, DBVars, and more.
327
+ *
328
+ * @see https://help.quickbase.com/docs/api-getschema
329
+ *
330
+ * @example
331
+ * ```typescript
332
+ * const schema = await getSchema(caller, 'bqtable123');
333
+ * console.log(`Table: ${schema.table.name}`);
334
+ * for (const field of schema.table.fields ?? []) {
335
+ * console.log(` Field ${field.id}: ${field.label} (${field.fieldType})`);
336
+ * }
337
+ * ```
338
+ */
339
+ export async function getSchema(
340
+ caller: XmlCaller,
341
+ dbid: string
342
+ ): Promise<GetSchemaResult> {
343
+ const body = buildRequest('');
344
+ const xml = await caller.doXml(dbid, 'API_GetSchema', body);
345
+
346
+ const base = parseBaseResponse(xml);
347
+ checkError(base);
348
+
349
+ // Parse top-level elements
350
+ const timeZone = getTagContent(xml, 'time_zone') || undefined;
351
+ const dateFormat = getTagContent(xml, 'date_format') || undefined;
352
+
353
+ // Parse table element
354
+ const tableMatch = xml.match(/<table>([\s\S]*?)<\/table>/);
355
+ if (!tableMatch) {
356
+ throw new Error('Invalid API_GetSchema response: no table element');
357
+ }
358
+ const tableXml = tableMatch[0];
359
+
360
+ const table: SchemaTable = {
361
+ name: getTagContent(tableXml, 'name'),
362
+ description: getTagContent(tableXml, 'desc') || undefined,
363
+ };
364
+
365
+ // Parse original metadata
366
+ const originalMatch = tableXml.match(/<original>([\s\S]*?)<\/original>/);
367
+ if (originalMatch) {
368
+ const origXml = originalMatch[0];
369
+ const original: SchemaOriginal = {};
370
+
371
+ const appId = getTagContent(origXml, 'app_id');
372
+ if (appId) original.appId = appId;
373
+
374
+ const tableId = getTagContent(origXml, 'table_id');
375
+ if (tableId) original.tableId = tableId;
376
+
377
+ const creDate = getTagContent(origXml, 'cre_date');
378
+ if (creDate) original.createdTime = creDate;
379
+
380
+ const modDate = getTagContent(origXml, 'mod_date');
381
+ if (modDate) original.modifiedTime = modDate;
382
+
383
+ const nextRecordId = getTagContent(origXml, 'next_record_id');
384
+ if (nextRecordId) original.nextRecordId = parseXmlNumber(nextRecordId);
385
+
386
+ const nextFieldId = getTagContent(origXml, 'next_field_id');
387
+ if (nextFieldId) original.nextFieldId = parseXmlNumber(nextFieldId);
388
+
389
+ const nextQueryId = getTagContent(origXml, 'next_query_id');
390
+ if (nextQueryId) original.nextQueryId = parseXmlNumber(nextQueryId);
391
+
392
+ const defSortFid = getTagContent(origXml, 'def_sort_fid');
393
+ if (defSortFid) original.defaultSortFid = parseXmlNumber(defSortFid);
394
+
395
+ const defSortOrder = getTagContent(origXml, 'def_sort_order');
396
+ if (defSortOrder) original.defaultSortOrder = parseXmlNumber(defSortOrder);
397
+
398
+ table.original = original;
399
+ }
400
+
401
+ // Parse variables
402
+ const variablesMatch = tableXml.match(/<variables>([\s\S]*?)<\/variables>/);
403
+ if (variablesMatch) {
404
+ const varElements = getAllElements(variablesMatch[0], 'var');
405
+ table.variables = varElements.map((varXml): SchemaVariable => ({
406
+ name: getAttribute(varXml, 'name'),
407
+ value: getTagContent(varXml, 'var') || '',
408
+ }));
409
+ }
410
+
411
+ // Parse child tables (chdbids)
412
+ const chdbidsMatch = tableXml.match(/<chdbids>([\s\S]*?)<\/chdbids>/);
413
+ if (chdbidsMatch) {
414
+ const chdbElements = getAllElements(chdbidsMatch[0], 'chdbid');
415
+ table.childTables = chdbElements.map((chXml): SchemaChildTable => ({
416
+ name: getAttribute(chXml, 'name'),
417
+ dbid: getTagContent(chXml, 'chdbid') || '',
418
+ }));
419
+ }
420
+
421
+ // Parse queries
422
+ const queriesMatch = tableXml.match(/<queries>([\s\S]*?)<\/queries>/);
423
+ if (queriesMatch) {
424
+ const queryElements = getAllElements(queriesMatch[0], 'query');
425
+ table.queries = queryElements.map(parseSchemaQuery);
426
+ }
427
+
428
+ // Parse fields
429
+ const fieldsMatch = tableXml.match(/<fields>([\s\S]*?)<\/fields>/);
430
+ if (fieldsMatch && fieldsMatch[0].length > 20) {
431
+ const fieldElements = getAllElements(fieldsMatch[0], 'field');
432
+ table.fields = fieldElements.map(parseSchemaField);
433
+ }
434
+
435
+ return { table, timeZone, dateFormat };
436
+ }