quickbase-js 2.4.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 (46) hide show
  1. package/README.md +144 -76
  2. package/dist/quickbase.esm.js +3 -3
  3. package/dist/quickbase.min.js +3 -3
  4. package/dist/quickbase.min.js.map +1 -1
  5. package/package.json +13 -9
  6. package/src/auth/index.ts +55 -0
  7. package/src/auth/sso-token.ts +143 -0
  8. package/src/auth/temp-token.ts +160 -0
  9. package/src/auth/ticket.ts +329 -0
  10. package/src/auth/token-cache.ts +97 -0
  11. package/src/auth/types.ts +50 -0
  12. package/src/auth/user-token.ts +38 -0
  13. package/src/cli/schema.ts +512 -0
  14. package/src/client/index.ts +36 -0
  15. package/src/client/pagination.ts +337 -0
  16. package/src/client/request.ts +544 -0
  17. package/src/client/retry.ts +169 -0
  18. package/src/client/throttle.ts +105 -0
  19. package/src/core/config.ts +160 -0
  20. package/src/core/dates.ts +63 -0
  21. package/src/core/errors.ts +349 -0
  22. package/src/core/index.ts +50 -0
  23. package/src/core/logger.ts +113 -0
  24. package/src/core/schema.ts +213 -0
  25. package/src/core/transform.ts +295 -0
  26. package/src/core/types.ts +337 -0
  27. package/src/generated/client.ts +1785 -0
  28. package/src/generated/operations.json +672 -0
  29. package/src/generated/types.ts +4884 -0
  30. package/src/index.ts +259 -0
  31. package/src/xml/codepages.ts +119 -0
  32. package/src/xml/dbvars.ts +75 -0
  33. package/src/xml/errors.ts +133 -0
  34. package/src/xml/fields.ts +121 -0
  35. package/src/xml/groups.ts +560 -0
  36. package/src/xml/html.ts +152 -0
  37. package/src/xml/index.ts +1196 -0
  38. package/src/xml/metadata.ts +111 -0
  39. package/src/xml/records.ts +301 -0
  40. package/src/xml/request.ts +238 -0
  41. package/src/xml/roles.ts +321 -0
  42. package/src/xml/schema.ts +436 -0
  43. package/src/xml/transform.ts +381 -0
  44. package/src/xml/types.ts +602 -0
  45. package/src/xml/users.ts +221 -0
  46. package/src/xml/webhooks.ts +260 -0
