quickbase-js 0.1.0-beta.8 → 1.0.0

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 (132) hide show
  1. package/README.md +252 -126
  2. package/dist/esm/quickbase.d.ts +2725 -2689
  3. package/dist/esm/quickbase.js +1763 -3524
  4. package/dist/esm/quickbase.js.map +1 -1
  5. package/dist/esm/quickbase.min.js +1 -1
  6. package/dist/esm/quickbase.min.js.map +1 -1
  7. package/dist/umd/quickbase.umd.js +1762 -3706
  8. package/dist/umd/quickbase.umd.js.map +1 -1
  9. package/dist/umd/quickbase.umd.min.js +1 -1
  10. package/dist/umd/quickbase.umd.min.js.map +1 -1
  11. package/package.json +27 -12
  12. package/CHANGELOG.md +0 -155
  13. package/build-common.js +0 -131
  14. package/build-esm.js +0 -71
  15. package/build-umd.js +0 -69
  16. package/examples/quicbase-jsCDN.html +0 -36
  17. package/examples/react.js +0 -48
  18. package/examples/vanillajs.html +0 -47
  19. package/open-api/fix-spec-main.ts +0 -97
  20. package/open-api/generate-override-report.ts +0 -214
  21. package/open-api/generate-unified-interface.ts +0 -256
  22. package/open-api/output/override-report.html +0 -11
  23. package/open-api/output/override-report.json +0 -14395
  24. package/open-api/output/quickbase-fixed.json +0 -11191
  25. package/open-api/regenerate-client.ts +0 -116
  26. package/open-api/schema/enhance-general.ts +0 -100
  27. package/open-api/schema/fix-arrays.ts +0 -69
  28. package/open-api/schema/tags/apps.ts +0 -90
  29. package/open-api/schema/tags/fields.ts +0 -471
  30. package/open-api/schema/tags/groups.ts +0 -51
  31. package/open-api/schema/tags/index.ts +0 -66
  32. package/open-api/schema/tags/records.ts +0 -115
  33. package/open-api/schema/tags/tables.ts +0 -211
  34. package/open-api/schema/tags/users.ts +0 -47
  35. package/open-api/types/spec.ts +0 -42
  36. package/open-api/utils/infer-schema.ts +0 -40
  37. package/open-api/utils/naming.ts +0 -10
  38. package/playwright.config.ts +0 -37
  39. package/rollup.config.js +0 -114
  40. package/specs/QuickBase_RESTful_API_2025-03-04T06_22_39.725Z.json +0 -46878
  41. package/specs/split/apps.json +0 -1315
  42. package/specs/split/auth.json +0 -191
  43. package/specs/split/fields.json +0 -3869
  44. package/specs/split/records.json +0 -754
  45. package/specs/split/reports.json +0 -619
  46. package/specs/split/tables.json +0 -1605
  47. package/src/RateLimitError.ts +0 -12
  48. package/src/ThrottleBucket.ts +0 -36
  49. package/src/generated-unified/QuickbaseClient.ts +0 -359
  50. package/src/generated-unified/missing-types-report.json +0 -3
  51. package/src/index.ts +0 -1
  52. package/src/invokeMethod.ts +0 -357
  53. package/src/quickbaseClient.ts +0 -244
  54. package/src/tokenCache.ts +0 -51
  55. package/src/utils.ts +0 -7
  56. package/test-esm.js +0 -1
  57. package/test-results.json +0 -2050
  58. package/test-umd.js +0 -1
  59. package/test.ts +0 -1
  60. package/tests/playwright/qb/auth/cacheSwitching.test.ts +0 -159
  61. package/tests/playwright/qb/auth/fetchTempToken401.test.ts +0 -219
  62. package/tests/playwright/qb/auth/getAppWithTempToken.test.ts +0 -162
  63. package/tests/playwright/qb/auth/getAppWithUserToken.test.ts +0 -153
  64. package/tests/playwright/qb/auth/getTempToken.test.ts +0 -152
  65. package/tests/playwright/qb/auth/tempTokenPrefetch.test.ts +0 -179
  66. package/tests/playwright/qb/auth/tempTokenRenewal.test.ts +0 -133
  67. package/tests/setup.ts +0 -53
  68. package/tests/vitest/qb/apps/copyApp.test.ts +0 -76
  69. package/tests/vitest/qb/apps/createApp.test.ts +0 -64
  70. package/tests/vitest/qb/apps/deleteApp.test.ts +0 -76
  71. package/tests/vitest/qb/apps/getApp.test.ts +0 -44
  72. package/tests/vitest/qb/apps/getAppEvents.test.ts +0 -67
  73. package/tests/vitest/qb/apps/updateApp.test.ts +0 -58
  74. package/tests/vitest/qb/auth/retryUserToken401.test.ts +0 -56
  75. package/tests/vitest/qb/fields/createField.test.ts +0 -77
  76. package/tests/vitest/qb/fields/deleteFields.test.ts +0 -83
  77. package/tests/vitest/qb/fields/getField.test.ts +0 -85
  78. package/tests/vitest/qb/fields/getFieldUsage.test.ts +0 -174
  79. package/tests/vitest/qb/fields/getFields.test.ts +0 -232
  80. package/tests/vitest/qb/fields/getFieldsUsage.test.ts +0 -77
  81. package/tests/vitest/qb/fields/updateField.test.ts +0 -93
  82. package/tests/vitest/qb/index.ts +0 -29
  83. package/tests/vitest/qb/records/deleteRecords.test.ts +0 -110
  84. package/tests/vitest/qb/records/runQuery.test.ts +0 -146
  85. package/tests/vitest/qb/records/upsert.test.ts +0 -102
  86. package/tests/vitest/qb/tables/createTable.test.ts +0 -55
  87. package/tests/vitest/qb/tables/deleteTable.test.ts +0 -50
  88. package/tests/vitest/qb/tables/getAppTables.test.ts +0 -46
  89. package/tests/vitest/qb/tables/getRelationships.test.ts +0 -80
  90. package/tests/vitest/qb/tables/getTable.test.ts +0 -26
  91. package/tests/vitest/qb/tables/updateRelationship.test.ts +0 -277
  92. package/tests/vitest/qb/tables/updateTable.test.ts +0 -48
  93. package/tests/vitest/unit/apps/copyApp.test.ts +0 -488
  94. package/tests/vitest/unit/apps/createApp.test.ts +0 -186
  95. package/tests/vitest/unit/apps/deleteApp.test.ts +0 -216
  96. package/tests/vitest/unit/apps/getApp.test.ts +0 -128
  97. package/tests/vitest/unit/apps/getAppEvents.test.ts +0 -193
  98. package/tests/vitest/unit/apps/updateApp.test.ts +0 -381
  99. package/tests/vitest/unit/auth/fetchTempToken401.test.ts +0 -62
  100. package/tests/vitest/unit/auth/getTempToken.test.ts +0 -95
  101. package/tests/vitest/unit/auth/retryTempToken401.test.ts +0 -113
  102. package/tests/vitest/unit/auth/retryTempTokenTwo401.test.ts +0 -81
  103. package/tests/vitest/unit/auth/retryUserToken401.test.ts +0 -56
  104. package/tests/vitest/unit/fields/createField.test.ts +0 -387
  105. package/tests/vitest/unit/fields/deleteFields.test.ts +0 -141
  106. package/tests/vitest/unit/fields/getField.test.ts +0 -189
  107. package/tests/vitest/unit/fields/getFieldUsage.test.ts +0 -166
  108. package/tests/vitest/unit/fields/getFields.test.ts +0 -122
  109. package/tests/vitest/unit/fields/getFieldsUsage.test.ts +0 -160
  110. package/tests/vitest/unit/fields/updateField.test.ts +0 -280
  111. package/tests/vitest/unit/index.ts +0 -33
  112. package/tests/vitest/unit/rateLimit.test.ts +0 -189
  113. package/tests/vitest/unit/records/deleteRecords.test.ts +0 -112
  114. package/tests/vitest/unit/records/runQuery.test.ts +0 -102
  115. package/tests/vitest/unit/records/upsert.test.ts +0 -234
  116. package/tests/vitest/unit/tables/createTable.test.ts +0 -70
  117. package/tests/vitest/unit/tables/deleteTable.test.ts +0 -77
  118. package/tests/vitest/unit/tables/getAppTables.test.ts +0 -85
  119. package/tests/vitest/unit/tables/getRelationships.test.ts +0 -259
  120. package/tests/vitest/unit/tables/getTable.test.ts +0 -233
  121. package/tests/vitest/unit/tables/updateRelationship.test.ts +0 -305
  122. package/tests/vitest/unit/tables/updateTable.test.ts +0 -80
  123. package/tests/vitest/unit/tokenLifespan.test.ts +0 -138
  124. package/tests/vitest/unit/umd.test.ts +0 -8
  125. package/text.txt +0 -104
  126. package/tools/log-gen.ts +0 -235
  127. package/tools/log-gen.yaml +0 -4982
  128. package/tools/openapi-jars/openapi-generator-cli-7.12.0.jar +0 -0
  129. package/tsconfig.build.json +0 -17
  130. package/tsconfig.json +0 -32
  131. package/versionrc.json +0 -9
  132. package/vitest.config.ts +0 -27
