quickbase-js 0.1.0-beta.7
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 +129 -0
- package/LICENSE +21 -0
- package/README.md +204 -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 +7594 -0
- package/dist/esm/quickbase.js +9752 -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 +10502 -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/examples/quicbase-jsCDN.html +36 -0
- package/examples/react.js +48 -0
- package/examples/vanillajs.html +47 -0
- package/notes.txt +158 -0
- package/open-api/fix-spec-main.ts +96 -0
- package/open-api/generate-unified-interface.ts +277 -0
- package/open-api/output/override-report.json +58 -0
- package/open-api/output/quickbase-fixed.json +10610 -0
- package/open-api/regenerate-client.ts +116 -0
- package/open-api/schema/enhance-general.ts +100 -0
- package/open-api/schema/fix-arrays.ts +69 -0
- package/open-api/schema/tags/apps.ts +90 -0
- package/open-api/schema/tags/fields.ts +156 -0
- package/open-api/schema/tags/groups.ts +51 -0
- package/open-api/schema/tags/index.ts +68 -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 +41 -0
- package/open-api/utils/infer-schema.ts +40 -0
- package/open-api/utils/naming.ts +10 -0
- package/package.json +91 -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/generated-unified/QuickbaseClient.ts +359 -0
- package/src/generated-unified/missing-types-report.json +3 -0
- package/src/index.ts +1 -0
- package/src/quickbaseClient.ts +402 -0
- package/src/tokenCache.ts +51 -0
- package/src/utils.ts +7 -0
- package/test-esm.js +1 -0
- package/test-results.json +2050 -0
- package/test-umd.js +1 -0
- package/test.ts +1 -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/tokenPrefetch.test.ts +179 -0
- package/tests/playwright/qb/auth/tokenRenewal.test.ts +133 -0
- package/tests/setup.ts +51 -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 +40 -0
- package/tests/vitest/qb/fields/createField.test.ts +78 -0
- package/tests/vitest/qb/fields/deleteFields.test.ts +89 -0
- package/tests/vitest/qb/fields/getField.test.ts +85 -0
- package/tests/vitest/qb/fields/getFieldUsage.test.ts +174 -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/index.ts +28 -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/createTable.test.ts +55 -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/updateTable.test.ts +48 -0
- package/tests/vitest/unit/apps/copyApp.test.ts +488 -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/auth/fetchTempToken401.test.ts +82 -0
- package/tests/vitest/unit/auth/getTempToken.test.ts +95 -0
- package/tests/vitest/unit/auth/retryOn401.test.ts +116 -0
- package/tests/vitest/unit/auth/two401s.test.ts +86 -0
- package/tests/vitest/unit/fields/createField.test.ts +391 -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 +147 -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/index.ts +33 -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/createTable.test.ts +70 -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/updateTable.test.ts +80 -0
- package/tests/vitest/unit/umd.test.ts +8 -0
- package/text.txt +104 -0
- package/tools/generate-types.ts +29 -0
- package/tools/log-gen.ts +231 -0
- package/tools/log-gen.yaml +3826 -0
- package/tools/openapi-jars/openapi-generator-cli-7.12.0.jar +0 -0
- package/tools/openapi-schema-validator.ts +38 -0
- package/tools/spec-analyzer.ts +163 -0
- package/tools/split-spec.ts +148 -0
- package/tools/test-import.ts +3 -0
- package/tools/validate-spec.ts +40 -0
- package/tsconfig.build.json +17 -0
- package/tsconfig.json +29 -0
- package/versionrc.json +9 -0
- package/vitest.config.ts +27 -0
|
Binary file
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// tools/validate-spec.ts
|
|
2
|
+
import { promises as fs } from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
5
|
+
import * as glob from "glob";
|
|
6
|
+
import OpenAPISchemaValidator from "openapi-schema-validator";
|
|
7
|
+
|
|
8
|
+
async function validateSpecs(): Promise<void> {
|
|
9
|
+
const CODEGEN_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
const SPLIT_DIR = path.join(CODEGEN_DIR, "..", "specs", "split");
|
|
11
|
+
|
|
12
|
+
const specFiles = glob.sync(path.join(SPLIT_DIR, "*.json"));
|
|
13
|
+
if (specFiles.length === 0) {
|
|
14
|
+
console.error("No split spec files found in specs/split/ folder.");
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const validator = new OpenAPISchemaValidator({ version: 2 }); // Adjust based on spec version
|
|
19
|
+
|
|
20
|
+
for (const specFile of specFiles) {
|
|
21
|
+
console.log(`Validating ${path.basename(specFile)}...`);
|
|
22
|
+
const specContent = await fs.readFile(specFile, "utf8");
|
|
23
|
+
const spec = JSON.parse(specContent);
|
|
24
|
+
|
|
25
|
+
const result = validator.validate(spec);
|
|
26
|
+
if (result.errors.length > 0) {
|
|
27
|
+
console.error(`Validation errors in ${specFile}:`, result.errors);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
} else {
|
|
30
|
+
console.log(`${specFile} is valid.`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
validateSpecs().catch((error) => {
|
|
36
|
+
console.error("Failed to validate specs:", error);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
});
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { promises as fs } from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
import * as glob from "glob";
|
|
5
|
+
|
|
6
|
+
interface Parameter {
|
|
7
|
+
name: string;
|
|
8
|
+
in: string;
|
|
9
|
+
required?: boolean;
|
|
10
|
+
type?: string;
|
|
11
|
+
schema?: { type?: string; items?: any; $ref?: string; properties?: any };
|
|
12
|
+
example?: any;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface Operation {
|
|
16
|
+
parameters?: Parameter[];
|
|
17
|
+
responses?: Record<string, { description: string; schema?: any }>;
|
|
18
|
+
operationId?: string;
|
|
19
|
+
summary?: string;
|
|
20
|
+
tags?: string[];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface Spec {
|
|
24
|
+
paths: Record<string, Record<string, Operation>>;
|
|
25
|
+
definitions?: Record<string, any>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface TypeInference {
|
|
29
|
+
fieldId: string;
|
|
30
|
+
inferredType: string;
|
|
31
|
+
source: "schema" | "example";
|
|
32
|
+
confidence: number;
|
|
33
|
+
context: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface AnalysisReport {
|
|
37
|
+
requestTypes: TypeInference[];
|
|
38
|
+
responseTypes: { [status: string]: TypeInference[] };
|
|
39
|
+
dynamicFields: TypeInference[];
|
|
40
|
+
errors: string[];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function inferTypeFromValue(value: any): string {
|
|
44
|
+
if (typeof value === "string") return "string";
|
|
45
|
+
if (typeof value === "number")
|
|
46
|
+
return Number.isInteger(value) ? "integer" : "number";
|
|
47
|
+
if (typeof value === "boolean") return "boolean";
|
|
48
|
+
if (Array.isArray(value)) {
|
|
49
|
+
if (value.length === 0) return "any[]";
|
|
50
|
+
const itemTypes = value.map(inferTypeFromValue);
|
|
51
|
+
const uniqueTypes = [...new Set(itemTypes)];
|
|
52
|
+
return uniqueTypes.length === 1 ? `${uniqueTypes[0]}[]` : "any[]";
|
|
53
|
+
}
|
|
54
|
+
if (typeof value === "object" && value !== null) {
|
|
55
|
+
return "object";
|
|
56
|
+
}
|
|
57
|
+
return "any";
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function analyzeDynamicFields(
|
|
61
|
+
schema: any,
|
|
62
|
+
analysis: AnalysisReport,
|
|
63
|
+
context: string
|
|
64
|
+
) {
|
|
65
|
+
const examples = schema["x-amf-examples"];
|
|
66
|
+
if (examples) {
|
|
67
|
+
for (const [exampleName, example] of Object.entries(examples)) {
|
|
68
|
+
const data = example.value?.data;
|
|
69
|
+
if (Array.isArray(data)) {
|
|
70
|
+
data.forEach((record: any, index: number) => {
|
|
71
|
+
for (const [fieldId, fieldValue] of Object.entries(record)) {
|
|
72
|
+
const inferredType = inferTypeFromValue(fieldValue.value);
|
|
73
|
+
analysis.dynamicFields.push({
|
|
74
|
+
fieldId,
|
|
75
|
+
inferredType,
|
|
76
|
+
source: "example",
|
|
77
|
+
confidence: 0.8,
|
|
78
|
+
context: `${context}-example-${exampleName}-${index}`,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Add schema-based type extraction logic
|
|
87
|
+
if (schema.properties?.data?.items) {
|
|
88
|
+
// Add logic to extract types from schema
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function analyzeUpsertSpec(spec: Spec): AnalysisReport {
|
|
93
|
+
const upsertPath = spec.paths["/records"]?.post;
|
|
94
|
+
if (!upsertPath)
|
|
95
|
+
return {
|
|
96
|
+
errors: ["Upsert endpoint not found"],
|
|
97
|
+
requestTypes: [],
|
|
98
|
+
responseTypes: {},
|
|
99
|
+
dynamicFields: [],
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const analysis: AnalysisReport = {
|
|
103
|
+
requestTypes: [],
|
|
104
|
+
responseTypes: { "200": [], "207": [] },
|
|
105
|
+
dynamicFields: [],
|
|
106
|
+
errors: [],
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const requestSchema = upsertPath.parameters?.find(
|
|
110
|
+
(p) => p.in === "body"
|
|
111
|
+
)?.schema;
|
|
112
|
+
if (requestSchema) {
|
|
113
|
+
analyzeDynamicFields(requestSchema, analysis, "request");
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
for (const status of ["200", "207"]) {
|
|
117
|
+
const responseSchema = upsertPath.responses?.[status]?.schema;
|
|
118
|
+
if (responseSchema) {
|
|
119
|
+
analyzeDynamicFields(responseSchema, analysis, `response-${status}`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return analysis;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async function runAnalysis() {
|
|
127
|
+
try {
|
|
128
|
+
const CODEGEN_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
129
|
+
const SPECS_DIR = path.join(CODEGEN_DIR, "..", "specs");
|
|
130
|
+
const OUTPUT_DIR = path.join(CODEGEN_DIR, "..", "tools", "analysis");
|
|
131
|
+
console.log("Finding latest QuickBase RESTful API spec...");
|
|
132
|
+
const specFiles = glob.sync(
|
|
133
|
+
path.join(SPECS_DIR, "QuickBase_RESTful_*.json")
|
|
134
|
+
);
|
|
135
|
+
if (specFiles.length === 0) {
|
|
136
|
+
console.error(
|
|
137
|
+
"No QuickBase_RESTful_*.json files found in specs/ folder."
|
|
138
|
+
);
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
const inputFile = specFiles.sort().pop() as string;
|
|
142
|
+
console.log(`Reading ${path.basename(inputFile)} from specs/...`);
|
|
143
|
+
const specContent = await fs.readFile(inputFile, "utf8");
|
|
144
|
+
const spec: Spec = JSON.parse(specContent);
|
|
145
|
+
|
|
146
|
+
const analysis = analyzeUpsertSpec(spec);
|
|
147
|
+
await fs.mkdir(OUTPUT_DIR, { recursive: true });
|
|
148
|
+
await fs.writeFile(
|
|
149
|
+
path.join(OUTPUT_DIR, "upsert-analysis.json"),
|
|
150
|
+
JSON.stringify(analysis, null, 2),
|
|
151
|
+
"utf8"
|
|
152
|
+
);
|
|
153
|
+
console.log(
|
|
154
|
+
"Analysis complete. Output written to:",
|
|
155
|
+
path.join(OUTPUT_DIR, "upsert-analysis.json")
|
|
156
|
+
);
|
|
157
|
+
} catch (error) {
|
|
158
|
+
console.error("Failed to analyze spec:", error);
|
|
159
|
+
process.exit(1);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
runAnalysis();
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
// tools/split-spec.ts
|
|
2
|
+
import { promises as fs } from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
5
|
+
import * as glob from "glob";
|
|
6
|
+
|
|
7
|
+
interface Parameter {
|
|
8
|
+
name: string;
|
|
9
|
+
in: string;
|
|
10
|
+
required?: boolean;
|
|
11
|
+
type?: string;
|
|
12
|
+
schema?: any;
|
|
13
|
+
example?: any;
|
|
14
|
+
description?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface Operation {
|
|
18
|
+
parameters?: Parameter[];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface Spec {
|
|
22
|
+
swagger?: string;
|
|
23
|
+
openapi?: string;
|
|
24
|
+
info: any;
|
|
25
|
+
paths: Record<string, Record<string, Operation>>;
|
|
26
|
+
definitions?: Record<string, any>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function normalizeParameters(parameters: Parameter[] = []): Parameter[] {
|
|
30
|
+
return parameters.map((param) => {
|
|
31
|
+
const normalized: Parameter = {
|
|
32
|
+
name: param.name,
|
|
33
|
+
in: param.in,
|
|
34
|
+
required: param.required,
|
|
35
|
+
description: param.description,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// OpenAPI v2: 'body' parameters use 'schema', others use 'type'
|
|
39
|
+
if (param.in === "body") {
|
|
40
|
+
if (param.schema) {
|
|
41
|
+
normalized.schema = param.schema;
|
|
42
|
+
}
|
|
43
|
+
} else {
|
|
44
|
+
normalized.type = param.type || "string";
|
|
45
|
+
if (param.example !== undefined) {
|
|
46
|
+
delete param.example; // Remove 'example' as it’s not allowed in v2
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Ensure 'in' is valid
|
|
51
|
+
if (
|
|
52
|
+
!["query", "header", "path", "formData", "body"].includes(normalized.in)
|
|
53
|
+
) {
|
|
54
|
+
console.warn(
|
|
55
|
+
`Invalid 'in' value '${normalized.in}' for parameter '${normalized.name}', defaulting to 'query'`
|
|
56
|
+
);
|
|
57
|
+
normalized.in = "query";
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return normalized;
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function splitSpec(): Promise<void> {
|
|
65
|
+
const CODEGEN_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
66
|
+
const SPECS_DIR = path.join(CODEGEN_DIR, "..", "specs");
|
|
67
|
+
const OUTPUT_DIR = path.join(SPECS_DIR, "split");
|
|
68
|
+
|
|
69
|
+
// Find the latest spec file
|
|
70
|
+
const specFiles = glob.sync(path.join(SPECS_DIR, "QuickBase_RESTful_*.json"));
|
|
71
|
+
if (specFiles.length === 0) {
|
|
72
|
+
console.error("No QuickBase_RESTful_*.json files found in specs/ folder.");
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
const inputFile = specFiles.sort().pop() as string;
|
|
76
|
+
const specContent = await fs.readFile(inputFile, "utf8");
|
|
77
|
+
const spec: Spec = JSON.parse(specContent);
|
|
78
|
+
|
|
79
|
+
// Define categories based on endpoint paths
|
|
80
|
+
const categories: Record<string, string[]> = {
|
|
81
|
+
apps: Object.keys(spec.paths).filter((p) => p.startsWith("/apps")),
|
|
82
|
+
auth: Object.keys(spec.paths).filter((p) => p.startsWith("/auth")),
|
|
83
|
+
fields: Object.keys(spec.paths).filter((p) => p.startsWith("/fields")),
|
|
84
|
+
records: Object.keys(spec.paths).filter((p) => p.startsWith("/records")),
|
|
85
|
+
tables: Object.keys(spec.paths).filter((p) => p.startsWith("/tables")),
|
|
86
|
+
reports: Object.keys(spec.paths).filter((p) => p.startsWith("/reports")),
|
|
87
|
+
relationships: Object.keys(spec.paths).filter((p) =>
|
|
88
|
+
p.startsWith("/relationships")
|
|
89
|
+
),
|
|
90
|
+
events: Object.keys(spec.paths).filter((p) => p.startsWith("/events")),
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
await fs.mkdir(OUTPUT_DIR, { recursive: true });
|
|
94
|
+
|
|
95
|
+
// Split spec by category
|
|
96
|
+
for (const [category, paths] of Object.entries(categories)) {
|
|
97
|
+
if (paths.length === 0) continue;
|
|
98
|
+
|
|
99
|
+
const categorySpec: Spec = {
|
|
100
|
+
swagger: "2.0", // OpenAPI v2
|
|
101
|
+
info: spec.info || { title: `${category} API`, version: "1.0.0" },
|
|
102
|
+
paths: {},
|
|
103
|
+
definitions: {},
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
// Add relevant paths and normalize parameters
|
|
107
|
+
for (const p of paths) {
|
|
108
|
+
const pathObj = { ...spec.paths[p] };
|
|
109
|
+
for (const method in pathObj) {
|
|
110
|
+
if (pathObj[method].parameters) {
|
|
111
|
+
pathObj[method].parameters = normalizeParameters(
|
|
112
|
+
pathObj[method].parameters
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
categorySpec.paths[p] = pathObj;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Add relevant definitions
|
|
120
|
+
const refs = new Set<string>();
|
|
121
|
+
const refRegex = /#\/definitions\/([^"]+)/g;
|
|
122
|
+
const categorySpecString = JSON.stringify(categorySpec);
|
|
123
|
+
let match;
|
|
124
|
+
while ((match = refRegex.exec(categorySpecString))) {
|
|
125
|
+
refs.add(match[1]);
|
|
126
|
+
}
|
|
127
|
+
if (spec.definitions) {
|
|
128
|
+
for (const ref of refs) {
|
|
129
|
+
if (spec.definitions[ref]) {
|
|
130
|
+
categorySpec.definitions![ref] = spec.definitions[ref];
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const outputFile = path.join(OUTPUT_DIR, `${category}.json`);
|
|
136
|
+
await fs.writeFile(
|
|
137
|
+
outputFile,
|
|
138
|
+
JSON.stringify(categorySpec, null, 2),
|
|
139
|
+
"utf8"
|
|
140
|
+
);
|
|
141
|
+
console.log(`Wrote ${outputFile}`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
splitSpec().catch((error) => {
|
|
146
|
+
console.error("Failed to split spec:", error);
|
|
147
|
+
process.exit(1);
|
|
148
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// tools/validate-spec.ts
|
|
2
|
+
import { promises as fs } from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
5
|
+
import * as glob from "glob";
|
|
6
|
+
import OpenAPISchemaValidatorModule from "openapi-schema-validator";
|
|
7
|
+
|
|
8
|
+
const OpenAPISchemaValidator = OpenAPISchemaValidatorModule.default;
|
|
9
|
+
|
|
10
|
+
async function validateSpecs(): Promise<void> {
|
|
11
|
+
const CODEGEN_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
const SPLIT_DIR = path.join(CODEGEN_DIR, "..", "specs", "split");
|
|
13
|
+
|
|
14
|
+
const specFiles = glob.sync(path.join(SPLIT_DIR, "*.json"));
|
|
15
|
+
if (specFiles.length === 0) {
|
|
16
|
+
console.error("No split spec files found in specs/split/ folder.");
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const validator = new OpenAPISchemaValidator({ version: 2 });
|
|
21
|
+
|
|
22
|
+
for (const specFile of specFiles) {
|
|
23
|
+
console.log(`Validating ${path.basename(specFile)}...`);
|
|
24
|
+
const specContent = await fs.readFile(specFile, "utf8");
|
|
25
|
+
const spec = JSON.parse(specContent);
|
|
26
|
+
|
|
27
|
+
const result = validator.validate(spec);
|
|
28
|
+
if (result.errors.length > 0) {
|
|
29
|
+
console.error(`Validation errors in ${specFile}:`, result.errors);
|
|
30
|
+
process.exit(1);
|
|
31
|
+
} else {
|
|
32
|
+
console.log(`${specFile} is valid.`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
validateSpecs().catch((error) => {
|
|
38
|
+
console.error("Failed to validate specs:", error);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "./tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"outDir": "dist/temp",
|
|
5
|
+
"noEmit": false,
|
|
6
|
+
"allowImportingTsExtensions": false,
|
|
7
|
+
"declaration": false,
|
|
8
|
+
"sourceMap": true
|
|
9
|
+
},
|
|
10
|
+
"include": ["dist/temp-src/**/*"],
|
|
11
|
+
"exclude": [
|
|
12
|
+
"node_modules",
|
|
13
|
+
"dist",
|
|
14
|
+
"tests/**/*",
|
|
15
|
+
"src/**/*" // Exclude original src/ to avoid confusion
|
|
16
|
+
]
|
|
17
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ESNext",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"lib": ["ES2015", "ESNext", "DOM"],
|
|
8
|
+
"types": ["node", "playwright/test", "undici-types"],
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"resolveJsonModule": true,
|
|
11
|
+
"allowImportingTsExtensions": true,
|
|
12
|
+
"noEmit": true,
|
|
13
|
+
"outDir": "dist",
|
|
14
|
+
"rootDir": ".",
|
|
15
|
+
"declaration": true,
|
|
16
|
+
"sourceMap": true,
|
|
17
|
+
"baseUrl": ".",
|
|
18
|
+
"paths": {
|
|
19
|
+
"@/*": ["src/*"],
|
|
20
|
+
"@tests/*": ["tests/*"]
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"include": ["src/**/*", "tests/**/*", "open-api/**/*", "tools/**/*"],
|
|
24
|
+
"exclude": ["node_modules", "dist/**/*", "generated/**/*", "specs"],
|
|
25
|
+
"ts-node": {
|
|
26
|
+
"esm": true,
|
|
27
|
+
"transpileOnly": true
|
|
28
|
+
}
|
|
29
|
+
}
|
package/versionrc.json
ADDED
package/vitest.config.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// vitest.config.ts
|
|
2
|
+
import { defineConfig } from "vitest/config";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import dotenv from "dotenv"; // Add this import
|
|
5
|
+
|
|
6
|
+
// Load .env variables before tests run
|
|
7
|
+
dotenv.config({ path: path.resolve(__dirname, ".env") });
|
|
8
|
+
|
|
9
|
+
export default defineConfig({
|
|
10
|
+
test: {
|
|
11
|
+
environment: "jsdom",
|
|
12
|
+
include: ["tests/vitest/**/**/*.test.ts"],
|
|
13
|
+
setupFiles: ["tests/setup.ts"], // Relative path from root
|
|
14
|
+
pool: "forks",
|
|
15
|
+
poolOptions: {
|
|
16
|
+
forks: {
|
|
17
|
+
singleFork: true, // Run tests sequentially
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
resolve: {
|
|
22
|
+
alias: {
|
|
23
|
+
"@": path.resolve(__dirname, "./src"), // For src/ imports
|
|
24
|
+
"@tests": path.resolve(__dirname, "./tests"), // For tests/ imports
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
});
|