package/src/index.ts ADDED
@@ -0,0 +1,259 @@
1
+ /**
2
+ * QuickBase SDK v2
3
+ *
4
+ * A TypeScript/JavaScript client for the QuickBase JSON RESTful API.
5
+ *
6
+ * @example
7
+ * ```typescript
8
+ * import { createClient } from 'quickbase-js';
9
+ *
10
+ * const client = createClient({
11
+ * realm: 'mycompany',
12
+ * auth: {
13
+ * type: 'user-token',
14
+ * userToken: 'your-user-token',
15
+ * },
16
+ * });
17
+ *
18
+ * const app = await client.getApp({ appId: 'bpqe82s1' });
19
+ * console.log(app.name);
20
+ * ```
21
+ */
22
+
23
+ // Core exports
24
+ export type {
25
+ QuickbaseConfig,
26
+ AuthConfig,
27
+ UserTokenAuthConfig,
28
+ TempTokenAuthConfig,
29
+ SsoTokenAuthConfig,
30
+ TicketAuthConfig, // XML-API-TICKET: Remove if XML API discontinued
31
+ RateLimitConfig,
32
+ RetryConfig,
33
+ RateLimitInfo,
34
+ PaginationMetadata,
35
+ PaginatedResponse,
36
+ FieldInfo,
37
+ // Schema types
38
+ Schema,
39
+ TableSchema,
40
+ FieldSchema,
41
+ ResolvedSchema,
42
+ } from './core/index.js';
43
+
44
+ // Schema utilities
45
+ export { SchemaError } from './core/schema.js';
46
+
47
+ export {
48
+ QuickbaseError,
49
+ RateLimitError,
50
+ AuthenticationError,
51
+ AuthorizationError,
52
+ NotFoundError,
53
+ ValidationError,
54
+ TimeoutError,
55
+ ServerError,
56
+ QuickbaseTicketError,
57
+ QuickbaseAppTokenError,
58
+ QuickbaseXmlError,
59
+ ReadOnlyError,
60
+ } from './core/index.js';
61
+
62
+ // Auth exports (for advanced usage)
63
+ export type { AuthStrategy } from './auth/index.js';
64
+ export {
65
+ UserTokenStrategy,
66
+ TempTokenStrategy,
67
+ SsoTokenStrategy,
68
+ TicketStrategy, // XML-API-TICKET: Remove if XML API discontinued
69
+ createAuthStrategy,
70
+ } from './auth/index.js';
71
+ import type { AuthStrategy } from './auth/index.js';
72
+
73
+ // Client exports
74
+ export type { RequestOptions } from './client/index.js';
75
+ export { SlidingWindowThrottle, NoOpThrottle } from './client/index.js';
76
+
77
+ // Generated types and client
78
+ export type { QuickbaseAPI } from './generated/types.js';
79
+ export * from './generated/types.js';
80
+ export { createApiMethods } from './generated/client.js';
81
+
82
+ // Main client factory
83
+ import type { QuickbaseConfig, ResolvedConfig } from './core/types.js';
84
+ import { resolveConfig } from './core/config.js';
85
+ import { createLogger } from './core/logger.js';
86
+ import { createAuthStrategy } from './auth/index.js';
87
+ import { createThrottle } from './client/throttle.js';
88
+ import { createRequestExecutor } from './client/request.js';
89
+ import type { QuickbaseAPI } from './generated/types.js';
90
+ import { createApiMethods } from './generated/client.js';
91
+
92
+ /**
93
+ * QuickBase client with typed API methods
94
+ */
95
+ export interface QuickbaseClient extends QuickbaseAPI {
96
+ /** Execute a raw request (for custom API calls) */
97
+ request: <T = unknown>(options: {
98
+ method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
99
+ path: string;
100
+ body?: unknown;
101
+ query?: Record<string, string | number | boolean | undefined>;
102
+ dbid?: string;
103
+ }) => Promise<T>;
104
+
105
+ /** Get the resolved configuration */
106
+ getConfig: () => ResolvedConfig;
107
+
108
+ /**
109
+ * Get the auth strategy for advanced usage (e.g., XML API client).
110
+ * XML-API: Used by createXmlClient() to access authentication.
111
+ */
112
+ getAuthStrategy: () => AuthStrategy;
113
+ }
114
+
115
+ /**
116
+ * Create a QuickBase client
117
+ *
118
+ * @example User token authentication
119
+ * ```typescript
120
+ * const client = createClient({
121
+ * realm: 'mycompany',
122
+ * auth: {
123
+ * type: 'user-token',
124
+ * userToken: 'your-user-token',
125
+ * },
126
+ * });
127
+ * ```
128
+ *
129
+ * @example Temporary token authentication
130
+ * ```typescript
131
+ * const client = createClient({
132
+ * realm: 'mycompany',
133
+ * auth: {
134
+ * type: 'temp-token',
135
+ * userToken: 'your-user-token',
136
+ * appToken: 'optional-app-token',
137
+ * },
138
+ * });
139
+ * ```
140
+ *
141
+ * @example With rate limiting callback
142
+ * ```typescript
143
+ * const client = createClient({
144
+ * realm: 'mycompany',
145
+ * auth: { type: 'user-token', userToken: 'token' },
146
+ * rateLimit: {
147
+ * onRateLimit: (info) => {
148
+ * console.log(`Rate limited! Retry after ${info.retryAfter}s`);
149
+ * },
150
+ * },
151
+ * });
152
+ * ```
153
+ */
154
+ export function createClient(config: QuickbaseConfig): QuickbaseClient {
155
+ const resolvedConfig = resolveConfig(config);
156
+ const logger = createLogger(resolvedConfig.debug);
157
+
158
+ const authContext = {
159
+ realm: resolvedConfig.realm,
160
+ baseUrl: resolvedConfig.baseUrl,
161
+ fetchApi: resolvedConfig.fetchApi,
162
+ logger,
163
+ };
164
+
165
+ const auth = createAuthStrategy(resolvedConfig.auth, authContext);
166
+ const throttle = createThrottle(resolvedConfig.proactiveThrottle);
167
+
168
+ const executor = createRequestExecutor({
169
+ config: resolvedConfig,
170
+ auth,
171
+ throttle,
172
+ logger,
173
+ });
174
+
175
+ // Create typed API methods
176
+ const apiMethods = createApiMethods(executor.execute, resolvedConfig.autoPaginate);
177
+
178
+ return {
179
+ ...apiMethods,
180
+ request: executor.execute,
181
+ getConfig: () => resolvedConfig,
182
+ // XML-API: Expose auth strategy for XML client
183
+ getAuthStrategy: () => auth,
184
+ };
185
+ }
186
+
187
+ // XML-API: XML API exports - remove when QuickBase discontinues XML API
188
+ export {
189
+ createXmlClient,
190
+ XmlClient,
191
+ XmlError,
192
+ XmlErrorCode,
193
+ checkError,
194
+ isUnauthorized as isXmlUnauthorized,
195
+ isNotFound as isXmlNotFound,
196
+ isInvalidTicket as isXmlInvalidTicket,
197
+ isXmlWriteAction,
198
+ } from './xml/index.js';
199
+ export type {
200
+ XmlCaller,
201
+ XmlClientOptions,
202
+ XmlExecutor,
203
+ // Role types
204
+ Role,
205
+ RoleAccess,
206
+ UserRole,
207
+ RoleMember,
208
+ UserWithRoles,
209
+ // Group types
210
+ Group,
211
+ GroupUser,
212
+ GroupManager,
213
+ Subgroup,
214
+ // User types
215
+ UserInfo,
216
+ // Schema types (XML)
217
+ SchemaField,
218
+ SchemaQuery,
219
+ SchemaVariable,
220
+ SchemaChildTable,
221
+ SchemaOriginal,
222
+ SchemaTable,
223
+ // App discovery types
224
+ DatabaseInfo,
225
+ DBInfo,
226
+ // Record types (XML)
227
+ RecordField,
228
+ // Code page types
229
+ PageType,
230
+ // Webhook types
231
+ WebhookTrigger,
232
+ WebhookOptions,
233
+ // Result types
234
+ GetRoleInfoResult,
235
+ UserRolesResult,
236
+ GetUserRoleResult,
237
+ GetSchemaResult,
238
+ GrantedDBsResult,
239
+ FindDBByNameResult,
240
+ GetUsersInGroupResult,
241
+ CreateGroupResult,
242
+ GetGroupRoleResult,
243
+ ProvisionUserResult,
244
+ AddReplaceDBPageResult,
245
+ FieldAddChoicesResult,
246
+ FieldRemoveChoicesResult,
247
+ DoQueryCountResult,
248
+ GetRecordInfoResult,
249
+ GrantedGroupsResult,
250
+ GrantedDBsForGroupResult,
251
+ GetAppDTMInfoResult,
252
+ GetAncestorInfoResult,
253
+ ImportFromCSVResult,
254
+ CopyMasterDetailResult,
255
+ WebhookResult,
256
+ } from './xml/index.js';
257
+
258
+ // Default export
259
+ export default createClient;
@@ -0,0 +1,119 @@
1
+ /**
2
+ * XML API Code Pages
3
+ *
4
+ * Endpoints for managing stored code pages (HTML, XSL, etc.).
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
+ PageType,
13
+ AddReplaceDBPageResult,
14
+ } from './types.js';
15
+ import { checkError } from './errors.js';
16
+ import {
17
+ buildRequest,
18
+ escapeXml,
19
+ parseBaseResponse,
20
+ getTagContent,
21
+ xmlElementCDATA,
22
+ parseXmlNumber,
23
+ } from './request.js';
24
+
25
+ /**
26
+ * Get stored code page content.
27
+ *
28
+ * Returns the raw page content (HTML, XSL, etc.).
29
+ *
30
+ * @see https://help.quickbase.com/docs/api-getdbpage
31
+ *
32
+ * @param pageIdOrName - Page ID (number) or page name (string)
33
+ *
34
+ * @example
35
+ * ```typescript
36
+ * // By page ID
37
+ * const content = await getDBPage(caller, 'bqapp123', 3);
38
+ *
39
+ * // By page name
40
+ * const content = await getDBPage(caller, 'bqapp123', 'mystylesheet.xsl');
41
+ * ```
42
+ */
43
+ export async function getDBPage(
44
+ caller: XmlCaller,
45
+ appId: string,
46
+ pageIdOrName: string | number
47
+ ): Promise<string> {
48
+ const inner = `<pageID>${escapeXml(String(pageIdOrName))}</pageID>`;
49
+ const body = buildRequest(inner);
50
+ const xml = await caller.doXml(appId, 'API_GetDBPage', body);
51
+
52
+ // Note: GetDBPage returns raw HTML, not XML
53
+ // Check if it's an error response
54
+ if (xml.includes('<errcode>') && !xml.includes('<errcode>0</errcode>')) {
55
+ const base = parseBaseResponse(xml);
56
+ checkError(base);
57
+ }
58
+
59
+ // If not an error, return the raw content
60
+ // The response is the page content itself, not wrapped in XML
61
+ return xml;
62
+ }
63
+
64
+ /**
65
+ * Create or update a code page.
66
+ *
67
+ * Page types:
68
+ * - 1: XSL stylesheets or HTML pages
69
+ * - 3: Exact Forms (Word templates)
70
+ *
71
+ * @see https://help.quickbase.com/docs/api-addreplacedbpage
72
+ *
73
+ * @param pageId - Existing page ID to replace (undefined to create new)
74
+ * @param pageName - Name for the page (required when creating new)
75
+ *
76
+ * @example
77
+ * ```typescript
78
+ * // Create a new page
79
+ * const result = await addReplaceDBPage(
80
+ * caller, 'bqapp123',
81
+ * 'mypage.html', undefined, 1,
82
+ * '<html><body>Hello World</body></html>'
83
+ * );
84
+ * console.log(`Created page ID: ${result.pageId}`);
85
+ *
86
+ * // Update existing page
87
+ * await addReplaceDBPage(
88
+ * caller, 'bqapp123',
89
+ * 'mypage.html', 6, 1,
90
+ * '<html><body>Updated content</body></html>'
91
+ * );
92
+ * ```
93
+ */
94
+ export async function addReplaceDBPage(
95
+ caller: XmlCaller,
96
+ appId: string,
97
+ pageName: string,
98
+ pageId: number | undefined,
99
+ pageType: PageType,
100
+ pageBody: string
101
+ ): Promise<AddReplaceDBPageResult> {
102
+ let inner = '';
103
+
104
+ if (pageId !== undefined) {
105
+ inner += `<pageid>${pageId}</pageid>`;
106
+ }
107
+ inner += `<pagename>${escapeXml(pageName)}</pagename>`;
108
+ inner += `<pagetype>${pageType}</pagetype>`;
109
+ inner += xmlElementCDATA('pagebody', pageBody);
110
+
111
+ const body = buildRequest(inner);
112
+ const xml = await caller.doXml(appId, 'API_AddReplaceDBPage', body);
113
+
114
+ const base = parseBaseResponse(xml);
115
+ checkError(base);
116
+
117
+ const resultPageId = parseXmlNumber(getTagContent(xml, 'pageID'));
118
+ return { pageId: resultPageId };
119
+ }
@@ -0,0 +1,75 @@
1
+ /**
2
+ * XML API Application Variables (DBVars)
3
+ *
4
+ * Endpoints for managing application variables.
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 { XmlCaller } from './types.js';
11
+ import { checkError } from './errors.js';
12
+ import {
13
+ buildRequest,
14
+ escapeXml,
15
+ parseBaseResponse,
16
+ getTagContent,
17
+ } from './request.js';
18
+
19
+ /**
20
+ * Get an application variable value.
21
+ *
22
+ * DBVars are application-level variables that can be used in formulas
23
+ * and code pages.
24
+ *
25
+ * @see https://help.quickbase.com/docs/api-getdbvar
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * const value = await getDBVar(caller, 'bqapp123', 'myVariable');
30
+ * console.log(`Variable value: ${value}`);
31
+ * ```
32
+ */
33
+ export async function getDBVar(
34
+ caller: XmlCaller,
35
+ appId: string,
36
+ varName: string
37
+ ): Promise<string> {
38
+ const inner = `<varname>${escapeXml(varName)}</varname>`;
39
+ const body = buildRequest(inner);
40
+ const xml = await caller.doXml(appId, 'API_GetDBVar', body);
41
+
42
+ const base = parseBaseResponse(xml);
43
+ checkError(base);
44
+
45
+ return getTagContent(xml, 'value');
46
+ }
47
+
48
+ /**
49
+ * Set an application variable value.
50
+ *
51
+ * Creates the variable if it doesn't exist, or overwrites if it does.
52
+ * Requires Full Administration rights on the application.
53
+ *
54
+ * @see https://help.quickbase.com/docs/api-setdbvar
55
+ *
56
+ * @example
57
+ * ```typescript
58
+ * await setDBVar(caller, 'bqapp123', 'myVariable', '42');
59
+ * ```
60
+ */
61
+ export async function setDBVar(
62
+ caller: XmlCaller,
63
+ appId: string,
64
+ varName: string,
65
+ value: string
66
+ ): Promise<void> {
67
+ const inner =
68
+ `<varname>${escapeXml(varName)}</varname>` +
69
+ `<value>${escapeXml(value)}</value>`;
70
+ const body = buildRequest(inner);
71
+ const xml = await caller.doXml(appId, 'API_SetDBVar', body);
72
+
73
+ const base = parseBaseResponse(xml);
74
+ checkError(base);
75
+ }
@@ -0,0 +1,133 @@
1
+ /**
2
+ * XML API Errors
3
+ *
4
+ * Error handling for the legacy QuickBase XML API.
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 { BaseResponse } from './types.js';
11
+ import { QuickbaseTicketError, QuickbaseAppTokenError, QuickbaseXmlError } from '../core/errors.js';
12
+
13
+ /**
14
+ * Common QuickBase XML API error codes
15
+ */
16
+ export const XmlErrorCode = {
17
+ /** No error (success) */
18
+ Success: 0,
19
+ /** Invalid input parameters */
20
+ InvalidInput: 2,
21
+ /** User is not authorized for this action */
22
+ Unauthorized: 3,
23
+ /** Authentication ticket is invalid or expired (Bad Ticket) */
24
+ InvalidTicket: 4,
25
+ /** Database/table does not exist */
26
+ NoSuchDatabase: 6,
27
+ /** SSL is required for this application/table */
28
+ SSLRequired: 8,
29
+ /** Realm IP filtering is in place */
30
+ UnauthorizedIP: 19,
31
+ /** Application token provided is invalid or not assigned */
32
+ InvalidAppToken: 24,
33
+ /** Record does not exist */
34
+ NoSuchRecord: 30,
35
+ /** Field does not exist */
36
+ NoSuchField: 31,
37
+ /** Application does not exist or was deleted */
38
+ NoSuchApp: 32,
39
+ /** User does not exist */
40
+ NoSuchUser: 33,
41
+ /** Access denied */
42
+ AccessDenied: 83,
43
+ } as const;
44
+
45
+ /**
46
+ * Error returned by the QuickBase XML API.
47
+ *
48
+ * XML-API: This class is a legacy alias for QuickbaseXmlError.
49
+ */
50
+ export class XmlError extends QuickbaseXmlError {
51
+ constructor(code: number, text: string, detail?: string, action?: string) {
52
+ super(code, text, { description: detail, action });
53
+ this.name = 'XmlError';
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Check if the response contains an error and throw if so.
59
+ * Returns void if successful (errcode === 0).
60
+ */
61
+ export function checkError(resp: BaseResponse): void {
62
+ if (resp.errcode === 0) {
63
+ return;
64
+ }
65
+
66
+ // Map to specialized error types
67
+ if (resp.errcode === XmlErrorCode.InvalidTicket) {
68
+ throw new QuickbaseTicketError(resp.errtext, resp.errcode, {
69
+ description: resp.errdetail,
70
+ action: resp.action,
71
+ });
72
+ }
73
+
74
+ if (
75
+ resp.errcode === XmlErrorCode.InvalidAppToken
76
+ ) {
77
+ throw new QuickbaseAppTokenError(resp.errtext, resp.errcode, {
78
+ description: resp.errdetail,
79
+ action: resp.action,
80
+ });
81
+ }
82
+
83
+ throw new XmlError(resp.errcode, resp.errtext, resp.errdetail, resp.action);
84
+ }
85
+
86
+ /**
87
+ * Check if the error is an authorization error (code 3 or 83)
88
+ */
89
+ export function isUnauthorized(err: unknown): boolean {
90
+ if (err instanceof XmlError) {
91
+ return (
92
+ err.code === XmlErrorCode.Unauthorized ||
93
+ err.code === XmlErrorCode.AccessDenied
94
+ );
95
+ }
96
+ return false;
97
+ }
98
+
99
+ /**
100
+ * Check if the error indicates a resource was not found
101
+ */
102
+ export function isNotFound(err: unknown): boolean {
103
+ if (err instanceof XmlError) {
104
+ return (
105
+ err.code === XmlErrorCode.NoSuchDatabase ||
106
+ err.code === XmlErrorCode.NoSuchApp ||
107
+ err.code === XmlErrorCode.NoSuchRecord ||
108
+ err.code === XmlErrorCode.NoSuchField ||
109
+ err.code === XmlErrorCode.NoSuchUser
110
+ );
111
+ }
112
+ return false;
113
+ }
114
+
115
+ /**
116
+ * Check if the error indicates an invalid or expired authentication ticket
117
+ */
118
+ export function isInvalidTicket(err: unknown): boolean {
119
+ if (err instanceof XmlError) {
120
+ return err.code === XmlErrorCode.InvalidTicket;
121
+ }
122
+ return false;
123
+ }
124
+
125
+ /**
126
+ * Check if the error indicates invalid input parameters
127
+ */
128
+ export function isInvalidInput(err: unknown): boolean {
129
+ if (err instanceof XmlError) {
130
+ return err.code === XmlErrorCode.InvalidInput;
131
+ }
132
+ return false;
133
+ }
@@ -0,0 +1,121 @@
1
+ /**
2
+ * XML API Field Management
3
+ *
4
+ * Endpoints for managing field properties.
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
+ FieldAddChoicesResult,
13
+ FieldRemoveChoicesResult,
14
+ } from './types.js';
15
+ import { checkError } from './errors.js';
16
+ import {
17
+ buildRequest,
18
+ escapeXml,
19
+ parseBaseResponse,
20
+ getTagContent,
21
+ parseXmlNumber,
22
+ } from './request.js';
23
+
24
+ /**
25
+ * Add choices to a multiple-choice field.
26
+ *
27
+ * @see https://help.quickbase.com/docs/api-fieldaddchoices
28
+ *
29
+ * @example
30
+ * ```typescript
31
+ * const result = await fieldAddChoices(caller, 'bqtable123', 6, ['Option A', 'Option B']);
32
+ * console.log(`Added ${result.numAdded} choices`);
33
+ * ```
34
+ */
35
+ export async function fieldAddChoices(
36
+ caller: XmlCaller,
37
+ tableId: string,
38
+ fieldId: number,
39
+ choices: string[]
40
+ ): Promise<FieldAddChoicesResult> {
41
+ if (choices.length === 0) {
42
+ throw new Error('choices array cannot be empty');
43
+ }
44
+ let inner = `<fid>${fieldId}</fid>`;
45
+ for (const choice of choices) {
46
+ inner += `<choice>${escapeXml(choice)}</choice>`;
47
+ }
48
+
49
+ const body = buildRequest(inner);
50
+ const xml = await caller.doXml(tableId, 'API_FieldAddChoices', body);
51
+
52
+ const base = parseBaseResponse(xml);
53
+ checkError(base);
54
+
55
+ return {
56
+ fieldId,
57
+ fieldName: getTagContent(xml, 'fname') || '',
58
+ numAdded: parseXmlNumber(getTagContent(xml, 'numadded')),
59
+ };
60
+ }
61
+
62
+ /**
63
+ * Remove choices from a multiple-choice field.
64
+ *
65
+ * @see https://help.quickbase.com/docs/api-fieldremovechoices
66
+ *
67
+ * @example
68
+ * ```typescript
69
+ * const result = await fieldRemoveChoices(caller, 'bqtable123', 6, ['Option A']);
70
+ * console.log(`Removed ${result.numRemoved} choices`);
71
+ * ```
72
+ */
73
+ export async function fieldRemoveChoices(
74
+ caller: XmlCaller,
75
+ tableId: string,
76
+ fieldId: number,
77
+ choices: string[]
78
+ ): Promise<FieldRemoveChoicesResult> {
79
+ if (choices.length === 0) {
80
+ throw new Error('choices array cannot be empty');
81
+ }
82
+ let inner = `<fid>${fieldId}</fid>`;
83
+ for (const choice of choices) {
84
+ inner += `<choice>${escapeXml(choice)}</choice>`;
85
+ }
86
+
87
+ const body = buildRequest(inner);
88
+ const xml = await caller.doXml(tableId, 'API_FieldRemoveChoices', body);
89
+
90
+ const base = parseBaseResponse(xml);
91
+ checkError(base);
92
+
93
+ return {
94
+ fieldId,
95
+ fieldName: getTagContent(xml, 'fname') || '',
96
+ numRemoved: parseXmlNumber(getTagContent(xml, 'numremoved')),
97
+ };
98
+ }
99
+
100
+ /**
101
+ * Set the key field for a table.
102
+ *
103
+ * @see https://help.quickbase.com/docs/api-setkeyfield
104
+ *
105
+ * @example
106
+ * ```typescript
107
+ * await setKeyField(caller, 'bqtable123', 6);
108
+ * ```
109
+ */
110
+ export async function setKeyField(
111
+ caller: XmlCaller,
112
+ tableId: string,
113
+ fieldId: number
114
+ ): Promise<void> {
115
+ const inner = `<fid>${fieldId}</fid>`;
116
+ const body = buildRequest(inner);
117
+ const xml = await caller.doXml(tableId, 'API_SetKeyField', body);
118
+
119
+ const base = parseBaseResponse(xml);
120
+ checkError(base);
121
+ }