@@ -1,357 +0,0 @@
1
- // src/invokeMethod.ts
2
- import { QuickbaseClient } from "./quickbaseClient";
3
- import { ResponseError } from "./generated/runtime";
4
- import { ThrottleBucket } from "./ThrottleBucket";
5
- import { RateLimitError } from "./RateLimitError";
6
- import { GetTempTokenDBID200Response } from "./generated/models";
7
-
8
- export type ApiMethod<K extends keyof QuickbaseClient> = (
9
- requestParameters: Parameters<QuickbaseClient[K]>[0],
10
- initOverrides?: RequestInit
11
- ) => Promise<ReturnType<QuickbaseClient[K]>>;
12
-
13
- export interface MethodInfo<K extends keyof QuickbaseClient> {
14
- api: any;
15
- method: ApiMethod<K>;
16
- paramMap: string[];
17
- httpMethod: string;
18
- }
19
-
20
- export interface TempTokenParams {
21
- appId?: string;
22
- tableId?: string;
23
- dbid?: string;
24
- }
25
-
26
- export interface TokenCache {
27
- get: (dbid: string) => string | undefined;
28
- set: (dbid: string, token: string) => void;
29
- }
30
-
31
- export async function invokeMethod<K extends keyof QuickbaseClient>(
32
- methodName: K,
33
- params: Parameters<QuickbaseClient[K]>[0] & Partial<TempTokenParams>,
34
- methodMap: { [P in keyof QuickbaseClient]: MethodInfo<P> },
35
- baseHeaders: Record<string, string>,
36
- tokenCache: TokenCache,
37
- fetchTempToken: (dbid: string) => Promise<string>,
38
- transformDates: (obj: any, convertStringsToDates: boolean) => any,
39
- initialTempToken: string | undefined,
40
- userToken: string | undefined,
41
- useTempTokens: boolean | undefined,
42
- debug: boolean | undefined,
43
- convertDates: boolean,
44
- retryCount: number = 0,
45
- throttleBucket: ThrottleBucket | null = null,
46
- maxRetries: number = 3,
47
- retryDelay: number = 1000
48
- ): Promise<ReturnType<QuickbaseClient[K]>> {
49
- const methodInfo = methodMap[methodName];
50
- if (!methodInfo) throw new Error(`Method ${methodName} not found`);
51
-
52
- const hasBody = "body" in params && params.body !== undefined;
53
- const body = hasBody ? (params as any).body : undefined;
54
- const restParams: any = hasBody
55
- ? Object.fromEntries(
56
- Object.entries(params).filter(([key]) => key !== "body")
57
- )
58
- : { ...params };
59
-
60
- const requestParameters: any = {
61
- ...restParams,
62
- ...(hasBody ? { generated: body } : {}),
63
- };
64
-
65
- let requestOptions: RequestInit = {
66
- credentials: "omit",
67
- method: methodInfo.httpMethod,
68
- ...(hasBody ? { body } : {}),
69
- };
70
-
71
- const selectedToken =
72
- initialTempToken || (userToken && !useTempTokens ? userToken : undefined);
73
- let authorizationToken = selectedToken;
74
-
75
- if (methodName === "getTempTokenDBID" && useTempTokens) {
76
- const dbid = extractDbid(params, "No dbid provided for getTempTokenDBID");
77
- const cachedToken = tokenCache.get(dbid);
78
- if (cachedToken) {
79
- return Promise.resolve({
80
- temporaryAuthorization: cachedToken,
81
- } as GetTempTokenDBID200Response as ReturnType<QuickbaseClient[K]>);
82
- }
83
- }
84
-
85
- if (useTempTokens && !authorizationToken) {
86
- const dbid = extractDbid(
87
- params,
88
- `No dbid found in params for ${methodName} to fetch temp token`
89
- );
90
- const cachedToken = tokenCache.get(dbid);
91
- authorizationToken = cachedToken || (await fetchTempToken(dbid));
92
- if (methodName === "getTempTokenDBID") {
93
- return Promise.resolve({
94
- temporaryAuthorization: authorizationToken,
95
- } as GetTempTokenDBID200Response as ReturnType<QuickbaseClient[K]>);
96
- }
97
- }
98
- requestOptions.headers = authorizationToken
99
- ? {
100
- ...baseHeaders,
101
- Authorization: useTempTokens
102
- ? `QB-TEMP-TOKEN ${authorizationToken}`
103
- : `QB-USER-TOKEN ${authorizationToken}`,
104
- }
105
- : baseHeaders;
106
-
107
- if (debug) {
108
- console.log(`[${methodName}] requestParameters:`, requestParameters);
109
- console.log(`[${methodName}] requestOptions:`, requestOptions);
110
- }
111
-
112
- async function processResponse(
113
- rawResponse: any
114
- ): Promise<ReturnType<QuickbaseClient[K]>> {
115
- if (debug) console.log(`[${methodName}] rawResponse:`, rawResponse);
116
-
117
- if (rawResponse instanceof Response) {
118
- const contentType = rawResponse.headers
119
- .get("Content-Type")
120
- ?.toLowerCase();
121
- if (debug) console.log(`[${methodName}] contentType:`, contentType);
122
-
123
- if (contentType?.includes("application/octet-stream")) {
124
- return (await rawResponse.arrayBuffer()) as ReturnType<
125
- QuickbaseClient[K]
126
- >;
127
- }
128
- if (
129
- contentType?.includes("application/x-yaml") ||
130
- contentType?.includes("text/yaml")
131
- ) {
132
- return (await rawResponse.text()) as ReturnType<QuickbaseClient[K]>;
133
- }
134
- if (contentType?.includes("application/json")) {
135
- const jsonResponse = await rawResponse.json();
136
- return transformDates(jsonResponse, convertDates) as ReturnType<
137
- QuickbaseClient[K]
138
- >;
139
- }
140
- return rawResponse as ReturnType<QuickbaseClient[K]>;
141
- }
142
-
143
- if (rawResponse && typeof rawResponse.value === "function") {
144
- const response = await rawResponse.value();
145
- if (debug)
146
- console.log(`[${methodName}] Resolved JSONApiResponse:`, response);
147
- return transformDates(response, convertDates) as ReturnType<
148
- QuickbaseClient[K]
149
- >;
150
- }
151
-
152
- const transformed = transformDates(rawResponse, convertDates);
153
- if (debug)
154
- console.log(`[${methodName}] Transformed non-Response:`, transformed);
155
- return transformed as ReturnType<QuickbaseClient[K]>;
156
- }
157
-
158
- async function withRetries(
159
- fn: () => Promise<ReturnType<QuickbaseClient[K]>>,
160
- options: {
161
- maxAttempts: number;
162
- shouldRetry: (error: any) => boolean;
163
- onRetry?: (error: any, attempt: number) => Promise<void>;
164
- delay?: (attempt: number, error: any) => number;
165
- }
166
- ): Promise<ReturnType<QuickbaseClient[K]>> {
167
- let attempt = 0;
168
- while (true) {
169
- try {
170
- if (throttleBucket) {
171
- if (debug) console.log(`[${methodName}] Awaiting throttle bucket`);
172
- await throttleBucket.acquire();
173
- if (debug) console.log(`[${methodName}] Throttle bucket acquired`);
174
- }
175
- return await fn();
176
- } catch (error) {
177
- console.log(`[${methodName}] Caught error:`, error);
178
- console.log(
179
- `[${methodName}] instanceof ResponseError:`,
180
- error instanceof ResponseError
181
- );
182
- console.log(
183
- `[${methodName}] Response status:`,
184
- (error as any).response?.status
185
- );
186
- const effectiveError =
187
- error instanceof Object &&
188
- "cause" in error &&
189
- error.cause instanceof ResponseError
190
- ? error.cause
191
- : error instanceof ResponseError
192
- ? error
193
- : error;
194
- console.log(`[${methodName}] Effective error:`, effectiveError);
195
- console.log(
196
- `[${methodName}] Effective instanceof ResponseError:`,
197
- effectiveError instanceof ResponseError
198
- );
199
- console.log(
200
- `[${methodName}] Effective response status:`,
201
- (effectiveError as any).response?.status
202
- );
203
-
204
- attempt++;
205
- if (
206
- !options.shouldRetry(effectiveError) ||
207
- attempt >= options.maxAttempts
208
- ) {
209
- let errorMessage =
210
- effectiveError instanceof ResponseError
211
- ? effectiveError.message
212
- : String(error);
213
- console.log(`[${methodName}] No retry, throwing:`, errorMessage);
214
- if (
215
- effectiveError instanceof ResponseError &&
216
- effectiveError.response
217
- ) {
218
- try {
219
- const errorBody = await effectiveError.response.json();
220
- if (debug)
221
- console.log(`[${methodName}] Error response body:`, errorBody);
222
- errorMessage = errorBody?.message || errorMessage;
223
- } catch (e) {
224
- if (debug)
225
- console.log(`[${methodName}] Failed to parse error body:`, e);
226
- }
227
- throw new Error(
228
- `API Error: ${errorMessage} (Status: ${effectiveError.response.status})`
229
- );
230
- }
231
- throw new Error(`API Error: ${errorMessage} (Status: unknown)`);
232
- }
233
- if (options.onRetry) {
234
- console.log(`[${methodName}] Retrying, attempt:`, attempt);
235
- await options.onRetry(effectiveError, attempt);
236
- }
237
- const delayMs = options.delay
238
- ? options.delay(attempt, effectiveError)
239
- : retryDelay * Math.pow(2, attempt - 1);
240
- if (debug)
241
- console.log(
242
- `[${methodName}] Retrying after ${delayMs}ms (attempt ${attempt}/${
243
- options.maxAttempts - 1
244
- })`
245
- );
246
- await new Promise((resolve) => setTimeout(resolve, delayMs));
247
- }
248
- }
249
- }
250
-
251
- const mainPromise = withRetries(
252
- () =>
253
- methodInfo
254
- .method(requestParameters, requestOptions)
255
- .then(processResponse),
256
- {
257
- maxAttempts: (useTempTokens ?? false) || userToken ? 2 : 1,
258
- shouldRetry: (error) =>
259
- error instanceof ResponseError &&
260
- error.response?.status === 401 &&
261
- ((useTempTokens ?? false) ||
262
- (!!userToken && !(useTempTokens ?? false))),
263
- onRetry: async (error) => {
264
- if (error instanceof ResponseError && error.response?.status === 401) {
265
- if (useTempTokens ?? false) {
266
- if (debug)
267
- console.log(
268
- `Authorization error for ${methodName} (temp token), refreshing token:`,
269
- error.message
270
- );
271
- const dbid = extractDbid(
272
- params,
273
- `No dbid to refresh token after authorization error`
274
- );
275
- try {
276
- authorizationToken = await fetchTempToken(dbid);
277
- } catch (fetchError) {
278
- const fetchErrorMessage =
279
- fetchError instanceof ResponseError && fetchError.response
280
- ? (await fetchError.response.json())?.message ||
281
- "Unauthorized"
282
- : String(fetchError);
283
- throw new Error(
284
- `API Error: ${fetchErrorMessage} (Status: ${
285
- fetchError instanceof ResponseError &&
286
- fetchError.response?.status
287
- ? fetchError.response.status
288
- : "unknown"
289
- })`
290
- );
291
- }
292
- requestOptions.headers = {
293
- ...baseHeaders,
294
- Authorization: `QB-TEMP-TOKEN ${authorizationToken}`,
295
- };
296
- } else if (userToken) {
297
- if (debug)
298
- console.log(
299
- `Authorization error for ${methodName} (user token), retrying with same token:`,
300
- error.message
301
- );
302
- }
303
- if (debug)
304
- console.log(
305
- `Retrying ${methodName} with ${
306
- useTempTokens ? "temp" : "user"
307
- } token`
308
- );
309
- }
310
- },
311
- }
312
- );
313
-
314
- return mainPromise.catch((error: unknown) => {
315
- let effectiveError: unknown = error;
316
- if (
317
- error instanceof Object &&
318
- "cause" in error &&
319
- error.cause instanceof ResponseError
320
- ) {
321
- effectiveError = error.cause;
322
- } else if (error instanceof ResponseError) {
323
- effectiveError = error;
324
- }
325
-
326
- if (
327
- effectiveError instanceof ResponseError &&
328
- effectiveError.response?.status === 429
329
- ) {
330
- return withRetries(() => Promise.reject(error), {
331
- maxAttempts: maxRetries + 1,
332
- shouldRetry: (err) =>
333
- err instanceof ResponseError && err.response?.status === 429,
334
- delay: (attempt, err) => {
335
- const headers =
336
- err instanceof ResponseError && err.response
337
- ? err.response.headers
338
- : new Headers();
339
- const retryAfter = headers.get("Retry-After");
340
- return retryAfter
341
- ? parseInt(retryAfter, 10) * 1000
342
- : retryDelay * Math.pow(2, attempt - 1);
343
- },
344
- });
345
- }
346
- throw effectiveError;
347
- });
348
- }
349
-
350
- function extractDbid(
351
- params: Partial<TempTokenParams>,
352
- errorMessage: string
353
- ): string {
354
- const dbid = params.dbid || params.tableId || params.appId;
355
- if (!dbid) throw new Error(errorMessage);
356
- return dbid;
357
- }
@@ -1,244 +0,0 @@
1
- // src/quickbaseClient.ts
2
- import { QuickbaseClient as IQuickbaseClient } from "./generated-unified/QuickbaseClient";
3
- import { Configuration, HTTPHeaders } from "./generated/runtime";
4
- import * as apis from "./generated/apis";
5
- import { TokenCache } from "./tokenCache";
6
- import { simplifyName } from "./utils";
7
- import { invokeMethod, MethodInfo } from "./invokeMethod"; // Added import
8
- import { ThrottleBucket } from "./ThrottleBucket";
9
-
10
- export * from "./generated/models/index";
11
-
12
- export interface QuickbaseClient extends IQuickbaseClient {}
13
-
14
- export interface QuickbaseConfig {
15
- realm: string;
16
- userToken?: string;
17
- tempToken?: string;
18
- useTempTokens?: boolean;
19
- debug?: boolean;
20
- fetchApi?: typeof fetch;
21
- convertDates?: boolean;
22
- tokenLifespan?: number;
23
- throttle?: { rate: number; burst: number };
24
- maxRetries?: number;
25
- retryDelay?: number;
26
- }
27
-
28
- type MethodMap = {
29
- [K in keyof QuickbaseClient]: MethodInfo<K>;
30
- };
31
-
32
- const getParamNames = (fn: (...args: any[]) => any): string[] =>
33
- fn
34
- .toString()
35
- .slice(fn.toString().indexOf("(") + 1, fn.toString().indexOf(")"))
36
- .split(",")
37
- .map((p) => p.trim().split("=")[0]?.trim())
38
- .filter((p) => p && !p.match(/^\{/) && p !== "options");
39
-
40
- function transformDates(obj: any, convertStringsToDates: boolean = true): any {
41
- if (obj === null || obj === undefined) return obj;
42
- if (obj instanceof Date) return obj;
43
- if (
44
- convertStringsToDates &&
45
- typeof obj === "string" &&
46
- /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})?/.test(
47
- obj
48
- )
49
- ) {
50
- return new Date(obj);
51
- }
52
- if (Array.isArray(obj)) {
53
- return obj.map((item) => transformDates(item, convertStringsToDates));
54
- }
55
- if (typeof obj === "object") {
56
- return Object.fromEntries(
57
- Object.entries(obj).map(([key, value]) => [
58
- key,
59
- transformDates(value, convertStringsToDates),
60
- ])
61
- );
62
- }
63
- return obj;
64
- }
65
-
66
- function inferHttpMethod(methodSource: string, debug?: boolean): string {
67
- const methodMatch = methodSource.match(/method:\s*['"]?(\w+)['"]?/i);
68
- const method = methodMatch ? methodMatch[1].toUpperCase() : "GET";
69
- if (debug) {
70
- // console.log(`[inferHttpMethod] Source:`, methodSource);
71
- // console.log(`[inferHttpMethod] Extracted method:`, method);
72
- }
73
- return method;
74
- }
75
-
76
- export function quickbase(config: QuickbaseConfig): QuickbaseClient {
77
- const {
78
- realm,
79
- userToken,
80
- tempToken: initialTempToken,
81
- useTempTokens,
82
- fetchApi,
83
- debug,
84
- convertDates = true,
85
- tokenLifespan,
86
- throttle = { rate: 10, burst: 10 },
87
- maxRetries = 3,
88
- retryDelay = 1000,
89
- } = config;
90
- const baseUrl = `https://api.quickbase.com/v1`;
91
-
92
- const tokenCache = new TokenCache(tokenLifespan);
93
- const throttleBucket = throttle
94
- ? new ThrottleBucket(throttle.rate, throttle.burst)
95
- : null;
96
-
97
- const baseHeaders: HTTPHeaders = {
98
- "QB-Realm-Hostname": `${realm}.quickbase.com`,
99
- "Content-Type": "application/json",
100
- };
101
-
102
- if (initialTempToken) {
103
- baseHeaders["Authorization"] = `QB-TEMP-TOKEN ${initialTempToken}`;
104
- } else if (userToken && !useTempTokens) {
105
- baseHeaders["Authorization"] = `QB-USER-TOKEN ${userToken}`;
106
- }
107
-
108
- const defaultFetch: typeof fetch | undefined =
109
- typeof globalThis.window !== "undefined"
110
- ? globalThis.window.fetch.bind(globalThis.window)
111
- : undefined;
112
- const configuration = new Configuration({
113
- basePath: baseUrl,
114
- headers: { ...baseHeaders },
115
- fetchApi: fetchApi || defaultFetch,
116
- credentials: "omit",
117
- });
118
-
119
- if (!configuration.fetchApi && typeof globalThis.window === "undefined") {
120
- throw new Error(
121
- "fetchApi must be provided in non-browser environments (e.g., Node.js)"
122
- );
123
- }
124
-
125
- const apiInstances = Object.fromEntries(
126
- Object.entries(apis)
127
- .filter(([name]) => name.endsWith("Api"))
128
- .map(([name, ApiClass]) => [
129
- name.replace("Api", "").toLowerCase(),
130
- new ApiClass(configuration),
131
- ])
132
- );
133
-
134
- const methodMap = buildMethodMap();
135
-
136
- function buildMethodMap(): MethodMap {
137
- const methodMap: Partial<MethodMap> = {};
138
- const isValidMethod = (name: string) =>
139
- !name.startsWith("_") &&
140
- name !== "constructor" &&
141
- !["Middleware", "Pre", "Post", "Raw"].some((s) => name.includes(s));
142
-
143
- for (const [apiName, api] of Object.entries(apiInstances)) {
144
- Object.getOwnPropertyNames(Object.getPrototypeOf(api))
145
- .filter(
146
- (name) =>
147
- isValidMethod(name) &&
148
- typeof api[name as keyof typeof api] === "function"
149
- )
150
- .forEach((rawMethodName) => {
151
- const simplifiedName = simplifyName(
152
- rawMethodName
153
- ) as keyof QuickbaseClient;
154
- const rawMethodKey = `${rawMethodName}Raw` as keyof typeof api;
155
- const method =
156
- api[rawMethodKey] || api[rawMethodName as keyof typeof api];
157
- const boundMethod = method.bind(api as any) as unknown;
158
- if (typeof boundMethod === "function" && boundMethod.length <= 2) {
159
- const methodSource = method.toString();
160
- methodMap[simplifiedName] = {
161
- api,
162
- method: boundMethod as any, // Adjust typing as needed
163
- paramMap: getParamNames(method),
164
- httpMethod: inferHttpMethod(methodSource, debug),
165
- };
166
- }
167
- });
168
- }
169
- return methodMap as MethodMap;
170
- }
171
-
172
- const fetchTempToken = async (dbid: string): Promise<string> => {
173
- const effectiveFetch = fetchApi || defaultFetch;
174
- if (!effectiveFetch) {
175
- throw new Error(
176
- "No fetch implementation available for fetching temp token"
177
- );
178
- }
179
-
180
- const response = await effectiveFetch(
181
- `https://api.quickbase.com/v1/auth/temporary/${dbid}`,
182
- {
183
- method: "GET",
184
- headers: { ...baseHeaders },
185
- credentials: "include",
186
- }
187
- );
188
-
189
- if (!response.ok) {
190
- const errorBody: { message?: string } = await response.json();
191
- throw new Error(
192
- `API Error: ${errorBody.message || "Unknown error"} (Status: ${
193
- response.status
194
- })`
195
- );
196
- }
197
-
198
- const tokenResult = await response.json();
199
- const token = tokenResult.temporaryAuthorization;
200
- if (!token) {
201
- throw new Error("No temporary token returned from API");
202
- }
203
- tokenCache.set(dbid, token);
204
- if (debug) {
205
- console.log(`Fetched and cached new token for dbid: ${dbid}`, token);
206
- }
207
- return token;
208
- };
209
-
210
- const proxy = new Proxy<QuickbaseClient>({} as QuickbaseClient, {
211
- get: (_, prop: string): ((params: any) => Promise<any>) | undefined => {
212
- if (prop in methodMap) {
213
- const methodName = prop as keyof QuickbaseClient;
214
- return (params: Parameters<QuickbaseClient[typeof methodName]>[0]) =>
215
- invokeMethod(
216
- methodName,
217
- params,
218
- methodMap,
219
- baseHeaders,
220
- tokenCache,
221
- fetchTempToken,
222
- transformDates,
223
- initialTempToken,
224
- userToken,
225
- useTempTokens,
226
- debug,
227
- convertDates,
228
- 0, // retryCount
229
- throttleBucket,
230
- maxRetries,
231
- retryDelay
232
- );
233
- }
234
- return undefined;
235
- },
236
- });
237
-
238
- if (debug) {
239
- console.log("[createClient] Config:", config);
240
- console.log("[createClient] Returning:", proxy);
241
- }
242
-
243
- return proxy;
244
- }
package/src/tokenCache.ts DELETED
@@ -1,51 +0,0 @@
1
- interface CachedToken {
2
- token: string;
3
- expiresAt: number; // Timestamp in milliseconds
4
- }
5
-
6
- export class TokenCache {
7
- private cache: Map<string, CachedToken>;
8
- private readonly lifespan: number; // Token lifespan in milliseconds
9
-
10
- constructor(lifespan: number = 4 * 60 * 1000 + 50 * 1000) {
11
- // Default 4:50
12
- this.cache = new Map<string, CachedToken>();
13
- this.lifespan = lifespan;
14
- }
15
-
16
- get(dbid: string): string | undefined {
17
- const entry = this.cache.get(dbid);
18
- const now = Date.now();
19
- if (entry && entry.expiresAt > now) {
20
- return entry.token;
21
- }
22
- return undefined; // Expired or not found
23
- }
24
-
25
- // New method to get full entry
26
- getEntry(dbid: string): CachedToken | undefined {
27
- const entry = this.cache.get(dbid);
28
- const now = Date.now();
29
- if (entry && entry.expiresAt > now) {
30
- return entry;
31
- }
32
- return undefined; // Expired or not found
33
- }
34
-
35
- set(dbid: string, token: string): void {
36
- const now = Date.now();
37
- this.cache.set(dbid, {
38
- token,
39
- expiresAt: now + this.lifespan,
40
- });
41
- }
42
-
43
- dump(): [string, CachedToken][] {
44
- return Array.from(this.cache.entries());
45
- }
46
-
47
- // New method to clear the cache
48
- clear(): void {
49
- this.cache.clear();
50
- }
51
- }
package/src/utils.ts DELETED
@@ -1,7 +0,0 @@
1
- // src/utils.ts
2
- export function simplifyName(name: string): string {
3
- return name
4
- .replace(/ById$/, "")
5
- .replace(/Api$/, "")
6
- .replace(/^(\w)/, (_, c) => c.toLowerCase());
7
- }
package/test-esm.js DELETED
@@ -1 +0,0 @@
1
- import { quickbase } from './dist/esm/quickbase.js'; const qb = quickbase({ realm: 'example', userToken: 'your-token' }); console.log(qb);