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.
Files changed (165) hide show
  1. package/CHANGELOG.md +211 -0
  2. package/LICENSE +21 -0
  3. package/README.md +179 -0
  4. package/api-examples.md +49 -0
  5. package/build-common.js +131 -0
  6. package/build-esm.js +71 -0
  7. package/build-umd.js +69 -0
  8. package/dist/esm/quickbase.d.ts +8081 -0
  9. package/dist/esm/quickbase.js +7928 -0
  10. package/dist/esm/quickbase.js.map +1 -0
  11. package/dist/esm/quickbase.min.js +2 -0
  12. package/dist/esm/quickbase.min.js.map +1 -0
  13. package/dist/umd/quickbase.umd.js +8504 -0
  14. package/dist/umd/quickbase.umd.js.map +1 -0
  15. package/dist/umd/quickbase.umd.min.js +2 -0
  16. package/dist/umd/quickbase.umd.min.js.map +1 -0
  17. package/docs/.vitepress/cache/deps/@theme_index.js +275 -0
  18. package/docs/.vitepress/cache/deps/@theme_index.js.map +7 -0
  19. package/docs/.vitepress/cache/deps/_metadata.json +40 -0
  20. package/docs/.vitepress/cache/deps/chunk-5A67QEFC.js +12542 -0
  21. package/docs/.vitepress/cache/deps/chunk-5A67QEFC.js.map +7 -0
  22. package/docs/.vitepress/cache/deps/chunk-7D4P4MMP.js +9719 -0
  23. package/docs/.vitepress/cache/deps/chunk-7D4P4MMP.js.map +7 -0
  24. package/docs/.vitepress/cache/deps/package.json +3 -0
  25. package/docs/.vitepress/cache/deps/vitepress___@vue_devtools-api.js +4504 -0
  26. package/docs/.vitepress/cache/deps/vitepress___@vue_devtools-api.js.map +7 -0
  27. package/docs/.vitepress/cache/deps/vitepress___@vueuse_core.js +583 -0
  28. package/docs/.vitepress/cache/deps/vitepress___@vueuse_core.js.map +7 -0
  29. package/docs/.vitepress/cache/deps/vue.js +343 -0
  30. package/docs/.vitepress/cache/deps/vue.js.map +7 -0
  31. package/docs/.vitepress/config.mts +26 -0
  32. package/docs/api-examples.md +49 -0
  33. package/docs/index.md +59 -0
  34. package/docs/markdown-examples.md +85 -0
  35. package/docs-data/api-docs.json +4143 -0
  36. package/examples/quicbase-jsCDN.html +36 -0
  37. package/examples/react.js +48 -0
  38. package/examples/vanillajs.html +47 -0
  39. package/index.md +25 -0
  40. package/markdown-examples.md +85 -0
  41. package/open-api/fix-spec-main.ts +133 -0
  42. package/open-api/generate-unified-interface.ts +104 -0
  43. package/open-api/generateDocsJson.ts +24 -0
  44. package/open-api/output/quickbase-fixed.json +11191 -0
  45. package/open-api/post-process-generated.ts +226 -0
  46. package/open-api/regenerate-client.ts +117 -0
  47. package/open-api/schema/enhance-general.ts +111 -0
  48. package/open-api/schema/fix-arrays.ts +68 -0
  49. package/open-api/schema/tags/apps.ts +90 -0
  50. package/open-api/schema/tags/fields.ts +471 -0
  51. package/open-api/schema/tags/groups.ts +51 -0
  52. package/open-api/schema/tags/index.ts +66 -0
  53. package/open-api/schema/tags/records.ts +115 -0
  54. package/open-api/schema/tags/tables.ts +211 -0
  55. package/open-api/schema/tags/users.ts +47 -0
  56. package/open-api/types/spec.ts +42 -0
  57. package/open-api/utils/common.ts +29 -0
  58. package/open-api/utils/generateJsDoc.ts +74 -0
  59. package/open-api/utils/infer-schema.ts +40 -0
  60. package/open-api/utils/naming.ts +10 -0
  61. package/open-api/utils/sharedUtils.ts +352 -0
  62. package/package.json +96 -0
  63. package/playwright.config.ts +37 -0
  64. package/rollup.config.js +114 -0
  65. package/specs/QuickBase_RESTful_API_2025-03-04T06_22_39.725Z.json +46878 -0
  66. package/specs/split/apps.json +1315 -0
  67. package/specs/split/auth.json +191 -0
  68. package/specs/split/fields.json +3869 -0
  69. package/specs/split/records.json +754 -0
  70. package/specs/split/reports.json +619 -0
  71. package/specs/split/tables.json +1605 -0
  72. package/src/RateLimitError.ts +12 -0
  73. package/src/ThrottleBucket.ts +41 -0
  74. package/src/authorizationStrategy.ts +283 -0
  75. package/src/generated-unified/QuickbaseClient.ts +1135 -0
  76. package/src/generated-unified/missing-types-report.json +3 -0
  77. package/src/index.ts +1 -0
  78. package/src/invokeMethod.ts +245 -0
  79. package/src/quickbaseClient.ts +194 -0
  80. package/src/rateLimiter.ts +25 -0
  81. package/src/tokenCache.ts +35 -0
  82. package/src/utils.ts +50 -0
  83. package/tests/playwright/qb/auth/cacheSwitching.test.ts +159 -0
  84. package/tests/playwright/qb/auth/fetchTempToken401.test.ts +219 -0
  85. package/tests/playwright/qb/auth/getAppWithTempToken.test.ts +162 -0
  86. package/tests/playwright/qb/auth/getAppWithUserToken.test.ts +153 -0
  87. package/tests/playwright/qb/auth/getTempToken.test.ts +152 -0
  88. package/tests/playwright/qb/auth/tempTokenPrefetch.test.ts +179 -0
  89. package/tests/playwright/qb/auth/tempTokenRenewal.test.ts +133 -0
  90. package/tests/setup.ts +53 -0
  91. package/tests/vitest/qb/apps/copyApp.test.ts +76 -0
  92. package/tests/vitest/qb/apps/createApp.test.ts +64 -0
  93. package/tests/vitest/qb/apps/deleteApp.test.ts +76 -0
  94. package/tests/vitest/qb/apps/getApp.test.ts +44 -0
  95. package/tests/vitest/qb/apps/getAppEvents.test.ts +67 -0
  96. package/tests/vitest/qb/apps/updateApp.test.ts +58 -0
  97. package/tests/vitest/qb/auth/mockQuickbaseServer.ts +60 -0
  98. package/tests/vitest/qb/auth/retryUserToken401.test.ts +73 -0
  99. package/tests/vitest/qb/auth/ssoTokenIntegrationWithServer.test.ts +65 -0
  100. package/tests/vitest/qb/fields/createField.test.ts +77 -0
  101. package/tests/vitest/qb/fields/deleteFields.test.ts +83 -0
  102. package/tests/vitest/qb/fields/getField.test.ts +85 -0
  103. package/tests/vitest/qb/fields/getFieldUsage.test.ts +82 -0
  104. package/tests/vitest/qb/fields/getFields.test.ts +232 -0
  105. package/tests/vitest/qb/fields/getFieldsUsage.test.ts +77 -0
  106. package/tests/vitest/qb/fields/updateField.test.ts +93 -0
  107. package/tests/vitest/qb/formulas/runFormula.test.ts +96 -0
  108. package/tests/vitest/qb/index.ts +29 -0
  109. package/tests/vitest/qb/records/deleteRecords.test.ts +110 -0
  110. package/tests/vitest/qb/records/runQuery.test.ts +146 -0
  111. package/tests/vitest/qb/records/upsert.test.ts +102 -0
  112. package/tests/vitest/qb/tables/createRelationship.test.ts +102 -0
  113. package/tests/vitest/qb/tables/createTable.test.ts +55 -0
  114. package/tests/vitest/qb/tables/deleteRelationship.test.ts +143 -0
  115. package/tests/vitest/qb/tables/deleteTable.test.ts +50 -0
  116. package/tests/vitest/qb/tables/getAppTables.test.ts +46 -0
  117. package/tests/vitest/qb/tables/getRelationships.test.ts +80 -0
  118. package/tests/vitest/qb/tables/getTable.test.ts +26 -0
  119. package/tests/vitest/qb/tables/updateRelationship.test.ts +250 -0
  120. package/tests/vitest/qb/tables/updateTable.test.ts +48 -0
  121. package/tests/vitest/unit/ThrottleBucket.test.ts +95 -0
  122. package/tests/vitest/unit/apps/copyApp.test.ts +581 -0
  123. package/tests/vitest/unit/apps/createApp.test.ts +186 -0
  124. package/tests/vitest/unit/apps/deleteApp.test.ts +216 -0
  125. package/tests/vitest/unit/apps/getApp.test.ts +128 -0
  126. package/tests/vitest/unit/apps/getAppEvents.test.ts +193 -0
  127. package/tests/vitest/unit/apps/updateApp.test.ts +412 -0
  128. package/tests/vitest/unit/auth/fetchTempToken401.test.ts +63 -0
  129. package/tests/vitest/unit/auth/getTempToken.test.ts +119 -0
  130. package/tests/vitest/unit/auth/retryTempToken401.test.ts +95 -0
  131. package/tests/vitest/unit/auth/retryTempTokenMaxRetries.test.ts +206 -0
  132. package/tests/vitest/unit/auth/retryUserToken401.test.ts +69 -0
  133. package/tests/vitest/unit/auth/ssoTokenRefresh.test.ts +325 -0
  134. package/tests/vitest/unit/fields/createField.test.ts +451 -0
  135. package/tests/vitest/unit/fields/deleteFields.test.ts +141 -0
  136. package/tests/vitest/unit/fields/getField.test.ts +189 -0
  137. package/tests/vitest/unit/fields/getFieldUsage.test.ts +180 -0
  138. package/tests/vitest/unit/fields/getFields.test.ts +122 -0
  139. package/tests/vitest/unit/fields/getFieldsUsage.test.ts +160 -0
  140. package/tests/vitest/unit/fields/updateField.test.ts +285 -0
  141. package/tests/vitest/unit/formulas/runFormula.test.ts +205 -0
  142. package/tests/vitest/unit/index.ts +34 -0
  143. package/tests/vitest/unit/quickbaseClientHttpMethods.test.ts +139 -0
  144. package/tests/vitest/unit/rateLimit.test.ts +191 -0
  145. package/tests/vitest/unit/records/deleteRecords.test.ts +112 -0
  146. package/tests/vitest/unit/records/runQuery.test.ts +102 -0
  147. package/tests/vitest/unit/records/upsert.test.ts +234 -0
  148. package/tests/vitest/unit/tables/createRelationship.test.ts +212 -0
  149. package/tests/vitest/unit/tables/createTable.test.ts +70 -0
  150. package/tests/vitest/unit/tables/deleteRelationship.test.ts +97 -0
  151. package/tests/vitest/unit/tables/deleteTable.test.ts +77 -0
  152. package/tests/vitest/unit/tables/getAppTables.test.ts +85 -0
  153. package/tests/vitest/unit/tables/getRelationships.test.ts +259 -0
  154. package/tests/vitest/unit/tables/getTable.test.ts +233 -0
  155. package/tests/vitest/unit/tables/updateRelationship.test.ts +307 -0
  156. package/tests/vitest/unit/tables/updateTable.test.ts +80 -0
  157. package/tests/vitest/unit/tokenLifespan.test.ts +204 -0
  158. package/tests/vitest/unit/umd.test.ts +8 -0
  159. package/tools/log-gen.ts +251 -0
  160. package/tools/log-gen.yaml +4777 -0
  161. package/tools/openapi-jars/openapi-generator-cli-7.12.0.jar +0 -0
  162. package/tsconfig.build.json +17 -0
  163. package/tsconfig.json +32 -0
  164. package/versionrc.json +9 -0
  165. package/vitest.config.ts +27 -0
