@questpie/openapi 2.0.0 → 3.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.
- package/README.md +40 -30
- package/dist/plugin.d.mts +7 -0
- package/dist/plugin.mjs +30 -0
- package/dist/server.d.mts +89 -40
- package/dist/server.mjs +335 -94
- package/package.json +34 -20
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @questpie/openapi
|
|
2
2
|
|
|
3
|
-
Auto-generate an OpenAPI 3.1 spec from a QUESTPIE
|
|
3
|
+
Auto-generate an OpenAPI 3.1 spec from a QUESTPIE app instance and serve interactive API docs via Scalar UI.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -10,40 +10,50 @@ bun add @questpie/openapi
|
|
|
10
10
|
|
|
11
11
|
## Usage
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Register `openApiModule()` in your `modules.ts` file to add `/openapi.json` and `/docs` routes:
|
|
14
14
|
|
|
15
15
|
```ts
|
|
16
|
+
// src/questpie/server/modules.ts
|
|
17
|
+
import { adminModule } from "@questpie/admin/server";
|
|
18
|
+
import { openApiModule } from "@questpie/openapi";
|
|
19
|
+
|
|
20
|
+
export default [
|
|
21
|
+
adminModule,
|
|
22
|
+
openApiModule({
|
|
23
|
+
info: { title: "My API", version: "1.0.0" },
|
|
24
|
+
scalar: { theme: "purple" },
|
|
25
|
+
}),
|
|
26
|
+
] as const;
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Your route handler stays clean — no wrapper needed:
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
// routes/api/$.ts
|
|
16
33
|
import { createFetchHandler } from "questpie";
|
|
17
|
-
import {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
scalar: { theme: "purple" },
|
|
28
|
-
},
|
|
29
|
-
);
|
|
30
|
-
|
|
31
|
-
// GET /api/cms/openapi.json → OpenAPI spec
|
|
32
|
-
// GET /api/cms/docs → Scalar UI
|
|
33
|
-
// Everything else → CMS routes
|
|
34
|
+
import { app } from "#questpie";
|
|
35
|
+
|
|
36
|
+
const handler = createFetchHandler(app, { basePath: "/api" });
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Once registered, the following endpoints are available:
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
GET /api/openapi.json → OpenAPI spec
|
|
43
|
+
GET /api/docs → Scalar UI
|
|
34
44
|
```
|
|
35
45
|
|
|
36
46
|
## What Gets Documented
|
|
37
47
|
|
|
38
|
-
| Category | Endpoints
|
|
39
|
-
| --------------- |
|
|
40
|
-
| **Collections** | List, create, findOne, update, delete, count, deleteMany, restore, upload, schema, meta |
|
|
41
|
-
| **Globals** | Get, update, schema |
|
|
42
|
-
| **
|
|
43
|
-
| **Auth** | Better Auth endpoints (sign-in, sign-up, session, sign-out)
|
|
44
|
-
| **Search** | Full-text search and reindex
|
|
48
|
+
| Category | Endpoints |
|
|
49
|
+
| --------------- | --------------------------------------------------------------------------------------------------------- |
|
|
50
|
+
| **Collections** | List, create, findOne, update, delete, count, deleteMany, restore, versions, revert, upload, schema, meta |
|
|
51
|
+
| **Globals** | Get, update, versions, revert, schema |
|
|
52
|
+
| **Routes** | All standalone routes, with input/output from Zod schemas |
|
|
53
|
+
| **Auth** | Better Auth endpoints (sign-in, sign-up, session, sign-out) |
|
|
54
|
+
| **Search** | Full-text search and reindex |
|
|
45
55
|
|
|
46
|
-
|
|
56
|
+
Routes with an explicit `outputSchema` get full request/response documentation. Routes without it fall back to `{ type: "object" }`.
|
|
47
57
|
|
|
48
58
|
## Standalone Spec Generation
|
|
49
59
|
|
|
@@ -52,9 +62,9 @@ Generate the spec without mounting routes:
|
|
|
52
62
|
```ts
|
|
53
63
|
import { generateOpenApiSpec } from "@questpie/openapi";
|
|
54
64
|
|
|
55
|
-
const spec = generateOpenApiSpec(
|
|
56
|
-
|
|
57
|
-
|
|
65
|
+
const spec = generateOpenApiSpec(app, {
|
|
66
|
+
basePath: "/api",
|
|
67
|
+
info: { title: "My API", version: "1.0.0" },
|
|
58
68
|
});
|
|
59
69
|
```
|
|
60
70
|
|
package/dist/plugin.mjs
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
//#region src/plugin.ts
|
|
2
|
+
function openApiPlugin() {
|
|
3
|
+
return {
|
|
4
|
+
name: "questpie-openapi",
|
|
5
|
+
targets: { server: {
|
|
6
|
+
root: ".",
|
|
7
|
+
outputFile: "index.ts",
|
|
8
|
+
discover: { openapi: {
|
|
9
|
+
pattern: "config/openapi.ts",
|
|
10
|
+
configKey: "openapi"
|
|
11
|
+
} },
|
|
12
|
+
registries: { singletonFactories: { openapi: {
|
|
13
|
+
configType: "OpenApiModuleConfig",
|
|
14
|
+
imports: [{
|
|
15
|
+
name: "OpenApiModuleConfig",
|
|
16
|
+
from: "@questpie/openapi"
|
|
17
|
+
}]
|
|
18
|
+
} } },
|
|
19
|
+
transform: (ctx) => {
|
|
20
|
+
const routes = ctx.categories.get("routes");
|
|
21
|
+
if (!routes?.size) return;
|
|
22
|
+
const union = [...routes.keys()].map((k) => `"${k}"`).join(" | ");
|
|
23
|
+
ctx.addTypeDeclaration(`export type AppRouteKeys = ${union};`);
|
|
24
|
+
}
|
|
25
|
+
} }
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
//#endregion
|
|
30
|
+
export { openApiPlugin };
|
package/dist/server.d.mts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as questpie0 from "questpie";
|
|
2
2
|
|
|
3
3
|
//#region src/types.d.ts
|
|
4
|
-
|
|
5
4
|
/**
|
|
6
5
|
* Configuration for OpenAPI spec generation.
|
|
7
6
|
*/
|
|
@@ -17,7 +16,7 @@ interface OpenApiConfig {
|
|
|
17
16
|
url: string;
|
|
18
17
|
description?: string;
|
|
19
18
|
}>;
|
|
20
|
-
/** Base path for
|
|
19
|
+
/** Base path for routes (must match your adapter basePath) */
|
|
21
20
|
basePath?: string;
|
|
22
21
|
/** Exclude specific collections or globals from the spec */
|
|
23
22
|
exclude?: {
|
|
@@ -48,18 +47,15 @@ interface ScalarConfig {
|
|
|
48
47
|
};
|
|
49
48
|
}
|
|
50
49
|
/**
|
|
51
|
-
* Configuration for
|
|
50
|
+
* Configuration for the OpenAPI module.
|
|
51
|
+
* Pass to `openApiModule()` for zero-config setup via `modules.ts`.
|
|
52
52
|
*/
|
|
53
|
-
interface
|
|
54
|
-
/** CMS instance */
|
|
55
|
-
cms: Questpie<any>;
|
|
56
|
-
/** RPC router tree (same one passed to createFetchHandler) */
|
|
57
|
-
rpc?: RpcRouterTree<any>;
|
|
53
|
+
interface OpenApiModuleConfig extends OpenApiConfig {
|
|
58
54
|
/** Scalar UI options */
|
|
59
55
|
scalar?: ScalarConfig;
|
|
60
|
-
/** Path for the JSON spec (
|
|
56
|
+
/** Path for the JSON spec route (default: "openapi.json") */
|
|
61
57
|
specPath?: string;
|
|
62
|
-
/** Path for the Scalar UI docs (
|
|
58
|
+
/** Path for the Scalar UI docs route (default: "docs") */
|
|
63
59
|
docsPath?: string;
|
|
64
60
|
}
|
|
65
61
|
/**
|
|
@@ -99,44 +95,97 @@ interface PathOperation {
|
|
|
99
95
|
}
|
|
100
96
|
//#endregion
|
|
101
97
|
//#region src/server.d.ts
|
|
98
|
+
|
|
102
99
|
/**
|
|
103
|
-
*
|
|
100
|
+
* Identity factory for `config/openapi.ts` — provides type inference.
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* ```ts
|
|
104
|
+
* // config/openapi.ts
|
|
105
|
+
* import { openApiConfig } from "@questpie/openapi";
|
|
106
|
+
*
|
|
107
|
+
* export default openApiConfig({
|
|
108
|
+
* info: { title: "My API", version: "1.0.0" },
|
|
109
|
+
* scalar: { theme: "purple" },
|
|
110
|
+
* });
|
|
111
|
+
* ```
|
|
104
112
|
*/
|
|
105
|
-
declare function
|
|
113
|
+
declare function openApiConfig(config: OpenApiModuleConfig): OpenApiModuleConfig;
|
|
106
114
|
/**
|
|
107
|
-
*
|
|
115
|
+
* Generate a complete OpenAPI 3.1 spec from a QUESTPIE app instance.
|
|
116
|
+
* Routes are read from `app.config.routes` automatically.
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* ```ts
|
|
120
|
+
* import { generateOpenApiSpec } from "@questpie/openapi";
|
|
121
|
+
*
|
|
122
|
+
* const spec = generateOpenApiSpec(app, {
|
|
123
|
+
* info: { title: "My API", version: "1.0.0" },
|
|
124
|
+
* });
|
|
125
|
+
* ```
|
|
108
126
|
*/
|
|
109
|
-
declare function
|
|
110
|
-
scalar?: ScalarConfig;
|
|
111
|
-
}): {
|
|
112
|
-
/** Returns the OpenAPI spec as JSON */
|
|
113
|
-
specHandler: () => Response;
|
|
114
|
-
/** Returns the Scalar UI HTML page */
|
|
115
|
-
scalarHandler: () => Response;
|
|
116
|
-
};
|
|
127
|
+
declare function generateOpenApiSpec(app: unknown, config?: OpenApiConfig): OpenApiSpec;
|
|
117
128
|
/**
|
|
118
|
-
*
|
|
129
|
+
* Create a route that serves the OpenAPI 3.1 JSON spec.
|
|
130
|
+
* Place in your `routes/` directory for automatic discovery.
|
|
131
|
+
*
|
|
132
|
+
* Spec is lazy-generated on first request and cached with ETag.
|
|
133
|
+
* Config is read from `config/openapi.ts` at request time, or from
|
|
134
|
+
* explicit parameter if provided.
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* ```ts title="routes/openapi-spec.ts"
|
|
138
|
+
* import { openApiRoute } from "@questpie/openapi";
|
|
119
139
|
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
140
|
+
* export default openApiRoute();
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
143
|
+
declare function openApiRoute(config?: OpenApiConfig): questpie0.RawRouteDefinition;
|
|
144
|
+
/**
|
|
145
|
+
* Create a route that serves the Scalar interactive API docs.
|
|
146
|
+
* Place in your `routes/` directory for automatic discovery.
|
|
122
147
|
*
|
|
123
148
|
* @example
|
|
149
|
+
* ```ts title="routes/docs.ts"
|
|
150
|
+
* import { docsRoute } from "@questpie/openapi";
|
|
151
|
+
*
|
|
152
|
+
* export default docsRoute();
|
|
153
|
+
* ```
|
|
154
|
+
*/
|
|
155
|
+
declare function docsRoute(config?: OpenApiConfig & {
|
|
156
|
+
scalar?: ScalarConfig;
|
|
157
|
+
}): questpie0.RawRouteDefinition;
|
|
158
|
+
/**
|
|
159
|
+
* OpenAPI module — registers spec + docs routes.
|
|
160
|
+
*
|
|
161
|
+
* Routes are served as:
|
|
162
|
+
* - `GET /api/openapi.json` — OpenAPI 3.1 JSON spec
|
|
163
|
+
* - `GET /api/docs` — Scalar interactive API reference
|
|
164
|
+
*
|
|
165
|
+
* Configure via `config/openapi.ts`:
|
|
166
|
+
* ```ts
|
|
167
|
+
* import { openApiConfig } from "@questpie/openapi";
|
|
168
|
+
* export default openApiConfig({ info: { title: "My API", version: "1.0.0" } });
|
|
169
|
+
* ```
|
|
170
|
+
*
|
|
171
|
+
* Or pass config directly for backward compatibility:
|
|
124
172
|
* ```ts
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
* scalar: { theme: 'purple' },
|
|
133
|
-
* }
|
|
134
|
-
* )
|
|
135
|
-
* // GET /api/cms/openapi.json → spec JSON
|
|
136
|
-
* // GET /api/cms/docs → Scalar UI
|
|
137
|
-
* // Everything else → CMS handler
|
|
173
|
+
* openApiModule({ info: { title: "My API" } })
|
|
174
|
+
* ```
|
|
175
|
+
*
|
|
176
|
+
* @example Static (reads config from config/openapi.ts):
|
|
177
|
+
* ```ts title="questpie/server/modules.ts"
|
|
178
|
+
* import { openApiModule } from "@questpie/openapi";
|
|
179
|
+
* export default [openApiModule] as const;
|
|
138
180
|
* ```
|
|
139
181
|
*/
|
|
140
|
-
declare
|
|
182
|
+
declare const openApiModule: {
|
|
183
|
+
name: "questpie-openapi";
|
|
184
|
+
plugin: questpie0.CodegenPlugin;
|
|
185
|
+
routes: {
|
|
186
|
+
"openapi.json": questpie0.RawRouteDefinition;
|
|
187
|
+
docs: questpie0.RawRouteDefinition;
|
|
188
|
+
};
|
|
189
|
+
};
|
|
141
190
|
//#endregion
|
|
142
|
-
export { type OpenApiConfig, type OpenApiSpec, type ScalarConfig,
|
|
191
|
+
export { type OpenApiConfig, type OpenApiModuleConfig, type OpenApiSpec, type ScalarConfig, openApiModule as default, openApiModule, docsRoute, generateOpenApiSpec, openApiConfig, openApiRoute };
|
package/dist/server.mjs
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { openApiPlugin } from "./plugin.mjs";
|
|
2
|
+
import { module, route } from "questpie";
|
|
1
3
|
import { z } from "zod";
|
|
2
4
|
|
|
3
5
|
//#region src/generator/schemas.ts
|
|
@@ -104,7 +106,8 @@ function listQueryParameters() {
|
|
|
104
106
|
in: "query",
|
|
105
107
|
schema: { type: "string" },
|
|
106
108
|
description: "Content locale"
|
|
107
|
-
}
|
|
109
|
+
},
|
|
110
|
+
stageQueryParameter()
|
|
108
111
|
];
|
|
109
112
|
}
|
|
110
113
|
/**
|
|
@@ -116,7 +119,15 @@ function singleQueryParameters() {
|
|
|
116
119
|
in: "query",
|
|
117
120
|
schema: { type: "string" },
|
|
118
121
|
description: "Content locale"
|
|
119
|
-
}];
|
|
122
|
+
}, stageQueryParameter()];
|
|
123
|
+
}
|
|
124
|
+
function stageQueryParameter() {
|
|
125
|
+
return {
|
|
126
|
+
name: "stage",
|
|
127
|
+
in: "query",
|
|
128
|
+
schema: { type: "string" },
|
|
129
|
+
description: "Workflow stage"
|
|
130
|
+
};
|
|
120
131
|
}
|
|
121
132
|
/**
|
|
122
133
|
* Standard JSON responses helper.
|
|
@@ -201,7 +212,7 @@ function generateAuthPaths(config) {
|
|
|
201
212
|
paths: {},
|
|
202
213
|
tags: []
|
|
203
214
|
};
|
|
204
|
-
const basePath = config.basePath ?? "/
|
|
215
|
+
const basePath = config.basePath ?? "/";
|
|
205
216
|
const tag = "Auth";
|
|
206
217
|
const paths = {};
|
|
207
218
|
paths[`${basePath}/auth/sign-in/email`] = { post: {
|
|
@@ -290,9 +301,9 @@ function generateAuthPaths(config) {
|
|
|
290
301
|
/**
|
|
291
302
|
* Generate OpenAPI paths and component schemas for all collections.
|
|
292
303
|
*/
|
|
293
|
-
function generateCollectionPaths(
|
|
294
|
-
const collections =
|
|
295
|
-
const basePath = config.basePath ?? "/
|
|
304
|
+
function generateCollectionPaths(app, config) {
|
|
305
|
+
const collections = app.getCollections();
|
|
306
|
+
const basePath = config.basePath ?? "/";
|
|
296
307
|
const excluded = new Set(config.exclude?.collections ?? []);
|
|
297
308
|
const paths = {};
|
|
298
309
|
const schemas = {};
|
|
@@ -351,6 +362,7 @@ function generateCollectionPaths(cms, config) {
|
|
|
351
362
|
operationId: `${name}_create`,
|
|
352
363
|
summary: `Create ${name}`,
|
|
353
364
|
tags: [tag],
|
|
365
|
+
parameters: [stageQueryParameter()],
|
|
354
366
|
requestBody: jsonRequestBody(ref(insertSchemaName)),
|
|
355
367
|
responses: jsonResponse(ref(documentSchemaName), `Created ${name} record`)
|
|
356
368
|
}
|
|
@@ -434,7 +446,7 @@ function generateCollectionPaths(cms, config) {
|
|
|
434
446
|
operationId: `${name}_update`,
|
|
435
447
|
summary: `Update ${name}`,
|
|
436
448
|
tags: [tag],
|
|
437
|
-
parameters: [idParam],
|
|
449
|
+
parameters: [idParam, stageQueryParameter()],
|
|
438
450
|
requestBody: jsonRequestBody(ref(updateSchemaName)),
|
|
439
451
|
responses: jsonResponse(ref(documentSchemaName), `Updated ${name} record`)
|
|
440
452
|
},
|
|
@@ -442,7 +454,7 @@ function generateCollectionPaths(cms, config) {
|
|
|
442
454
|
operationId: `${name}_delete`,
|
|
443
455
|
summary: `Delete ${name}`,
|
|
444
456
|
tags: [tag],
|
|
445
|
-
parameters: [idParam],
|
|
457
|
+
parameters: [idParam, stageQueryParameter()],
|
|
446
458
|
responses: jsonResponse(ref("SuccessResponse"), `Deleted ${name} record`)
|
|
447
459
|
}
|
|
448
460
|
};
|
|
@@ -450,9 +462,76 @@ function generateCollectionPaths(cms, config) {
|
|
|
450
462
|
operationId: `${name}_restore`,
|
|
451
463
|
summary: `Restore deleted ${name}`,
|
|
452
464
|
tags: [tag],
|
|
453
|
-
parameters: [idParam],
|
|
465
|
+
parameters: [idParam, stageQueryParameter()],
|
|
454
466
|
responses: jsonResponse(ref(documentSchemaName), `Restored ${name} record`)
|
|
455
467
|
} };
|
|
468
|
+
paths[`${prefix}/{id}/versions`] = { get: {
|
|
469
|
+
operationId: `${name}_findVersions`,
|
|
470
|
+
summary: `List ${name} versions`,
|
|
471
|
+
tags: [tag],
|
|
472
|
+
parameters: [
|
|
473
|
+
idParam,
|
|
474
|
+
{
|
|
475
|
+
name: "limit",
|
|
476
|
+
in: "query",
|
|
477
|
+
schema: { type: "number" },
|
|
478
|
+
description: "Maximum number of versions to return"
|
|
479
|
+
},
|
|
480
|
+
{
|
|
481
|
+
name: "offset",
|
|
482
|
+
in: "query",
|
|
483
|
+
schema: { type: "number" },
|
|
484
|
+
description: "Number of versions to skip"
|
|
485
|
+
}
|
|
486
|
+
],
|
|
487
|
+
responses: jsonResponse({
|
|
488
|
+
type: "array",
|
|
489
|
+
items: {
|
|
490
|
+
type: "object",
|
|
491
|
+
properties: {
|
|
492
|
+
id: { type: "string" },
|
|
493
|
+
versionId: { type: "string" },
|
|
494
|
+
versionNumber: { type: "number" },
|
|
495
|
+
versionOperation: { type: "string" },
|
|
496
|
+
versionUserId: { type: ["string", "null"] },
|
|
497
|
+
versionCreatedAt: {
|
|
498
|
+
type: "string",
|
|
499
|
+
format: "date-time"
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}, `Version history for ${name}`)
|
|
504
|
+
} };
|
|
505
|
+
paths[`${prefix}/{id}/revert`] = { post: {
|
|
506
|
+
operationId: `${name}_revertToVersion`,
|
|
507
|
+
summary: `Revert ${name} to a version`,
|
|
508
|
+
tags: [tag],
|
|
509
|
+
parameters: [idParam, stageQueryParameter()],
|
|
510
|
+
requestBody: jsonRequestBody({
|
|
511
|
+
type: "object",
|
|
512
|
+
properties: {
|
|
513
|
+
version: { type: "number" },
|
|
514
|
+
versionId: { type: "string" }
|
|
515
|
+
}
|
|
516
|
+
}),
|
|
517
|
+
responses: jsonResponse(ref(documentSchemaName), `Reverted ${name} record`)
|
|
518
|
+
} };
|
|
519
|
+
const versioningOpts = state.options?.versioning;
|
|
520
|
+
if (versioningOpts && typeof versioningOpts === "object" && versioningOpts.workflow) paths[`${prefix}/{id}/transition`] = { post: {
|
|
521
|
+
operationId: `${name}_transition`,
|
|
522
|
+
summary: `Transition ${name} workflow stage`,
|
|
523
|
+
tags: [tag],
|
|
524
|
+
parameters: [idParam],
|
|
525
|
+
requestBody: jsonRequestBody({
|
|
526
|
+
type: "object",
|
|
527
|
+
required: ["stage"],
|
|
528
|
+
properties: { stage: {
|
|
529
|
+
type: "string",
|
|
530
|
+
description: "Target workflow stage"
|
|
531
|
+
} }
|
|
532
|
+
}),
|
|
533
|
+
responses: jsonResponse(ref(documentSchemaName), `Transitioned ${name} record`)
|
|
534
|
+
} };
|
|
456
535
|
}
|
|
457
536
|
return {
|
|
458
537
|
paths,
|
|
@@ -496,10 +575,10 @@ function buildSchemaFromFieldDefinitions(fieldDefinitions) {
|
|
|
496
575
|
if (!fieldDefinitions || typeof fieldDefinitions !== "object") return null;
|
|
497
576
|
const shape = {};
|
|
498
577
|
for (const [fieldName, fieldDefinition] of Object.entries(fieldDefinitions)) {
|
|
499
|
-
const
|
|
500
|
-
if (typeof toZodSchema !== "function") continue;
|
|
578
|
+
const fd = fieldDefinition;
|
|
579
|
+
if (typeof fd.toZodSchema !== "function") continue;
|
|
501
580
|
try {
|
|
502
|
-
const schema = toZodSchema();
|
|
581
|
+
const schema = fd.toZodSchema();
|
|
503
582
|
if (schema && typeof schema === "object" && "_def" in schema) shape[fieldName] = schema;
|
|
504
583
|
} catch {}
|
|
505
584
|
}
|
|
@@ -517,9 +596,9 @@ function buildSchemaFromFieldDefinitions(fieldDefinitions) {
|
|
|
517
596
|
/**
|
|
518
597
|
* Generate OpenAPI paths and component schemas for all globals.
|
|
519
598
|
*/
|
|
520
|
-
function generateGlobalPaths(
|
|
521
|
-
const globals =
|
|
522
|
-
const basePath = config.basePath ?? "/
|
|
599
|
+
function generateGlobalPaths(app, config) {
|
|
600
|
+
const globals = app.getGlobals();
|
|
601
|
+
const basePath = config.basePath ?? "/";
|
|
523
602
|
const excluded = new Set(config.exclude?.globals ?? []);
|
|
524
603
|
const paths = {};
|
|
525
604
|
const schemas = {};
|
|
@@ -580,13 +659,14 @@ function generateGlobalPaths(cms, config) {
|
|
|
580
659
|
in: "query",
|
|
581
660
|
schema: { type: "string" },
|
|
582
661
|
description: "Content locale"
|
|
583
|
-
}],
|
|
662
|
+
}, stageQueryParameter()],
|
|
584
663
|
responses: jsonResponse(ref(valueSchemaName), `Current value of ${name} global`)
|
|
585
664
|
},
|
|
586
665
|
patch: {
|
|
587
666
|
operationId: `global_${name}_update`,
|
|
588
667
|
summary: `Update ${name} global`,
|
|
589
668
|
tags: [tag],
|
|
669
|
+
parameters: [stageQueryParameter()],
|
|
590
670
|
requestBody: jsonRequestBody(ref(updateSchemaName)),
|
|
591
671
|
responses: jsonResponse(ref(valueSchemaName), `Updated ${name} global`)
|
|
592
672
|
}
|
|
@@ -600,6 +680,78 @@ function generateGlobalPaths(cms, config) {
|
|
|
600
680
|
description: "Introspected global schema"
|
|
601
681
|
}, `Introspection schema for ${name} global`)
|
|
602
682
|
} };
|
|
683
|
+
paths[`${prefix}/versions`] = { get: {
|
|
684
|
+
operationId: `global_${name}_findVersions`,
|
|
685
|
+
summary: `List ${name} global versions`,
|
|
686
|
+
tags: [tag],
|
|
687
|
+
parameters: [
|
|
688
|
+
{
|
|
689
|
+
name: "id",
|
|
690
|
+
in: "query",
|
|
691
|
+
schema: { type: "string" },
|
|
692
|
+
description: "Global record ID"
|
|
693
|
+
},
|
|
694
|
+
{
|
|
695
|
+
name: "limit",
|
|
696
|
+
in: "query",
|
|
697
|
+
schema: { type: "number" },
|
|
698
|
+
description: "Maximum number of versions to return"
|
|
699
|
+
},
|
|
700
|
+
{
|
|
701
|
+
name: "offset",
|
|
702
|
+
in: "query",
|
|
703
|
+
schema: { type: "number" },
|
|
704
|
+
description: "Number of versions to skip"
|
|
705
|
+
}
|
|
706
|
+
],
|
|
707
|
+
responses: jsonResponse({
|
|
708
|
+
type: "array",
|
|
709
|
+
items: {
|
|
710
|
+
type: "object",
|
|
711
|
+
properties: {
|
|
712
|
+
id: { type: "string" },
|
|
713
|
+
versionId: { type: "string" },
|
|
714
|
+
versionNumber: { type: "number" },
|
|
715
|
+
versionOperation: { type: "string" },
|
|
716
|
+
versionUserId: { type: ["string", "null"] },
|
|
717
|
+
versionCreatedAt: {
|
|
718
|
+
type: "string",
|
|
719
|
+
format: "date-time"
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
}, `Version history for ${name} global`)
|
|
724
|
+
} };
|
|
725
|
+
paths[`${prefix}/revert`] = { post: {
|
|
726
|
+
operationId: `global_${name}_revertToVersion`,
|
|
727
|
+
summary: `Revert ${name} global to a version`,
|
|
728
|
+
tags: [tag],
|
|
729
|
+
parameters: [stageQueryParameter()],
|
|
730
|
+
requestBody: jsonRequestBody({
|
|
731
|
+
type: "object",
|
|
732
|
+
properties: {
|
|
733
|
+
id: { type: "string" },
|
|
734
|
+
version: { type: "number" },
|
|
735
|
+
versionId: { type: "string" }
|
|
736
|
+
}
|
|
737
|
+
}),
|
|
738
|
+
responses: jsonResponse(ref(valueSchemaName), `Reverted ${name} global value`)
|
|
739
|
+
} };
|
|
740
|
+
const versioningOpts = state.options?.versioning;
|
|
741
|
+
if (versioningOpts && typeof versioningOpts === "object" && versioningOpts.workflow) paths[`${prefix}/transition`] = { post: {
|
|
742
|
+
operationId: `global_${name}_transition`,
|
|
743
|
+
summary: `Transition ${name} global workflow stage`,
|
|
744
|
+
tags: [tag],
|
|
745
|
+
requestBody: jsonRequestBody({
|
|
746
|
+
type: "object",
|
|
747
|
+
required: ["stage"],
|
|
748
|
+
properties: { stage: {
|
|
749
|
+
type: "string",
|
|
750
|
+
description: "Target workflow stage"
|
|
751
|
+
} }
|
|
752
|
+
}),
|
|
753
|
+
responses: jsonResponse(ref(valueSchemaName), `Transitioned ${name} global value`)
|
|
754
|
+
} };
|
|
603
755
|
}
|
|
604
756
|
return {
|
|
605
757
|
paths,
|
|
@@ -614,10 +766,10 @@ function buildGlobalSchemaFromFieldDefinitions(fieldDefinitions) {
|
|
|
614
766
|
if (!fieldDefinitions || typeof fieldDefinitions !== "object") return null;
|
|
615
767
|
const shape = {};
|
|
616
768
|
for (const [fieldName, fieldDefinition] of Object.entries(fieldDefinitions)) {
|
|
617
|
-
const
|
|
618
|
-
if (typeof toZodSchema !== "function") continue;
|
|
769
|
+
const fd = fieldDefinition;
|
|
770
|
+
if (typeof fd.toZodSchema !== "function") continue;
|
|
619
771
|
try {
|
|
620
|
-
const schema = toZodSchema();
|
|
772
|
+
const schema = fd.toZodSchema();
|
|
621
773
|
if (schema && typeof schema === "object" && "_def" in schema) shape[fieldName] = schema;
|
|
622
774
|
} catch {}
|
|
623
775
|
}
|
|
@@ -626,11 +778,18 @@ function buildGlobalSchemaFromFieldDefinitions(fieldDefinitions) {
|
|
|
626
778
|
}
|
|
627
779
|
|
|
628
780
|
//#endregion
|
|
629
|
-
//#region src/generator/
|
|
781
|
+
//#region src/generator/routes.ts
|
|
782
|
+
/**
|
|
783
|
+
* Convert camelCase to kebab-case, matching the HTTP adapter's URL generation.
|
|
784
|
+
* e.g. "createBooking" → "create-booking"
|
|
785
|
+
*/
|
|
786
|
+
function camelToKebab(str) {
|
|
787
|
+
return str.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);
|
|
788
|
+
}
|
|
630
789
|
/**
|
|
631
|
-
* Flatten
|
|
790
|
+
* Flatten a routes tree into a list of { path, definition }.
|
|
632
791
|
*/
|
|
633
|
-
function
|
|
792
|
+
function flattenRoutesTree(tree, prefix = []) {
|
|
634
793
|
const entries = [];
|
|
635
794
|
for (const [key, value] of Object.entries(tree)) {
|
|
636
795
|
const segments = [...prefix, key];
|
|
@@ -639,46 +798,47 @@ function flattenRpcTree(tree, prefix = []) {
|
|
|
639
798
|
segments,
|
|
640
799
|
definition: value
|
|
641
800
|
});
|
|
642
|
-
else if (value && typeof value === "object") entries.push(...
|
|
801
|
+
else if (value && typeof value === "object") entries.push(...flattenRoutesTree(value, segments));
|
|
643
802
|
}
|
|
644
803
|
return entries;
|
|
645
804
|
}
|
|
646
805
|
/**
|
|
647
|
-
* Generate OpenAPI paths for all
|
|
806
|
+
* Generate OpenAPI paths for all routes in the routes tree.
|
|
648
807
|
*/
|
|
649
|
-
function
|
|
808
|
+
function generateRoutePaths(routes, config) {
|
|
650
809
|
const paths = {};
|
|
651
810
|
const schemas = {};
|
|
652
811
|
const tags = [];
|
|
653
|
-
if (!
|
|
812
|
+
if (!routes) return {
|
|
654
813
|
paths,
|
|
655
814
|
schemas,
|
|
656
815
|
tags
|
|
657
816
|
};
|
|
658
|
-
const basePath = config.basePath ?? "/
|
|
659
|
-
const entries =
|
|
817
|
+
const basePath = config.basePath ?? "/";
|
|
818
|
+
const entries = flattenRoutesTree(routes);
|
|
660
819
|
const tagSet = /* @__PURE__ */ new Set();
|
|
661
820
|
for (const entry of entries) {
|
|
662
821
|
const def = entry.definition;
|
|
663
822
|
const isRaw = def.mode === "raw";
|
|
664
|
-
const
|
|
823
|
+
const method = (def.method ?? "post").toLowerCase();
|
|
824
|
+
const topLevel = entry.segments[0] ?? "routes";
|
|
665
825
|
if (!tagSet.has(topLevel)) {
|
|
666
826
|
tagSet.add(topLevel);
|
|
667
827
|
tags.push({
|
|
668
|
-
name: `
|
|
669
|
-
description: `
|
|
828
|
+
name: `Routes: ${topLevel}`,
|
|
829
|
+
description: `Routes under ${topLevel}`
|
|
670
830
|
});
|
|
671
831
|
}
|
|
672
|
-
const operationId = `
|
|
673
|
-
const routePath = `${basePath}
|
|
832
|
+
const operationId = `route_${entry.segments.join("_")}`;
|
|
833
|
+
const routePath = `${basePath}/${entry.segments.map(camelToKebab).join("/")}`;
|
|
674
834
|
const operation = {
|
|
675
835
|
operationId,
|
|
676
836
|
summary: entry.path,
|
|
677
|
-
tags: [`
|
|
837
|
+
tags: [`Routes: ${topLevel}`],
|
|
678
838
|
responses: {}
|
|
679
839
|
};
|
|
680
840
|
if (isRaw) {
|
|
681
|
-
operation.description = "Raw
|
|
841
|
+
operation.description = "Raw route - accepts any request body and returns a raw response.";
|
|
682
842
|
operation.requestBody = { content: {
|
|
683
843
|
"application/json": { schema: {} },
|
|
684
844
|
"application/octet-stream": { schema: {
|
|
@@ -706,10 +866,10 @@ function generateRpcPaths(rpc, config) {
|
|
|
706
866
|
schemas[schemaName] = zodToJsonSchema(def.outputSchema);
|
|
707
867
|
outputSchema = { $ref: `#/components/schemas/${schemaName}` };
|
|
708
868
|
}
|
|
709
|
-
operation.requestBody = jsonRequestBody(inputSchema, "
|
|
710
|
-
operation.responses = jsonResponse(outputSchema, "
|
|
869
|
+
operation.requestBody = jsonRequestBody(inputSchema, "Route input");
|
|
870
|
+
operation.responses = jsonResponse(outputSchema, "Route output");
|
|
711
871
|
}
|
|
712
|
-
paths[routePath] = {
|
|
872
|
+
paths[routePath] = { [method]: operation };
|
|
713
873
|
}
|
|
714
874
|
return {
|
|
715
875
|
paths,
|
|
@@ -728,7 +888,7 @@ function generateSearchPaths(config) {
|
|
|
728
888
|
paths: {},
|
|
729
889
|
tags: []
|
|
730
890
|
};
|
|
731
|
-
const basePath = config.basePath ?? "/
|
|
891
|
+
const basePath = config.basePath ?? "/";
|
|
732
892
|
const tag = "Search";
|
|
733
893
|
const paths = {};
|
|
734
894
|
paths[`${basePath}/search`] = { post: {
|
|
@@ -815,24 +975,24 @@ function generateSearchPaths(config) {
|
|
|
815
975
|
//#endregion
|
|
816
976
|
//#region src/generator/index.ts
|
|
817
977
|
/**
|
|
818
|
-
* Generate a complete OpenAPI 3.1 spec from a Questpie
|
|
978
|
+
* Generate a complete OpenAPI 3.1 spec from a Questpie app instance and optional routes tree.
|
|
819
979
|
*/
|
|
820
|
-
function generateOpenApiSpec$1(
|
|
980
|
+
function generateOpenApiSpec$1(app, routes, config = {}) {
|
|
821
981
|
const allPaths = {};
|
|
822
982
|
const allSchemas = { ...baseComponentSchemas() };
|
|
823
983
|
const allTags = [];
|
|
824
|
-
const collections = generateCollectionPaths(
|
|
984
|
+
const collections = generateCollectionPaths(app, config);
|
|
825
985
|
Object.assign(allPaths, collections.paths);
|
|
826
986
|
Object.assign(allSchemas, collections.schemas);
|
|
827
987
|
allTags.push(...collections.tags);
|
|
828
|
-
const globals = generateGlobalPaths(
|
|
988
|
+
const globals = generateGlobalPaths(app, config);
|
|
829
989
|
Object.assign(allPaths, globals.paths);
|
|
830
990
|
Object.assign(allSchemas, globals.schemas);
|
|
831
991
|
allTags.push(...globals.tags);
|
|
832
|
-
const
|
|
833
|
-
Object.assign(allPaths,
|
|
834
|
-
Object.assign(allSchemas,
|
|
835
|
-
allTags.push(...
|
|
992
|
+
const routeResult = generateRoutePaths(routes, config);
|
|
993
|
+
Object.assign(allPaths, routeResult.paths);
|
|
994
|
+
Object.assign(allSchemas, routeResult.schemas);
|
|
995
|
+
allTags.push(...routeResult.tags);
|
|
836
996
|
const auth = generateAuthPaths(config);
|
|
837
997
|
Object.assign(allPaths, auth.paths);
|
|
838
998
|
allTags.push(...auth.tags);
|
|
@@ -842,7 +1002,7 @@ function generateOpenApiSpec$1(cms, rpc, config = {}) {
|
|
|
842
1002
|
return {
|
|
843
1003
|
openapi: "3.1.0",
|
|
844
1004
|
info: {
|
|
845
|
-
title: config.info?.title ?? "QUESTPIE
|
|
1005
|
+
title: config.info?.title ?? "QUESTPIE API",
|
|
846
1006
|
version: config.info?.version ?? "1.0.0",
|
|
847
1007
|
description: config.info?.description
|
|
848
1008
|
},
|
|
@@ -906,64 +1066,145 @@ function escapeAttr(str) {
|
|
|
906
1066
|
//#endregion
|
|
907
1067
|
//#region src/server.ts
|
|
908
1068
|
/**
|
|
909
|
-
*
|
|
1069
|
+
* Identity factory for `config/openapi.ts` — provides type inference.
|
|
1070
|
+
*
|
|
1071
|
+
* @example
|
|
1072
|
+
* ```ts
|
|
1073
|
+
* // config/openapi.ts
|
|
1074
|
+
* import { openApiConfig } from "@questpie/openapi";
|
|
1075
|
+
*
|
|
1076
|
+
* export default openApiConfig({
|
|
1077
|
+
* info: { title: "My API", version: "1.0.0" },
|
|
1078
|
+
* scalar: { theme: "purple" },
|
|
1079
|
+
* });
|
|
1080
|
+
* ```
|
|
910
1081
|
*/
|
|
911
|
-
function
|
|
912
|
-
return
|
|
1082
|
+
function openApiConfig(config) {
|
|
1083
|
+
return config;
|
|
913
1084
|
}
|
|
914
1085
|
/**
|
|
915
|
-
*
|
|
1086
|
+
* Generate a complete OpenAPI 3.1 spec from a QUESTPIE app instance.
|
|
1087
|
+
* Routes are read from `app.config.routes` automatically.
|
|
1088
|
+
*
|
|
1089
|
+
* @example
|
|
1090
|
+
* ```ts
|
|
1091
|
+
* import { generateOpenApiSpec } from "@questpie/openapi";
|
|
1092
|
+
*
|
|
1093
|
+
* const spec = generateOpenApiSpec(app, {
|
|
1094
|
+
* info: { title: "My API", version: "1.0.0" },
|
|
1095
|
+
* });
|
|
1096
|
+
* ```
|
|
916
1097
|
*/
|
|
917
|
-
function
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
1098
|
+
function generateOpenApiSpec(app, config) {
|
|
1099
|
+
const routes = app.config?.routes;
|
|
1100
|
+
return generateOpenApiSpec$1(app, routes, config);
|
|
1101
|
+
}
|
|
1102
|
+
const specCache = /* @__PURE__ */ new WeakMap();
|
|
1103
|
+
function getCachedSpec(app, config) {
|
|
1104
|
+
const appObj = app;
|
|
1105
|
+
let cached = specCache.get(appObj);
|
|
1106
|
+
if (!cached) {
|
|
1107
|
+
const spec = generateOpenApiSpec(app, config);
|
|
1108
|
+
const json = JSON.stringify(spec);
|
|
1109
|
+
let hash = 0;
|
|
1110
|
+
for (let i = 0; i < json.length; i++) hash = (hash << 5) - hash + json.charCodeAt(i) | 0;
|
|
1111
|
+
cached = {
|
|
1112
|
+
json,
|
|
1113
|
+
etag: `"oapi-${(hash >>> 0).toString(36)}"`
|
|
1114
|
+
};
|
|
1115
|
+
specCache.set(appObj, cached);
|
|
1116
|
+
}
|
|
1117
|
+
return cached;
|
|
925
1118
|
}
|
|
926
1119
|
/**
|
|
927
|
-
*
|
|
1120
|
+
* Read OpenAPI config from `app.state.config.openapi` (set via config/openapi.ts).
|
|
1121
|
+
* Falls back to explicit config parameter if provided.
|
|
1122
|
+
*/
|
|
1123
|
+
function resolveOpenApiConfig(app, explicitConfig) {
|
|
1124
|
+
if (explicitConfig) return explicitConfig;
|
|
1125
|
+
return (app.state?.config)?.openapi;
|
|
1126
|
+
}
|
|
1127
|
+
/**
|
|
1128
|
+
* Create a route that serves the OpenAPI 3.1 JSON spec.
|
|
1129
|
+
* Place in your `routes/` directory for automatic discovery.
|
|
928
1130
|
*
|
|
929
|
-
*
|
|
930
|
-
*
|
|
1131
|
+
* Spec is lazy-generated on first request and cached with ETag.
|
|
1132
|
+
* Config is read from `config/openapi.ts` at request time, or from
|
|
1133
|
+
* explicit parameter if provided.
|
|
931
1134
|
*
|
|
932
1135
|
* @example
|
|
933
|
-
* ```ts
|
|
934
|
-
*
|
|
935
|
-
*
|
|
936
|
-
*
|
|
937
|
-
* cms,
|
|
938
|
-
* rpc: appRpc,
|
|
939
|
-
* basePath: '/api/cms',
|
|
940
|
-
* info: { title: 'My API', version: '1.0.0' },
|
|
941
|
-
* scalar: { theme: 'purple' },
|
|
942
|
-
* }
|
|
943
|
-
* )
|
|
944
|
-
* // GET /api/cms/openapi.json → spec JSON
|
|
945
|
-
* // GET /api/cms/docs → Scalar UI
|
|
946
|
-
* // Everything else → CMS handler
|
|
1136
|
+
* ```ts title="routes/openapi-spec.ts"
|
|
1137
|
+
* import { openApiRoute } from "@questpie/openapi";
|
|
1138
|
+
*
|
|
1139
|
+
* export default openApiRoute();
|
|
947
1140
|
* ```
|
|
948
1141
|
*/
|
|
949
|
-
function
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
return handler(request, context);
|
|
962
|
-
};
|
|
1142
|
+
function openApiRoute(config) {
|
|
1143
|
+
return route().get().raw().handler(async (ctx) => {
|
|
1144
|
+
const app = ctx.app;
|
|
1145
|
+
const { json, etag } = getCachedSpec(app, resolveOpenApiConfig(app, config));
|
|
1146
|
+
if (ctx.request.headers.get("if-none-match") === etag) return new Response(null, { status: 304 });
|
|
1147
|
+
return new Response(json, { headers: {
|
|
1148
|
+
"Content-Type": "application/json",
|
|
1149
|
+
"Cache-Control": "public, max-age=3600, stale-while-revalidate=43200",
|
|
1150
|
+
ETag: etag,
|
|
1151
|
+
"Access-Control-Allow-Origin": "*"
|
|
1152
|
+
} });
|
|
1153
|
+
});
|
|
963
1154
|
}
|
|
964
|
-
|
|
965
|
-
|
|
1155
|
+
/**
|
|
1156
|
+
* Create a route that serves the Scalar interactive API docs.
|
|
1157
|
+
* Place in your `routes/` directory for automatic discovery.
|
|
1158
|
+
*
|
|
1159
|
+
* @example
|
|
1160
|
+
* ```ts title="routes/docs.ts"
|
|
1161
|
+
* import { docsRoute } from "@questpie/openapi";
|
|
1162
|
+
*
|
|
1163
|
+
* export default docsRoute();
|
|
1164
|
+
* ```
|
|
1165
|
+
*/
|
|
1166
|
+
function docsRoute(config) {
|
|
1167
|
+
const { scalar: scalarConfig, ...openApiConfig$1 } = config ?? {};
|
|
1168
|
+
return route().get().raw().handler(async (ctx) => {
|
|
1169
|
+
const app = ctx.app;
|
|
1170
|
+
const resolved = resolveOpenApiConfig(app, openApiConfig$1);
|
|
1171
|
+
const scalarOpts = scalarConfig ?? resolved?.scalar;
|
|
1172
|
+
return serveScalarUI(generateOpenApiSpec(app, resolved), scalarOpts);
|
|
1173
|
+
});
|
|
966
1174
|
}
|
|
1175
|
+
/**
|
|
1176
|
+
* OpenAPI module — registers spec + docs routes.
|
|
1177
|
+
*
|
|
1178
|
+
* Routes are served as:
|
|
1179
|
+
* - `GET /api/openapi.json` — OpenAPI 3.1 JSON spec
|
|
1180
|
+
* - `GET /api/docs` — Scalar interactive API reference
|
|
1181
|
+
*
|
|
1182
|
+
* Configure via `config/openapi.ts`:
|
|
1183
|
+
* ```ts
|
|
1184
|
+
* import { openApiConfig } from "@questpie/openapi";
|
|
1185
|
+
* export default openApiConfig({ info: { title: "My API", version: "1.0.0" } });
|
|
1186
|
+
* ```
|
|
1187
|
+
*
|
|
1188
|
+
* Or pass config directly for backward compatibility:
|
|
1189
|
+
* ```ts
|
|
1190
|
+
* openApiModule({ info: { title: "My API" } })
|
|
1191
|
+
* ```
|
|
1192
|
+
*
|
|
1193
|
+
* @example Static (reads config from config/openapi.ts):
|
|
1194
|
+
* ```ts title="questpie/server/modules.ts"
|
|
1195
|
+
* import { openApiModule } from "@questpie/openapi";
|
|
1196
|
+
* export default [openApiModule] as const;
|
|
1197
|
+
* ```
|
|
1198
|
+
*/
|
|
1199
|
+
const openApiModule = module({
|
|
1200
|
+
name: "questpie-openapi",
|
|
1201
|
+
plugin: openApiPlugin(),
|
|
1202
|
+
routes: {
|
|
1203
|
+
"openapi.json": openApiRoute(),
|
|
1204
|
+
docs: docsRoute()
|
|
1205
|
+
}
|
|
1206
|
+
});
|
|
1207
|
+
var server_default = openApiModule;
|
|
967
1208
|
|
|
968
1209
|
//#endregion
|
|
969
|
-
export {
|
|
1210
|
+
export { server_default as default, docsRoute, generateOpenApiSpec, openApiConfig, openApiModule, openApiRoute };
|
package/package.json
CHANGED
|
@@ -1,21 +1,49 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@questpie/openapi",
|
|
3
|
-
"version": "
|
|
4
|
-
"type": "module",
|
|
3
|
+
"version": "3.0.0",
|
|
5
4
|
"repository": {
|
|
6
5
|
"type": "git",
|
|
7
|
-
"url": "https://github.com/questpie/questpie
|
|
6
|
+
"url": "https://github.com/questpie/questpie.git",
|
|
8
7
|
"directory": "packages/openapi"
|
|
9
8
|
},
|
|
10
9
|
"files": [
|
|
11
10
|
"dist"
|
|
12
11
|
],
|
|
12
|
+
"type": "module",
|
|
13
|
+
"main": "./dist/server.mjs",
|
|
14
|
+
"module": "./dist/server.mjs",
|
|
15
|
+
"types": "./dist/server.d.mts",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./dist/server.d.mts",
|
|
19
|
+
"default": "./dist/server.mjs"
|
|
20
|
+
},
|
|
21
|
+
"./plugin": {
|
|
22
|
+
"types": "./dist/plugin.d.mts",
|
|
23
|
+
"default": "./dist/plugin.mjs"
|
|
24
|
+
},
|
|
25
|
+
"./package.json": "./package.json"
|
|
26
|
+
},
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public",
|
|
29
|
+
"exports": {
|
|
30
|
+
".": {
|
|
31
|
+
"types": "./dist/server.d.mts",
|
|
32
|
+
"default": "./dist/server.mjs"
|
|
33
|
+
},
|
|
34
|
+
"./plugin": {
|
|
35
|
+
"types": "./dist/plugin.d.mts",
|
|
36
|
+
"default": "./dist/plugin.mjs"
|
|
37
|
+
},
|
|
38
|
+
"./package.json": "./package.json"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
13
41
|
"scripts": {
|
|
14
42
|
"build": "tsdown",
|
|
15
43
|
"check-types": "tsc --noEmit"
|
|
16
44
|
},
|
|
17
45
|
"dependencies": {
|
|
18
|
-
"questpie": "
|
|
46
|
+
"questpie": "^3.0.0",
|
|
19
47
|
"zod": "^4.2.1"
|
|
20
48
|
},
|
|
21
49
|
"devDependencies": {
|
|
@@ -23,20 +51,6 @@
|
|
|
23
51
|
"tsdown": "^0.18.3"
|
|
24
52
|
},
|
|
25
53
|
"peerDependencies": {
|
|
26
|
-
"questpie": "^
|
|
27
|
-
}
|
|
28
|
-
"exports": {
|
|
29
|
-
".": "./src/server.ts",
|
|
30
|
-
"./package.json": "./package.json"
|
|
31
|
-
},
|
|
32
|
-
"publishConfig": {
|
|
33
|
-
"access": "public",
|
|
34
|
-
"exports": {
|
|
35
|
-
".": "./dist/server.mjs",
|
|
36
|
-
"./package.json": "./package.json"
|
|
37
|
-
}
|
|
38
|
-
},
|
|
39
|
-
"main": "./dist/server.mjs",
|
|
40
|
-
"module": "./dist/server.mjs",
|
|
41
|
-
"types": "./dist/server.d.mts"
|
|
54
|
+
"questpie": "^3.0.0"
|
|
55
|
+
}
|
|
42
56
|
}
|