quickbase-js 0.1.0-beta.10
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/CHANGELOG.md +211 -0
- package/LICENSE +21 -0
- package/README.md +179 -0
- package/api-examples.md +49 -0
- package/build-common.js +131 -0
- package/build-esm.js +71 -0
- package/build-umd.js +69 -0
- package/dist/esm/quickbase.d.ts +8081 -0
- package/dist/esm/quickbase.js +7928 -0
- package/dist/esm/quickbase.js.map +1 -0
- package/dist/esm/quickbase.min.js +2 -0
- package/dist/esm/quickbase.min.js.map +1 -0
- package/dist/umd/quickbase.umd.js +8504 -0
- package/dist/umd/quickbase.umd.js.map +1 -0
- package/dist/umd/quickbase.umd.min.js +2 -0
- package/dist/umd/quickbase.umd.min.js.map +1 -0
- package/docs/.vitepress/cache/deps/@theme_index.js +275 -0
- package/docs/.vitepress/cache/deps/@theme_index.js.map +7 -0
- package/docs/.vitepress/cache/deps/_metadata.json +40 -0
- package/docs/.vitepress/cache/deps/chunk-5A67QEFC.js +12542 -0
- package/docs/.vitepress/cache/deps/chunk-5A67QEFC.js.map +7 -0
- package/docs/.vitepress/cache/deps/chunk-7D4P4MMP.js +9719 -0
- package/docs/.vitepress/cache/deps/chunk-7D4P4MMP.js.map +7 -0
- package/docs/.vitepress/cache/deps/package.json +3 -0
- package/docs/.vitepress/cache/deps/vitepress___@vue_devtools-api.js +4504 -0
- package/docs/.vitepress/cache/deps/vitepress___@vue_devtools-api.js.map +7 -0
- package/docs/.vitepress/cache/deps/vitepress___@vueuse_core.js +583 -0
- package/docs/.vitepress/cache/deps/vitepress___@vueuse_core.js.map +7 -0
- package/docs/.vitepress/cache/deps/vue.js +343 -0
- package/docs/.vitepress/cache/deps/vue.js.map +7 -0
- package/docs/.vitepress/config.mts +26 -0
- package/docs/api-examples.md +49 -0
- package/docs/index.md +59 -0
- package/docs/markdown-examples.md +85 -0
- package/docs-data/api-docs.json +4143 -0
- package/examples/quicbase-jsCDN.html +36 -0
- package/examples/react.js +48 -0
- package/examples/vanillajs.html +47 -0
- package/index.md +25 -0
- package/markdown-examples.md +85 -0
- package/open-api/fix-spec-main.ts +133 -0
- package/open-api/generate-unified-interface.ts +104 -0
- package/open-api/generateDocsJson.ts +24 -0
- package/open-api/output/quickbase-fixed.json +11191 -0
- package/open-api/post-process-generated.ts +226 -0
- package/open-api/regenerate-client.ts +117 -0
- package/open-api/schema/enhance-general.ts +111 -0
- package/open-api/schema/fix-arrays.ts +68 -0
- package/open-api/schema/tags/apps.ts +90 -0
- package/open-api/schema/tags/fields.ts +471 -0
- package/open-api/schema/tags/groups.ts +51 -0
- package/open-api/schema/tags/index.ts +66 -0
- package/open-api/schema/tags/records.ts +115 -0
- package/open-api/schema/tags/tables.ts +211 -0
- package/open-api/schema/tags/users.ts +47 -0
- package/open-api/types/spec.ts +42 -0
- package/open-api/utils/common.ts +29 -0
- package/open-api/utils/generateJsDoc.ts +74 -0
- package/open-api/utils/infer-schema.ts +40 -0
- package/open-api/utils/naming.ts +10 -0
- package/open-api/utils/sharedUtils.ts +352 -0
- package/package.json +96 -0
- package/playwright.config.ts +37 -0
- package/rollup.config.js +114 -0
- package/specs/QuickBase_RESTful_API_2025-03-04T06_22_39.725Z.json +46878 -0
- package/specs/split/apps.json +1315 -0
- package/specs/split/auth.json +191 -0
- package/specs/split/fields.json +3869 -0
- package/specs/split/records.json +754 -0
- package/specs/split/reports.json +619 -0
- package/specs/split/tables.json +1605 -0
- package/src/RateLimitError.ts +12 -0
- package/src/ThrottleBucket.ts +41 -0
- package/src/authorizationStrategy.ts +283 -0
- package/src/generated-unified/QuickbaseClient.ts +1135 -0
- package/src/generated-unified/missing-types-report.json +3 -0
- package/src/index.ts +1 -0
- package/src/invokeMethod.ts +245 -0
- package/src/quickbaseClient.ts +194 -0
- package/src/rateLimiter.ts +25 -0
- package/src/tokenCache.ts +35 -0
- package/src/utils.ts +50 -0
- package/tests/playwright/qb/auth/cacheSwitching.test.ts +159 -0
- package/tests/playwright/qb/auth/fetchTempToken401.test.ts +219 -0
- package/tests/playwright/qb/auth/getAppWithTempToken.test.ts +162 -0
- package/tests/playwright/qb/auth/getAppWithUserToken.test.ts +153 -0
- package/tests/playwright/qb/auth/getTempToken.test.ts +152 -0
- package/tests/playwright/qb/auth/tempTokenPrefetch.test.ts +179 -0
- package/tests/playwright/qb/auth/tempTokenRenewal.test.ts +133 -0
- package/tests/setup.ts +53 -0
- package/tests/vitest/qb/apps/copyApp.test.ts +76 -0
- package/tests/vitest/qb/apps/createApp.test.ts +64 -0
- package/tests/vitest/qb/apps/deleteApp.test.ts +76 -0
- package/tests/vitest/qb/apps/getApp.test.ts +44 -0
- package/tests/vitest/qb/apps/getAppEvents.test.ts +67 -0
- package/tests/vitest/qb/apps/updateApp.test.ts +58 -0
- package/tests/vitest/qb/auth/mockQuickbaseServer.ts +60 -0
- package/tests/vitest/qb/auth/retryUserToken401.test.ts +73 -0
- package/tests/vitest/qb/auth/ssoTokenIntegrationWithServer.test.ts +65 -0
- package/tests/vitest/qb/fields/createField.test.ts +77 -0
- package/tests/vitest/qb/fields/deleteFields.test.ts +83 -0
- package/tests/vitest/qb/fields/getField.test.ts +85 -0
- package/tests/vitest/qb/fields/getFieldUsage.test.ts +82 -0
- package/tests/vitest/qb/fields/getFields.test.ts +232 -0
- package/tests/vitest/qb/fields/getFieldsUsage.test.ts +77 -0
- package/tests/vitest/qb/fields/updateField.test.ts +93 -0
- package/tests/vitest/qb/formulas/runFormula.test.ts +96 -0
- package/tests/vitest/qb/index.ts +29 -0
- package/tests/vitest/qb/records/deleteRecords.test.ts +110 -0
- package/tests/vitest/qb/records/runQuery.test.ts +146 -0
- package/tests/vitest/qb/records/upsert.test.ts +102 -0
- package/tests/vitest/qb/tables/createRelationship.test.ts +102 -0
- package/tests/vitest/qb/tables/createTable.test.ts +55 -0
- package/tests/vitest/qb/tables/deleteRelationship.test.ts +143 -0
- package/tests/vitest/qb/tables/deleteTable.test.ts +50 -0
- package/tests/vitest/qb/tables/getAppTables.test.ts +46 -0
- package/tests/vitest/qb/tables/getRelationships.test.ts +80 -0
- package/tests/vitest/qb/tables/getTable.test.ts +26 -0
- package/tests/vitest/qb/tables/updateRelationship.test.ts +250 -0
- package/tests/vitest/qb/tables/updateTable.test.ts +48 -0
- package/tests/vitest/unit/ThrottleBucket.test.ts +95 -0
- package/tests/vitest/unit/apps/copyApp.test.ts +581 -0
- package/tests/vitest/unit/apps/createApp.test.ts +186 -0
- package/tests/vitest/unit/apps/deleteApp.test.ts +216 -0
- package/tests/vitest/unit/apps/getApp.test.ts +128 -0
- package/tests/vitest/unit/apps/getAppEvents.test.ts +193 -0
- package/tests/vitest/unit/apps/updateApp.test.ts +412 -0
- package/tests/vitest/unit/auth/fetchTempToken401.test.ts +63 -0
- package/tests/vitest/unit/auth/getTempToken.test.ts +119 -0
- package/tests/vitest/unit/auth/retryTempToken401.test.ts +95 -0
- package/tests/vitest/unit/auth/retryTempTokenMaxRetries.test.ts +206 -0
- package/tests/vitest/unit/auth/retryUserToken401.test.ts +69 -0
- package/tests/vitest/unit/auth/ssoTokenRefresh.test.ts +325 -0
- package/tests/vitest/unit/fields/createField.test.ts +451 -0
- package/tests/vitest/unit/fields/deleteFields.test.ts +141 -0
- package/tests/vitest/unit/fields/getField.test.ts +189 -0
- package/tests/vitest/unit/fields/getFieldUsage.test.ts +180 -0
- package/tests/vitest/unit/fields/getFields.test.ts +122 -0
- package/tests/vitest/unit/fields/getFieldsUsage.test.ts +160 -0
- package/tests/vitest/unit/fields/updateField.test.ts +285 -0
- package/tests/vitest/unit/formulas/runFormula.test.ts +205 -0
- package/tests/vitest/unit/index.ts +34 -0
- package/tests/vitest/unit/quickbaseClientHttpMethods.test.ts +139 -0
- package/tests/vitest/unit/rateLimit.test.ts +191 -0
- package/tests/vitest/unit/records/deleteRecords.test.ts +112 -0
- package/tests/vitest/unit/records/runQuery.test.ts +102 -0
- package/tests/vitest/unit/records/upsert.test.ts +234 -0
- package/tests/vitest/unit/tables/createRelationship.test.ts +212 -0
- package/tests/vitest/unit/tables/createTable.test.ts +70 -0
- package/tests/vitest/unit/tables/deleteRelationship.test.ts +97 -0
- package/tests/vitest/unit/tables/deleteTable.test.ts +77 -0
- package/tests/vitest/unit/tables/getAppTables.test.ts +85 -0
- package/tests/vitest/unit/tables/getRelationships.test.ts +259 -0
- package/tests/vitest/unit/tables/getTable.test.ts +233 -0
- package/tests/vitest/unit/tables/updateRelationship.test.ts +307 -0
- package/tests/vitest/unit/tables/updateTable.test.ts +80 -0
- package/tests/vitest/unit/tokenLifespan.test.ts +204 -0
- package/tests/vitest/unit/umd.test.ts +8 -0
- package/tools/log-gen.ts +251 -0
- package/tools/log-gen.yaml +4777 -0
- package/tools/openapi-jars/openapi-generator-cli-7.12.0.jar +0 -0
- package/tsconfig.build.json +17 -0
- package/tsconfig.json +32 -0
- package/versionrc.json +9 -0
- package/vitest.config.ts +27 -0
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { OpenAPIV2 } from "openapi-types";
|
|
4
|
+
import { join } from "path";
|
|
5
|
+
import { existsSync, readdirSync, readFileSync } from "fs";
|
|
6
|
+
import { Project, PropertySignature, ts } from "ts-morph"; // Import ts from ts-morph
|
|
7
|
+
import { simplifyName } from "../../src/utils.ts";
|
|
8
|
+
|
|
9
|
+
// (Existing interfaces remain unchanged)
|
|
10
|
+
export interface PropertyDetail {
|
|
11
|
+
name: string;
|
|
12
|
+
type: string;
|
|
13
|
+
required: boolean;
|
|
14
|
+
jsdoc?: string;
|
|
15
|
+
properties?: PropertyDetail[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface ParamDetail {
|
|
19
|
+
name: string;
|
|
20
|
+
type: string;
|
|
21
|
+
required: boolean;
|
|
22
|
+
properties: PropertyDetail[];
|
|
23
|
+
description?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface JsDocOptions {
|
|
27
|
+
summary: string;
|
|
28
|
+
opId: string;
|
|
29
|
+
paramDetails: ParamDetail[];
|
|
30
|
+
returnType: string;
|
|
31
|
+
returnTypeDetails: PropertyDetail[];
|
|
32
|
+
docLink: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function mapOpenApiTypeToTs(
|
|
36
|
+
openApiType: string | string[] | undefined
|
|
37
|
+
): string {
|
|
38
|
+
const type = Array.isArray(openApiType)
|
|
39
|
+
? openApiType[0]
|
|
40
|
+
: openApiType || "any";
|
|
41
|
+
switch (type.toLowerCase()) {
|
|
42
|
+
case "integer":
|
|
43
|
+
case "int":
|
|
44
|
+
case "number":
|
|
45
|
+
return "number";
|
|
46
|
+
case "string":
|
|
47
|
+
return "string";
|
|
48
|
+
case "boolean":
|
|
49
|
+
return "boolean";
|
|
50
|
+
default:
|
|
51
|
+
return "any";
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function mapRefToType(
|
|
56
|
+
schema: OpenAPIV2.SchemaObject | OpenAPIV2.ReferenceObject | undefined,
|
|
57
|
+
modelImports: Set<string>,
|
|
58
|
+
spec: OpenAPIV2.Document,
|
|
59
|
+
depth: number = 0,
|
|
60
|
+
availableModels: string[],
|
|
61
|
+
missingTypes: Set<string>
|
|
62
|
+
): string {
|
|
63
|
+
if (!schema) return "any";
|
|
64
|
+
if ("$ref" in schema && schema.$ref) {
|
|
65
|
+
const refParts = schema.$ref.split("/");
|
|
66
|
+
const model = refParts[refParts.length - 1];
|
|
67
|
+
const pascalModel = model.charAt(0).toUpperCase() + model.slice(1);
|
|
68
|
+
if (availableModels.includes(pascalModel)) {
|
|
69
|
+
modelImports.add(pascalModel);
|
|
70
|
+
return pascalModel;
|
|
71
|
+
}
|
|
72
|
+
missingTypes.add(pascalModel);
|
|
73
|
+
console.warn(`Type ${pascalModel} not found, defaulting to 'any'`);
|
|
74
|
+
return "any";
|
|
75
|
+
}
|
|
76
|
+
if ("type" in schema) {
|
|
77
|
+
if (schema.type === "object" && schema.properties) {
|
|
78
|
+
const props = schema.properties;
|
|
79
|
+
const propTypes = Object.entries(props).map(([key, prop]) => {
|
|
80
|
+
const propSchema = prop as OpenAPIV2.SchemaObject;
|
|
81
|
+
const propType = mapRefToType(
|
|
82
|
+
propSchema,
|
|
83
|
+
modelImports,
|
|
84
|
+
spec,
|
|
85
|
+
depth + 1,
|
|
86
|
+
availableModels,
|
|
87
|
+
missingTypes
|
|
88
|
+
);
|
|
89
|
+
return `${key}${propSchema.required ? "" : "?"}: ${propType}`;
|
|
90
|
+
});
|
|
91
|
+
return `{ ${propTypes.join("; ")} }`;
|
|
92
|
+
}
|
|
93
|
+
if (schema.type === "array" && schema.items) {
|
|
94
|
+
const items = schema.items as
|
|
95
|
+
| OpenAPIV2.SchemaObject
|
|
96
|
+
| OpenAPIV2.ReferenceObject;
|
|
97
|
+
const itemType = mapRefToType(
|
|
98
|
+
items,
|
|
99
|
+
modelImports,
|
|
100
|
+
spec,
|
|
101
|
+
depth + 1,
|
|
102
|
+
availableModels,
|
|
103
|
+
missingTypes
|
|
104
|
+
);
|
|
105
|
+
return `${itemType}[]`;
|
|
106
|
+
}
|
|
107
|
+
return mapOpenApiTypeToTs(schema.type);
|
|
108
|
+
}
|
|
109
|
+
return "any";
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function parseInterfaceProperties(
|
|
113
|
+
modelName: string,
|
|
114
|
+
modelsDir: string,
|
|
115
|
+
availableModels?: string[],
|
|
116
|
+
depth: number = 0,
|
|
117
|
+
visited: Set<string> = new Set()
|
|
118
|
+
): PropertyDetail[] {
|
|
119
|
+
if (availableModels && (depth > 10 || visited.has(modelName))) {
|
|
120
|
+
console.warn(
|
|
121
|
+
`Recursion limit reached or circular reference detected for ${modelName}`
|
|
122
|
+
);
|
|
123
|
+
return [];
|
|
124
|
+
}
|
|
125
|
+
if (availableModels) visited.add(modelName);
|
|
126
|
+
|
|
127
|
+
const project = new Project();
|
|
128
|
+
const filePath = join(modelsDir, `${modelName}.ts`);
|
|
129
|
+
if (!existsSync(filePath)) {
|
|
130
|
+
console.warn(`Model file ${filePath} not found for ${modelName}`);
|
|
131
|
+
return [];
|
|
132
|
+
}
|
|
133
|
+
const sourceFile = project.addSourceFileAtPath(filePath);
|
|
134
|
+
|
|
135
|
+
// Try to get the interface first
|
|
136
|
+
let interfaceDec = sourceFile.getInterface(modelName);
|
|
137
|
+
if (!interfaceDec) {
|
|
138
|
+
// If no interface, check for a type alias (e.g., array type)
|
|
139
|
+
const typeAlias = sourceFile.getTypeAlias(modelName);
|
|
140
|
+
if (typeAlias) {
|
|
141
|
+
const typeNode = typeAlias.getTypeNode();
|
|
142
|
+
if (typeNode && typeNode.getKindName() === "ArrayType") {
|
|
143
|
+
const elementType = typeNode.getFirstChildByKindOrThrow(
|
|
144
|
+
ts.SyntaxKind.TypeReference // Use ts.SyntaxKind from ts-morph
|
|
145
|
+
);
|
|
146
|
+
const innerType = elementType.getText(); // e.g., "GetFields200ResponseItemsInner"
|
|
147
|
+
if (availableModels?.includes(innerType)) {
|
|
148
|
+
console.log(
|
|
149
|
+
`Detected array type alias ${modelName}, parsing inner type ${innerType}`
|
|
150
|
+
);
|
|
151
|
+
// Recursively parse the inner interface
|
|
152
|
+
return parseInterfaceProperties(
|
|
153
|
+
innerType,
|
|
154
|
+
modelsDir,
|
|
155
|
+
availableModels,
|
|
156
|
+
depth + 1,
|
|
157
|
+
visited
|
|
158
|
+
);
|
|
159
|
+
} else {
|
|
160
|
+
console.warn(
|
|
161
|
+
`Inner type ${innerType} not found in available models for ${modelName}`
|
|
162
|
+
);
|
|
163
|
+
return [];
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
console.warn(
|
|
168
|
+
`Interface or supported type alias ${modelName} not found in ${filePath}`
|
|
169
|
+
);
|
|
170
|
+
return [];
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Parse properties of the interface
|
|
174
|
+
return interfaceDec.getProperties().map((prop: PropertySignature) => {
|
|
175
|
+
const jsDocs = prop.getJsDocs();
|
|
176
|
+
const jsdocText =
|
|
177
|
+
jsDocs.length > 0 ? jsDocs[0].getDescription().trim() : undefined;
|
|
178
|
+
const propType = prop.getType().getText(prop);
|
|
179
|
+
let properties: PropertyDetail[] | undefined = undefined;
|
|
180
|
+
|
|
181
|
+
if (availableModels) {
|
|
182
|
+
const arrayMatch = propType.match(/(.+)\[\]$/);
|
|
183
|
+
if (arrayMatch) {
|
|
184
|
+
const innerType = arrayMatch[1].trim();
|
|
185
|
+
if (availableModels.includes(innerType) && !innerType.includes("[]")) {
|
|
186
|
+
properties = parseInterfaceProperties(
|
|
187
|
+
innerType,
|
|
188
|
+
modelsDir,
|
|
189
|
+
availableModels,
|
|
190
|
+
depth + 1,
|
|
191
|
+
visited
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return {
|
|
198
|
+
name: prop.getName(),
|
|
199
|
+
type: propType,
|
|
200
|
+
required: !prop.hasQuestionToken(),
|
|
201
|
+
jsdoc: jsdocText,
|
|
202
|
+
properties: properties && properties.length > 0 ? properties : undefined,
|
|
203
|
+
};
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export interface OperationDoc {
|
|
208
|
+
name: string;
|
|
209
|
+
summary: string;
|
|
210
|
+
method: string;
|
|
211
|
+
path: string;
|
|
212
|
+
parameters: ParamDetail[];
|
|
213
|
+
returns: string;
|
|
214
|
+
returnTypeDetails: PropertyDetail[] | undefined;
|
|
215
|
+
docLink: string;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export function parseOpenApiOperations(
|
|
219
|
+
specFile: string,
|
|
220
|
+
modelsDir: string
|
|
221
|
+
): {
|
|
222
|
+
operations: OperationDoc[];
|
|
223
|
+
modelImports: Set<string>;
|
|
224
|
+
missingTypes: Set<string>;
|
|
225
|
+
} {
|
|
226
|
+
if (!existsSync(specFile)) {
|
|
227
|
+
throw new Error(
|
|
228
|
+
`Spec file ${specFile} not found. Run 'npm run fix-spec' first.`
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
const spec: OpenAPIV2.Document = JSON.parse(readFileSync(specFile, "utf8"));
|
|
232
|
+
const availableModels = readdirSync(modelsDir)
|
|
233
|
+
.filter((file) => file.endsWith(".ts") && !file.startsWith("index"))
|
|
234
|
+
.map((file) => file.replace(".ts", ""));
|
|
235
|
+
const modelImports = new Set<string>();
|
|
236
|
+
const missingTypes = new Set<string>();
|
|
237
|
+
const operations: OperationDoc[] = [];
|
|
238
|
+
for (const [path, methodsObj] of Object.entries(
|
|
239
|
+
spec.paths as OpenAPIV2.PathsObject
|
|
240
|
+
)) {
|
|
241
|
+
if (!methodsObj) continue;
|
|
242
|
+
for (const [method, operation] of Object.entries(
|
|
243
|
+
methodsObj as OpenAPIV2.PathItemObject
|
|
244
|
+
)) {
|
|
245
|
+
const op = operation as OpenAPIV2.OperationObject | undefined;
|
|
246
|
+
if (!op || !op.operationId) continue;
|
|
247
|
+
const opId = simplifyName(op.operationId);
|
|
248
|
+
console.log(`Processing operation ${opId} (${method} ${path})`);
|
|
249
|
+
const paramDetails = (op.parameters || [])
|
|
250
|
+
.filter(
|
|
251
|
+
(p) =>
|
|
252
|
+
!["QB-Realm-Hostname", "Authorization", "User-Agent"].includes(
|
|
253
|
+
(p as OpenAPIV2.ParameterObject).name || ""
|
|
254
|
+
)
|
|
255
|
+
)
|
|
256
|
+
.map((p) => {
|
|
257
|
+
const param = p as OpenAPIV2.ParameterObject;
|
|
258
|
+
let type = "any";
|
|
259
|
+
let properties: PropertyDetail[] | undefined = undefined;
|
|
260
|
+
if ("schema" in p && p.schema) {
|
|
261
|
+
type = mapRefToType(
|
|
262
|
+
p.schema,
|
|
263
|
+
modelImports,
|
|
264
|
+
spec,
|
|
265
|
+
1,
|
|
266
|
+
availableModels,
|
|
267
|
+
missingTypes
|
|
268
|
+
);
|
|
269
|
+
if ("$ref" in p.schema && p.schema.$ref) {
|
|
270
|
+
const refParts = p.schema.$ref.split("/");
|
|
271
|
+
const model = refParts[refParts.length - 1];
|
|
272
|
+
const pascalModel =
|
|
273
|
+
model.charAt(0).toUpperCase() + model.slice(1);
|
|
274
|
+
properties = parseInterfaceProperties(
|
|
275
|
+
pascalModel,
|
|
276
|
+
modelsDir,
|
|
277
|
+
availableModels
|
|
278
|
+
);
|
|
279
|
+
} else if (type !== "any" && availableModels.includes(type)) {
|
|
280
|
+
properties = parseInterfaceProperties(
|
|
281
|
+
type,
|
|
282
|
+
modelsDir,
|
|
283
|
+
availableModels
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
} else if ("type" in p) {
|
|
287
|
+
type = mapOpenApiTypeToTs(p.type);
|
|
288
|
+
}
|
|
289
|
+
return {
|
|
290
|
+
name: param.in === "body" ? "body" : param.name,
|
|
291
|
+
type,
|
|
292
|
+
required: param.required || false,
|
|
293
|
+
description: param.description || "",
|
|
294
|
+
properties: properties || [],
|
|
295
|
+
};
|
|
296
|
+
});
|
|
297
|
+
const returnTypes = ["200", "207"]
|
|
298
|
+
.map(
|
|
299
|
+
(code) => (op.responses?.[code] as OpenAPIV2.ResponseObject)?.schema
|
|
300
|
+
)
|
|
301
|
+
.filter(Boolean)
|
|
302
|
+
.map((schema) =>
|
|
303
|
+
mapRefToType(
|
|
304
|
+
schema!,
|
|
305
|
+
modelImports,
|
|
306
|
+
spec,
|
|
307
|
+
1,
|
|
308
|
+
availableModels,
|
|
309
|
+
missingTypes
|
|
310
|
+
)
|
|
311
|
+
);
|
|
312
|
+
const returnType =
|
|
313
|
+
returnTypes.length > 1
|
|
314
|
+
? returnTypes.join(" | ")
|
|
315
|
+
: returnTypes[0] || "void";
|
|
316
|
+
const returnTypeDetailsRaw = returnTypes
|
|
317
|
+
.filter((type) => type !== "void" && availableModels.includes(type))
|
|
318
|
+
.map((type) =>
|
|
319
|
+
parseInterfaceProperties(type, modelsDir, availableModels)
|
|
320
|
+
)
|
|
321
|
+
.flat();
|
|
322
|
+
const returnTypeDetails =
|
|
323
|
+
returnTypeDetailsRaw.length > 0
|
|
324
|
+
? returnTypeDetailsRaw.map((prop) => ({
|
|
325
|
+
...prop,
|
|
326
|
+
properties:
|
|
327
|
+
prop.properties && prop.properties.length > 0
|
|
328
|
+
? [...prop.properties]
|
|
329
|
+
: undefined,
|
|
330
|
+
}))
|
|
331
|
+
: undefined;
|
|
332
|
+
operations.push({
|
|
333
|
+
name: opId,
|
|
334
|
+
summary: op.summary || "No description.",
|
|
335
|
+
method: method.toUpperCase(),
|
|
336
|
+
path,
|
|
337
|
+
parameters: paramDetails,
|
|
338
|
+
returns: returnType,
|
|
339
|
+
returnTypeDetails,
|
|
340
|
+
docLink: `https://developer.quickbase.com/operation/${op.operationId}`,
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
return { operations, modelImports, missingTypes };
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// New shared function
|
|
348
|
+
export function getPropertyDescription(prop: PropertyDetail): string {
|
|
349
|
+
return prop.jsdoc
|
|
350
|
+
? prop.jsdoc.replace(/@type\s*{[^}]+}\s*@memberof\s*\w+/, "").trim()
|
|
351
|
+
: `Type: ${prop.type}`;
|
|
352
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "quickbase-js",
|
|
3
|
+
"version": "0.1.0-beta.10",
|
|
4
|
+
"description": "QuickBase JavaScript client for frameworks and vanilla JS",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/esm/quickbase.js",
|
|
7
|
+
"module": "dist/esm/quickbase.js",
|
|
8
|
+
"types": "dist/esm/quickbase.d.ts",
|
|
9
|
+
"publishConfig": {
|
|
10
|
+
"access": "public"
|
|
11
|
+
},
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/esm/quickbase.d.ts",
|
|
15
|
+
"import": {
|
|
16
|
+
"minified": "./dist/esm/quickbase.min.js",
|
|
17
|
+
"default": "./dist/esm/quickbase.js"
|
|
18
|
+
},
|
|
19
|
+
"require": {
|
|
20
|
+
"minified": "./dist/umd/quickbase.umd.min.js",
|
|
21
|
+
"default": "./dist/umd/quickbase.umd.js"
|
|
22
|
+
},
|
|
23
|
+
"umd:types": "./dist/umd/quickbase.umd.d.ts"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "npm run build:esm && npm run build:umd",
|
|
28
|
+
"build:esm": "rimraf dist/esm dist/temp dist/temp-src && node --import 'data:text/javascript,import { register } from \"node:module\"; import { pathToFileURL } from \"node:url\"; register(\"ts-node/esm\", pathToFileURL(\"./\"));' build-esm.js",
|
|
29
|
+
"build:umd": "rimraf dist/umd dist/temp dist/temp-src && node --import 'data:text/javascript,import { register } from \"node:module\"; import { pathToFileURL } from \"node:url\"; register(\"ts-node/esm\", pathToFileURL(\"./\"));' build-umd.js",
|
|
30
|
+
"release": "standard-version",
|
|
31
|
+
"release:beta": "standard-version --prerelease beta",
|
|
32
|
+
"release:alpha": "standard-version --prerelease alpha",
|
|
33
|
+
"release:push": "npm run release:beta && git push --follow-tags origin main",
|
|
34
|
+
"release:publish": "npm run release:push && npm publish --tag beta",
|
|
35
|
+
"release:stable": "standard-version && git push --follow-tags origin main && npm publish",
|
|
36
|
+
"gen:fixspec": "node --import 'data:text/javascript,import { register } from \"node:module\"; import { pathToFileURL } from \"node:url\"; register(\"ts-node/esm\", pathToFileURL(\"./\"));' open-api/fix-spec-main.ts",
|
|
37
|
+
"gen:openapi": "rimraf src/generated && node --import 'data:text/javascript,import { register } from \"node:module\"; import { pathToFileURL } from \"node:url\"; register(\"ts-node/esm\", pathToFileURL(\"./\"));' open-api/regenerate-client.ts",
|
|
38
|
+
"gen:openapi:post": "node --import 'data:text/javascript,import { register } from \"node:module\"; import { pathToFileURL } from \"node:url\"; register(\"ts-node/esm\", pathToFileURL(\"./\"));' open-api/post-process-generated.ts",
|
|
39
|
+
"gen:unified": "node --import 'data:text/javascript,import { register } from \"node:module\"; import { pathToFileURL } from \"node:url\"; register(\"ts-node/esm\", pathToFileURL(\"./\"));' open-api/generate-unified-interface.ts",
|
|
40
|
+
"gen:docsdata": "node --import 'data:text/javascript,import { register } from \"node:module\"; import { pathToFileURL } from \"node:url\"; register(\"ts-node/esm\", pathToFileURL(\"./\"));' open-api/generateDocsJson.ts",
|
|
41
|
+
"docs:dev": "vitepress dev docs",
|
|
42
|
+
"docs:build": "vitepress build docs",
|
|
43
|
+
"docs:preview": "vitepress preview docs",
|
|
44
|
+
"gen:all": "npm run gen:fixspec && npm run gen:openapi && npm run gen:openapi:post && npm run gen:unified && npm run gen:docsdata && npm run docs:build",
|
|
45
|
+
"log-gen": "node --import 'data:text/javascript,import { register } from \"node:module\"; import { pathToFileURL } from \"node:url\"; register(\"ts-node/esm\", pathToFileURL(\"./\"));' tools/log-gen.ts",
|
|
46
|
+
"test:all": "vitest run tests/vitest/**/**/*.test.ts --reporter=basic && playwright test tests/playwright/qb --config playwright.config.ts --reporter=dot --quiet",
|
|
47
|
+
"test:pw:qb:all": "playwright test tests/playwright/qb --config playwright.config.ts",
|
|
48
|
+
"test:vitest:all": "vitest run tests/vitest/**/**/*.test.ts",
|
|
49
|
+
"test:vitest:qb": "vitest run tests/vitest/qb/**/*.test.ts",
|
|
50
|
+
"test:vitest:unit": "vitest run tests/vitest/unit/**/*.test.ts",
|
|
51
|
+
"test:vitest:qb:apps": "vitest run tests/vitest/qb/apps/*.test.ts",
|
|
52
|
+
"test:vitest:qb:auth": "vitest run tests/vitest/qb/auth/*.test.ts",
|
|
53
|
+
"test:vitest:qb:fields": "vitest run tests/vitest/qb/fields/*.test.ts",
|
|
54
|
+
"test:vitest:qb:records": "vitest run tests/vitest/qb/records/*.test.ts",
|
|
55
|
+
"test:vitest:qb:tables": "vitest run tests/vitest/qb/tables/*.test.ts",
|
|
56
|
+
"test:vitest:unit:apps": "vitest run tests/vitest/unit/apps/*.test.ts",
|
|
57
|
+
"test:vitest:unit:auth": "vitest run tests/vitest/unit/auth/*.test.ts",
|
|
58
|
+
"test:vitest:unit:fields": "vitest run tests/vitest/unit/fields/*.test.ts",
|
|
59
|
+
"test:vitest:unit:formulas": "vitest run tests/vitest/unit/formulas/*.test.ts",
|
|
60
|
+
"test:vitest:unit:records": "vitest run tests/vitest/unit/records/*.test.ts",
|
|
61
|
+
"test:vitest:unit:tables": "vitest run tests/vitest/unit/tables/*.test.ts",
|
|
62
|
+
"test:pw:qb:auth": "playwright test tests/playwright/qb/auth"
|
|
63
|
+
},
|
|
64
|
+
"devDependencies": {
|
|
65
|
+
"@openapitools/openapi-generator-cli": "^2.18.4",
|
|
66
|
+
"@playwright/test": "^1.51.0",
|
|
67
|
+
"@rollup/plugin-commonjs": "^28.0.3",
|
|
68
|
+
"@rollup/plugin-node-resolve": "^16.0.1",
|
|
69
|
+
"@rollup/plugin-terser": "^0.4.4",
|
|
70
|
+
"@rollup/plugin-typescript": "^12.1.2",
|
|
71
|
+
"@types/fast-json-patch": "^1.0.4",
|
|
72
|
+
"@types/js-yaml": "^4.0.9",
|
|
73
|
+
"@types/node": "^22.13.10",
|
|
74
|
+
"dotenv": "^16.4.7",
|
|
75
|
+
"express": "^4.21.2",
|
|
76
|
+
"fast-json-patch": "^3.1.1",
|
|
77
|
+
"glob": "^10.4.5",
|
|
78
|
+
"js-yaml": "^4.1.0",
|
|
79
|
+
"jsdom": "^26.0.0",
|
|
80
|
+
"node-fetch": "^3.3.2",
|
|
81
|
+
"openapi-schema-validator": "^12.1.3",
|
|
82
|
+
"openapi-types": "^12.1.3",
|
|
83
|
+
"openapi-typescript": "^7.6.1",
|
|
84
|
+
"rimraf": "^5.0.10",
|
|
85
|
+
"rollup": "^4.35.0",
|
|
86
|
+
"rollup-plugin-dts": "^6.1.1",
|
|
87
|
+
"standard-version": "^9.5.0",
|
|
88
|
+
"ts-morph": "^25.0.1",
|
|
89
|
+
"ts-node": "^10.9.2",
|
|
90
|
+
"tslib": "^2.8.1",
|
|
91
|
+
"typescript": "^5.8.2",
|
|
92
|
+
"undici-types": "^7.5.0",
|
|
93
|
+
"vitepress": "^1.6.3",
|
|
94
|
+
"vitest": "^3.0.8"
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// playwright.config.ts
|
|
2
|
+
import { defineConfig, devices } from "@playwright/test";
|
|
3
|
+
|
|
4
|
+
const config = defineConfig({
|
|
5
|
+
fullyParallel: true,
|
|
6
|
+
workers: undefined, // Default: half of CPU cores (e.g., 2 on a 4-core machine)
|
|
7
|
+
// workers: 2, // Or explicitly set to 2 for controlled testing
|
|
8
|
+
testDir: "./tests/playwright",
|
|
9
|
+
timeout: 600000, // 10 minutes
|
|
10
|
+
retries: 2,
|
|
11
|
+
reporter: [
|
|
12
|
+
["list"],
|
|
13
|
+
["json", { outputFile: "test-artifacts/test-results.json" }],
|
|
14
|
+
],
|
|
15
|
+
use: {
|
|
16
|
+
baseURL: "https://api.quickbase.com",
|
|
17
|
+
headless: true,
|
|
18
|
+
viewport: { width: 1280, height: 720 },
|
|
19
|
+
actionTimeout: 10000,
|
|
20
|
+
screenshot: "off",
|
|
21
|
+
},
|
|
22
|
+
outputDir: "test-artifacts",
|
|
23
|
+
projects: [
|
|
24
|
+
{
|
|
25
|
+
name: "chromium",
|
|
26
|
+
use: { ...devices["Desktop Chrome"] },
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
console.log(
|
|
32
|
+
`Loading Playwright: Testing temp token authorization in browser. Be patient, this could take at least 5min. Workers: ${
|
|
33
|
+
config.workers || "default (half CPU cores)"
|
|
34
|
+
}`
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
export default config;
|
package/rollup.config.js
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import terser from "@rollup/plugin-terser";
|
|
2
|
+
import nodeResolve from "@rollup/plugin-node-resolve";
|
|
3
|
+
import commonjs from "@rollup/plugin-commonjs";
|
|
4
|
+
import dts from "rollup-plugin-dts";
|
|
5
|
+
import typescript from "@rollup/plugin-typescript";
|
|
6
|
+
|
|
7
|
+
const isProd = process.env.NODE_ENV === "production";
|
|
8
|
+
|
|
9
|
+
// External dependencies for both ESM and UMD builds
|
|
10
|
+
const external = [
|
|
11
|
+
"node:http",
|
|
12
|
+
"node:https",
|
|
13
|
+
"node:zlib",
|
|
14
|
+
"node:stream",
|
|
15
|
+
"node:buffer",
|
|
16
|
+
"node:util",
|
|
17
|
+
"node:url",
|
|
18
|
+
"node:net",
|
|
19
|
+
"node:fs",
|
|
20
|
+
"node:path",
|
|
21
|
+
"node-fetch",
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
// Global mappings for UMD (browser) build
|
|
25
|
+
const globals = {
|
|
26
|
+
"node-fetch": "fetch", // Map node-fetch to window.fetch in browsers
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export default [
|
|
30
|
+
// ESM Build
|
|
31
|
+
{
|
|
32
|
+
input: "src/quickbaseClient.ts",
|
|
33
|
+
output: {
|
|
34
|
+
dir: "dist/esm",
|
|
35
|
+
format: "esm",
|
|
36
|
+
sourcemap: true,
|
|
37
|
+
entryFileNames: "quickbase.js",
|
|
38
|
+
},
|
|
39
|
+
external,
|
|
40
|
+
plugins: [
|
|
41
|
+
nodeResolve({ preferBuiltins: true }), // Node.js resolution
|
|
42
|
+
commonjs(), // Convert CommonJS to ESM
|
|
43
|
+
typescript({
|
|
44
|
+
tsconfig: "./tsconfig.json",
|
|
45
|
+
noEmitOnError: true, // Fail on TypeScript errors
|
|
46
|
+
// Use Rollup to emit JavaScript, overriding noEmit for this build
|
|
47
|
+
}),
|
|
48
|
+
isProd && terser(), // Minification for production
|
|
49
|
+
],
|
|
50
|
+
},
|
|
51
|
+
// UMD Build
|
|
52
|
+
{
|
|
53
|
+
input: "src/quickbaseClient.ts",
|
|
54
|
+
output: {
|
|
55
|
+
file: "dist/umd/quickbase.umd.js",
|
|
56
|
+
format: "umd",
|
|
57
|
+
name: "QuickbaseJS", // Global name for browser
|
|
58
|
+
sourcemap: true,
|
|
59
|
+
globals,
|
|
60
|
+
},
|
|
61
|
+
external,
|
|
62
|
+
plugins: [
|
|
63
|
+
nodeResolve({ preferBuiltins: true, browser: true }), // Browser-first resolution
|
|
64
|
+
commonjs(), // Convert CommonJS to UMD
|
|
65
|
+
typescript({
|
|
66
|
+
tsconfig: "./tsconfig.json",
|
|
67
|
+
noEmitOnError: true, // Fail on TypeScript errors
|
|
68
|
+
// Use Rollup to emit JavaScript, overriding noEmit for this build
|
|
69
|
+
}),
|
|
70
|
+
isProd && terser(), // Minification for production
|
|
71
|
+
],
|
|
72
|
+
},
|
|
73
|
+
// ESM Declarations
|
|
74
|
+
{
|
|
75
|
+
input: "src/quickbaseClient.ts",
|
|
76
|
+
output: {
|
|
77
|
+
file: "dist/esm/quickbase.d.ts",
|
|
78
|
+
format: "esm",
|
|
79
|
+
},
|
|
80
|
+
plugins: [
|
|
81
|
+
dts({
|
|
82
|
+
tsconfig: "./tsconfig.json",
|
|
83
|
+
respectExternal: true, // Include external dependencies in declarations
|
|
84
|
+
}),
|
|
85
|
+
],
|
|
86
|
+
},
|
|
87
|
+
// UMD Declarations
|
|
88
|
+
{
|
|
89
|
+
input: "src/quickbaseClient.ts",
|
|
90
|
+
output: {
|
|
91
|
+
file: "dist/umd/quickbase.umd.d.ts",
|
|
92
|
+
format: "umd",
|
|
93
|
+
},
|
|
94
|
+
plugins: [
|
|
95
|
+
dts({
|
|
96
|
+
tsconfig: "./tsconfig.json",
|
|
97
|
+
respectExternal: true, // Include external dependencies in declarations
|
|
98
|
+
}),
|
|
99
|
+
],
|
|
100
|
+
},
|
|
101
|
+
].map((config) => ({
|
|
102
|
+
...config,
|
|
103
|
+
onwarn(warning, warn) {
|
|
104
|
+
// Suppress TypeScript plugin warnings
|
|
105
|
+
if (
|
|
106
|
+
warning.code === "PLUGIN_WARNING" &&
|
|
107
|
+
warning.plugin === "typescript" &&
|
|
108
|
+
warning.message.includes("TS5096")
|
|
109
|
+
) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
warn(warning);
|
|
113
|
+
},
|
|
114
|
+
}));
|