@@ -0,0 +1,115 @@
1
+ // open-api/schema/tags/records.ts
2
+ import { Operation, Parameter, Spec } from "../../types/spec.ts";
3
+ import { normalizeDefinitionName } from "../../utils/naming.ts"; // Ensure this import is present
4
+
5
+ export function enhanceRecords(
6
+ spec: Spec,
7
+ pathKey: string,
8
+ method: string,
9
+ operation: Operation
10
+ ): void {
11
+ const opId =
12
+ operation.operationId || `${method}${pathKey.replace(/\W/g, "")}`;
13
+ if (operation.parameters && operation.tags?.includes("Records")) {
14
+ operation.parameters.forEach((param: Parameter) => {
15
+ if (param.in === "body") {
16
+ if (!param.schema) param.schema = {};
17
+ let requestName =
18
+ param.schema.$ref?.split("/").pop() || `${opId}Request`;
19
+ requestName = normalizeDefinitionName(requestName);
20
+
21
+ if (
22
+ !spec.definitions[requestName] ||
23
+ !spec.definitions[requestName].type ||
24
+ Object.keys(spec.definitions[requestName]?.properties || {})
25
+ .length === 0
26
+ ) {
27
+ console.log(
28
+ `Defining schema for ${requestName} in ${pathKey}(${method})`
29
+ );
30
+ let properties;
31
+ // Upsert endpoint (/records)
32
+ if (pathKey === "/records" && method === "post") {
33
+ properties = {
34
+ data: { type: "array", items: { $ref: "#/definitions/Record" } },
35
+ to: { type: "string" },
36
+ fieldsToReturn: { type: "array", items: { type: "integer" } },
37
+ };
38
+ }
39
+ // Query endpoint (/records/query)
40
+ else if (pathKey === "/records/query" && method === "post") {
41
+ properties = {
42
+ from: { type: "string", description: "The table identifier." },
43
+ select: {
44
+ type: "array",
45
+ items: { type: "integer" },
46
+ description: "An array of field ids...",
47
+ },
48
+ where: {
49
+ type: "string",
50
+ description:
51
+ "The filter, using the Quickbase query language...",
52
+ },
53
+ sortBy: {
54
+ type: "array",
55
+ items: {
56
+ type: "object",
57
+ properties: {
58
+ fieldId: { type: "integer" },
59
+ order: {
60
+ type: "string",
61
+ enum: ["ASC", "DESC", "equal-values"],
62
+ },
63
+ },
64
+ required: ["fieldId", "order"],
65
+ },
66
+ description: "An array of field IDs and sort directions...",
67
+ },
68
+ groupBy: {
69
+ type: "array",
70
+ items: {
71
+ type: "object",
72
+ properties: {
73
+ fieldId: { type: "integer" },
74
+ grouping: { type: "string", enum: ["equal-values"] },
75
+ },
76
+ required: ["fieldId", "grouping"],
77
+ },
78
+ description:
79
+ "An array that contains the fields to group the records by.",
80
+ },
81
+ options: {
82
+ type: "object",
83
+ properties: {
84
+ skip: { type: "integer" },
85
+ top: { type: "integer" },
86
+ compareWithAppLocalTime: { type: "boolean" },
87
+ },
88
+ description: "Additional query options.",
89
+ },
90
+ };
91
+ }
92
+ // Delete endpoint (/records)
93
+ else if (method === "delete") {
94
+ properties = {
95
+ from: { type: "string" },
96
+ where: { type: "string" },
97
+ };
98
+ }
99
+ if (properties) {
100
+ spec.definitions[requestName] = {
101
+ type: "object",
102
+ properties,
103
+ required: ["from"], // Only 'from' is required per the spec
104
+ description: operation.summary || `Request body for ${opId}`,
105
+ };
106
+ }
107
+ }
108
+ param.schema = { $ref: `#/definitions/${requestName}` };
109
+ console.log(
110
+ `Ensured ${requestName} in definitions for ${pathKey}(${method})`
111
+ );
112
+ }
113
+ });
114
+ }
115
+ }
@@ -0,0 +1,211 @@
1
+ // schema/tags/tables.ts
2
+ import { Operation, Parameter, Spec } from "../../types/spec.ts";
3
+ import { normalizeDefinitionName } from "../../utils/naming.ts";
4
+
5
+ export function enhanceTables(
6
+ spec: Spec,
7
+ pathKey: string,
8
+ method: string,
9
+ operation: Operation
10
+ ): void {
11
+ const opId =
12
+ operation.operationId || `${method}${pathKey.replace(/\W/g, "")}`;
13
+ if (operation.parameters && operation.tags?.includes("Tables")) {
14
+ operation.parameters.forEach((param: Parameter) => {
15
+ if (param.in === "body") {
16
+ if (!param.schema) param.schema = {};
17
+ let requestName =
18
+ param.schema.$ref?.split("/").pop() || `${opId}Request`;
19
+ requestName = normalizeDefinitionName(requestName);
20
+
21
+ if (
22
+ !spec.definitions[requestName] ||
23
+ !spec.definitions[requestName].type ||
24
+ Object.keys(spec.definitions[requestName]?.properties || {})
25
+ .length === 0
26
+ ) {
27
+ console.log(
28
+ `Defining schema for ${requestName} in ${pathKey}(${method})`
29
+ );
30
+ let properties;
31
+
32
+ // Existing logic for other endpoints remains unchanged
33
+ if (pathKey === "/tables" && method === "post") {
34
+ properties = {
35
+ name: {
36
+ type: "string",
37
+ description: "The name for the table.",
38
+ minLength: 1,
39
+ },
40
+ description: {
41
+ type: "string",
42
+ description: "The description for the table...",
43
+ },
44
+ singleRecordName: {
45
+ type: "string",
46
+ description: "The singular noun for records...",
47
+ },
48
+ pluralRecordName: {
49
+ type: "string",
50
+ description: "The plural noun for records...",
51
+ },
52
+ };
53
+ } else if (pathKey === "/tables/{tableId}" && method === "post") {
54
+ properties = {
55
+ name: {
56
+ type: "string",
57
+ description: "The updated name of the table.",
58
+ },
59
+ description: {
60
+ type: "string",
61
+ description: "The updated description for the table.",
62
+ },
63
+ };
64
+ }
65
+ // Add minimal fix for relationship endpoints
66
+ else if (
67
+ pathKey === "/tables/{tableId}/relationship" &&
68
+ method === "post"
69
+ ) {
70
+ requestName = "CreateRelationshipRequest"; // Force correct name
71
+ if (!spec.definitions[requestName]) {
72
+ properties = {
73
+ parentTableId: {
74
+ type: "string",
75
+ description: "The parent table id for the relationship.",
76
+ },
77
+ foreignKeyField: {
78
+ type: "object",
79
+ properties: {
80
+ label: {
81
+ type: "string",
82
+ description: "The label for the foreign key field.",
83
+ },
84
+ },
85
+ additionalProperties: true,
86
+ },
87
+ lookupFieldIds: {
88
+ type: "array",
89
+ items: { type: "integer" },
90
+ description: "Array of field ids...",
91
+ },
92
+ summaryFields: {
93
+ type: "array",
94
+ items: {
95
+ type: "object",
96
+ properties: {
97
+ summaryFid: {
98
+ type: "number",
99
+ description: "The field id to summarize.",
100
+ },
101
+ label: {
102
+ type: "string",
103
+ description: "The label for the summary field.",
104
+ },
105
+ accumulationType: {
106
+ type: "string",
107
+ enum: [
108
+ "AVG",
109
+ "SUM",
110
+ "MAX",
111
+ "MIN",
112
+ "STD-DEV",
113
+ "COUNT",
114
+ "COMBINED-TEXT",
115
+ "COMBINED-USER",
116
+ "DISTINCT-COUNT",
117
+ ],
118
+ description: "The accumulation type...",
119
+ },
120
+ where: { type: "string", description: "The filter..." },
121
+ },
122
+ required: ["accumulationType"],
123
+ additionalProperties: true,
124
+ },
125
+ description: "Array of summary field objects...",
126
+ },
127
+ };
128
+ }
129
+ } else if (
130
+ pathKey === "/tables/{tableId}/relationship/{relationshipId}" &&
131
+ method === "post"
132
+ ) {
133
+ requestName = "UpdateRelationshipRequest"; // Force correct name
134
+ if (!spec.definitions[requestName]) {
135
+ properties = {
136
+ parentTableId: {
137
+ type: "string",
138
+ description: "The updated parent table id...",
139
+ },
140
+ foreignKeyField: {
141
+ type: "object",
142
+ properties: {
143
+ label: {
144
+ type: "string",
145
+ description: "The updated label...",
146
+ },
147
+ },
148
+ additionalProperties: true,
149
+ },
150
+ lookupFieldIds: {
151
+ type: "array",
152
+ items: { type: "integer" },
153
+ description: "Updated array of field ids...",
154
+ },
155
+ summaryFields: {
156
+ type: "array",
157
+ items: {
158
+ type: "object",
159
+ properties: {
160
+ summaryFid: { type: "number" },
161
+ label: { type: "string" },
162
+ accumulationType: {
163
+ type: "string",
164
+ enum: [
165
+ "AVG",
166
+ "SUM",
167
+ "MAX",
168
+ "MIN",
169
+ "STD-DEV",
170
+ "COUNT",
171
+ "COMBINED-TEXT",
172
+ "COMBINED-USER",
173
+ "DISTINCT-COUNT",
174
+ ],
175
+ },
176
+ where: { type: "string" },
177
+ },
178
+ required: ["accumulationType"],
179
+ additionalProperties: true,
180
+ },
181
+ },
182
+ };
183
+ }
184
+ }
185
+
186
+ if (properties) {
187
+ spec.definitions[requestName] = {
188
+ type: "object",
189
+ properties,
190
+ required:
191
+ pathKey.includes("relationship") &&
192
+ pathKey.endsWith("/relationship")
193
+ ? ["parentTableId"]
194
+ : pathKey === "/tables"
195
+ ? ["name"]
196
+ : [],
197
+ additionalProperties: pathKey.includes("relationship")
198
+ ? false
199
+ : true, // Match raw spec
200
+ description: operation.summary || `Request body for ${opId}`,
201
+ };
202
+ }
203
+ }
204
+ param.schema = { $ref: `#/definitions/${requestName}` };
205
+ console.log(
206
+ `Ensured ${requestName} in definitions for ${pathKey}(${method})`
207
+ );
208
+ }
209
+ });
210
+ }
211
+ }
@@ -0,0 +1,47 @@
1
+ // schema/tags/users.ts
2
+ import { Operation, Parameter, Spec } from "../../types/spec.ts";
3
+ import { normalizeDefinitionName } from "../../utils/naming.ts";
4
+
5
+ export function enhanceUsers(
6
+ spec: Spec,
7
+ pathKey: string,
8
+ method: string,
9
+ operation: Operation
10
+ ): void {
11
+ const opId =
12
+ operation.operationId || `${method}${pathKey.replace(/\W/g, "")}`;
13
+ if (operation.parameters && operation.tags?.includes("Users")) {
14
+ operation.parameters.forEach((param: Parameter) => {
15
+ if (param.in === "body") {
16
+ if (!param.schema) param.schema = {};
17
+ let requestName =
18
+ param.schema.$ref?.split("/").pop() || `${opId}Request`;
19
+ requestName = normalizeDefinitionName(requestName);
20
+
21
+ if (
22
+ !spec.definitions[requestName] ||
23
+ !spec.definitions[requestName].type ||
24
+ Object.keys(spec.definitions[requestName]?.properties || {})
25
+ .length === 0
26
+ ) {
27
+ console.log(
28
+ `Defining schema for ${requestName} in ${pathKey}(${method})`
29
+ );
30
+ const properties = {
31
+ userIds: { type: "array", items: { type: "string" } },
32
+ };
33
+ spec.definitions[requestName] = {
34
+ type: "object",
35
+ properties,
36
+ required: Object.keys(properties),
37
+ description: operation.summary || `Request body for ${opId}`,
38
+ };
39
+ }
40
+ param.schema = { $ref: `#/definitions/${requestName}` };
41
+ console.log(
42
+ `Ensured ${requestName} in definitions for ${pathKey}(${method})`
43
+ );
44
+ }
45
+ });
46
+ }
47
+ }
@@ -0,0 +1,42 @@
1
+ // open-api/types/spec.ts
2
+ export interface Parameter {
3
+ name: string;
4
+ in: string;
5
+ required?: boolean;
6
+ type?: string;
7
+ schema?: {
8
+ type?: string;
9
+ items?: any;
10
+ $ref?: string;
11
+ properties?: any;
12
+ example?: any;
13
+ };
14
+ example?: any;
15
+ }
16
+
17
+ export interface Operation {
18
+ parameters?: Parameter[];
19
+ responses?: Record<
20
+ string,
21
+ { description: string; schema?: any; "x-amf-mediaType"?: string }
22
+ >;
23
+ operationId?: string;
24
+ summary?: string;
25
+ tags?: string[];
26
+ }
27
+
28
+ export interface Spec {
29
+ paths: Record<string, Record<string, Operation>>;
30
+ definitions: Record<string, any>; // Required, no longer optional
31
+ swagger: string;
32
+ info: any;
33
+ operations?: any;
34
+ groups?: any;
35
+ components?: any;
36
+ }
37
+
38
+ export interface FixSpecConfig {
39
+ applyOverrides?: boolean;
40
+ overridePaths?: string[];
41
+ overrideDefinitions?: string[];
42
+ }
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { existsSync, mkdirSync, writeFileSync } from "fs";
4
+ import { dirname } from "path";
5
+
6
+ export function writeFileSafe(
7
+ filePath: string,
8
+ content: string,
9
+ encoding: BufferEncoding = "utf8"
10
+ ): void {
11
+ console.log(`writeFileSafe: Writing to ${filePath}`);
12
+ const dir = dirname(filePath);
13
+ if (!existsSync(dir)) {
14
+ console.log(`writeFileSafe: Creating directory ${dir}`);
15
+ mkdirSync(dir, { recursive: true });
16
+ }
17
+ writeFileSync(filePath, content, encoding);
18
+ }
19
+
20
+ export function runTask(taskName: string, task: () => void): void {
21
+ console.log(`runTask: Starting ${taskName}`);
22
+ try {
23
+ task();
24
+ console.log(`runTask: ${taskName} completed`);
25
+ } catch (error) {
26
+ console.error(`runTask: Error in ${taskName}:`, error);
27
+ process.exit(1);
28
+ }
29
+ }
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { ParamDetail, PropertyDetail, JsDocOptions } from "./sharedUtils.ts";
4
+ import { getPropertyDescription } from "./sharedUtils.ts";
5
+
6
+ export function generateJsDoc({
7
+ summary,
8
+ opId,
9
+ paramDetails,
10
+ returnType,
11
+ returnTypeDetails,
12
+ docLink,
13
+ }: JsDocOptions): string {
14
+ const jsDocLines: string[] = [` /**`, ` * ${summary}`, ` *`];
15
+
16
+ if (paramDetails.length > 0) {
17
+ jsDocLines.push(
18
+ ` * @param {Object} params - Object containing the parameters for ${opId}.`
19
+ );
20
+ paramDetails.forEach((p) => {
21
+ const paramDesc = p.description || "No description provided.";
22
+ const optionalMark = p.required ? "" : " (Optional)";
23
+ jsDocLines.push(
24
+ ` * @param {${p.type}} ${p.required ? "" : "["}params.${p.name}${
25
+ p.required ? "" : "]"
26
+ } - ${paramDesc}${optionalMark}`
27
+ );
28
+ if (p.properties.length > 0) {
29
+ p.properties.forEach((prop) => {
30
+ const propDesc = getPropertyDescription(prop);
31
+ jsDocLines.push(
32
+ ` * - **${prop.name}** (\`${prop.type}\`, ${
33
+ prop.required ? "required" : "optional"
34
+ }) - ${propDesc}`
35
+ );
36
+ });
37
+ }
38
+ });
39
+ } else {
40
+ jsDocLines.push(` * No parameters.`);
41
+ }
42
+
43
+ jsDocLines.push(` *`);
44
+ if (returnTypeDetails.length > 0) {
45
+ jsDocLines.push(
46
+ ` * @returns {Promise<${returnType}>} - Promise resolving to the ${opId} response.`
47
+ );
48
+ const renderProperties = (props: PropertyDetail[], indent: string) => {
49
+ props.forEach((prop) => {
50
+ const propDesc = getPropertyDescription(prop);
51
+ jsDocLines.push(
52
+ `${indent}* - **${prop.name}** (\`${prop.type}\`, ${
53
+ prop.required ? "required" : "optional"
54
+ }) - ${propDesc}`
55
+ );
56
+ if (prop.properties && prop.properties.length > 0) {
57
+ renderProperties(prop.properties, `${indent}* `);
58
+ }
59
+ });
60
+ };
61
+ renderProperties(returnTypeDetails, ` `);
62
+ } else {
63
+ jsDocLines.push(
64
+ ` * @returns {Promise<${returnType}>} - Promise resolving to the ${opId} response.`
65
+ );
66
+ }
67
+
68
+ jsDocLines.push(
69
+ ` *`,
70
+ ` * @see ${docLink} - Official Quickbase API documentation`
71
+ );
72
+ jsDocLines.push(` */`);
73
+ return jsDocLines.join("\n");
74
+ }
@@ -0,0 +1,40 @@
1
+ // utils/infer-schema.ts
2
+ export function inferSchema(example: any, operationId?: string): any {
3
+ if (!example || typeof example !== "object") {
4
+ return { type: "string" };
5
+ }
6
+ if (Array.isArray(example)) {
7
+ return {
8
+ type: "array",
9
+ items:
10
+ example.length > 0
11
+ ? inferSchema(example[0], operationId)
12
+ : { type: "string" },
13
+ };
14
+ }
15
+ const properties: Record<string, any> = {};
16
+ for (const [key, value] of Object.entries(example)) {
17
+ if (Array.isArray(value)) {
18
+ properties[key] = {
19
+ type: "array",
20
+ items:
21
+ value.length > 0
22
+ ? inferSchema(value[0], operationId)
23
+ : { type: "string" },
24
+ };
25
+ } else if (typeof value === "object" && value !== null) {
26
+ properties[key] = inferSchema(value, operationId);
27
+ } else {
28
+ properties[key] = {
29
+ type:
30
+ typeof value === "object" && value === null
31
+ ? "object"
32
+ : typeof value || "string",
33
+ };
34
+ }
35
+ }
36
+ if (operationId && operationId.endsWith("200Response")) {
37
+ return { type: "array", items: { type: "object", properties } };
38
+ }
39
+ return { type: "object", properties };
40
+ }
@@ -0,0 +1,10 @@
1
+ // utils/naming.ts
2
+ export function toCamelCase(str: string): string {
3
+ return str
4
+ .replace(/[-_](.)/g, (_, char) => char.toUpperCase())
5
+ .replace(/^./, (str) => str.toLowerCase());
6
+ }
7
+
8
+ export function normalizeDefinitionName(name: string): string {
9
+ return name.charAt(0).toUpperCase() + name.slice(1);
10
+ }