adorn-api 1.0.6 → 1.0.8
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 +66 -2
- package/dist/adapter/express/bootstrap.d.ts +1 -0
- package/dist/adapter/express/bootstrap.d.ts.map +1 -1
- package/dist/adapter/express/index.d.ts +11 -0
- package/dist/adapter/express/index.d.ts.map +1 -1
- package/dist/cli.cjs +141 -34
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +140 -33
- package/dist/cli.js.map +1 -1
- package/dist/compiler/analyze/extractQueryStyle.d.ts +8 -0
- package/dist/compiler/analyze/extractQueryStyle.d.ts.map +1 -0
- package/dist/compiler/manifest/emit.d.ts.map +1 -1
- package/dist/compiler/schema/openapi.d.ts.map +1 -1
- package/dist/compiler/schema/typeToJsonSchema.d.ts.map +1 -1
- package/dist/express.cjs +327 -60
- package/dist/express.cjs.map +1 -1
- package/dist/express.js +327 -60
- package/dist/express.js.map +1 -1
- package/dist/http.d.ts +4 -1
- package/dist/http.d.ts.map +1 -1
- package/dist/index.cjs +7 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -5
- package/dist/index.js.map +1 -1
- package/dist/metal/index.d.ts +1 -0
- package/dist/metal/index.d.ts.map +1 -1
- package/dist/metal/searchWhere.d.ts +42 -0
- package/dist/metal/searchWhere.d.ts.map +1 -0
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -140,6 +140,58 @@ export class ProductsController {
|
|
|
140
140
|
}
|
|
141
141
|
```
|
|
142
142
|
|
|
143
|
+
### Query Objects
|
|
144
|
+
|
|
145
|
+
Use an object-typed parameter to bind flat query keys:
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
import type { Query } from "adorn-api";
|
|
149
|
+
|
|
150
|
+
type Filters = {
|
|
151
|
+
status?: string;
|
|
152
|
+
responsavelId?: number;
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
@Get("/")
|
|
156
|
+
async list(query?: Query<Filters>) {
|
|
157
|
+
return query;
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
`GET /posts?status=published&responsavelId=1`
|
|
162
|
+
|
|
163
|
+
### Deep Object Query (opt-in)
|
|
164
|
+
|
|
165
|
+
For bracketed query serialization, opt in with `@QueryStyle({ style: "deepObject" })`:
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
import { QueryStyle } from "adorn-api";
|
|
169
|
+
|
|
170
|
+
type WhereFilter = {
|
|
171
|
+
responsavel?: {
|
|
172
|
+
perfil?: {
|
|
173
|
+
nome?: string;
|
|
174
|
+
};
|
|
175
|
+
};
|
|
176
|
+
tags?: string[];
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
@Get("/")
|
|
180
|
+
@QueryStyle({ style: "deepObject" })
|
|
181
|
+
async list(where?: WhereFilter) {
|
|
182
|
+
return where;
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
- `GET /posts?where[responsavel][perfil][nome]=Admin`
|
|
187
|
+
- `GET /posts?where[tags]=a&where[tags]=b`
|
|
188
|
+
- `GET /posts?where[comments][author][name]=Ali` (matches `Alice`)
|
|
189
|
+
|
|
190
|
+
Notes:
|
|
191
|
+
- Deep object is explicit and only applies to the query object parameter on that method.
|
|
192
|
+
- Repeated keys become arrays (for example, `where[tags]=a&where[tags]=b`).
|
|
193
|
+
- The `[]` shorthand is not supported; use repeated keys instead.
|
|
194
|
+
|
|
143
195
|
## Examples
|
|
144
196
|
|
|
145
197
|
### Basic Example
|
|
@@ -275,8 +327,8 @@ import { Controller, Get, Post, Put, Delete } from "adorn-api";
|
|
|
275
327
|
import { BlogPost } from "../entities/index.js";
|
|
276
328
|
import { getSession, selectFromEntity, eq } from "metal-orm";
|
|
277
329
|
|
|
278
|
-
@Controller("/posts")
|
|
279
|
-
export class
|
|
330
|
+
@Controller("/blog-posts")
|
|
331
|
+
export class BlogPostsController {
|
|
280
332
|
@Get("/")
|
|
281
333
|
async getPosts(query?: { authorId?: number; status?: string }) {
|
|
282
334
|
const session = getSession();
|
|
@@ -652,6 +704,7 @@ await bootstrap({
|
|
|
652
704
|
swaggerJsonPath?: string, // Default: "/docs/openapi.json"
|
|
653
705
|
middleware?: CreateRouterOptions["middleware"],
|
|
654
706
|
auth?: CreateRouterOptions["auth"],
|
|
707
|
+
coerce?: CreateRouterOptions["coerce"],
|
|
655
708
|
});
|
|
656
709
|
```
|
|
657
710
|
|
|
@@ -672,6 +725,15 @@ const router = await createExpressRouter({
|
|
|
672
725
|
auth?: {
|
|
673
726
|
schemes: Record<string, AuthSchemeRuntime>,
|
|
674
727
|
},
|
|
728
|
+
coerce?: {
|
|
729
|
+
body?: boolean,
|
|
730
|
+
query?: boolean,
|
|
731
|
+
path?: boolean,
|
|
732
|
+
header?: boolean,
|
|
733
|
+
cookie?: boolean,
|
|
734
|
+
dateTime?: boolean,
|
|
735
|
+
date?: boolean,
|
|
736
|
+
},
|
|
675
737
|
middleware?: {
|
|
676
738
|
global?: Middleware[],
|
|
677
739
|
named?: Record<string, Middleware>,
|
|
@@ -679,6 +741,8 @@ const router = await createExpressRouter({
|
|
|
679
741
|
});
|
|
680
742
|
```
|
|
681
743
|
|
|
744
|
+
Date properties in TypeScript map to OpenAPI `type: "string"` with `format: "date-time"`. Enable `coerce` to convert ISO 8601 date-time strings into `Date` instances before your handler runs. For date-only strings, use `@Format("date")` and keep `date` coercion off to avoid timezone shifts. You can disable per-field coercion with `@Schema({ "x-adorn-coerce": false })`.
|
|
745
|
+
|
|
682
746
|
### setupSwagger()
|
|
683
747
|
|
|
684
748
|
Add Swagger UI to any Express app:
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../../../src/adapter/express/bootstrap.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,MAAM,CAAC;AACnC,OAAO,EAAuB,KAAK,mBAAmB,EAA0C,MAAM,YAAY,CAAC;AAGnH,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,KAAK,CAAC,KAAK,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,CAAC,CAAC;IAChD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,mBAAmB,CAAC,YAAY,CAAC,CAAC;IAC/C,IAAI,CAAC,EAAE,mBAAmB,CAAC,MAAM,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../../../src/adapter/express/bootstrap.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,MAAM,CAAC;AACnC,OAAO,EAAuB,KAAK,mBAAmB,EAA0C,MAAM,YAAY,CAAC;AAGnH,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,KAAK,CAAC,KAAK,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,CAAC,CAAC;IAChD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,mBAAmB,CAAC,YAAY,CAAC,CAAC;IAC/C,IAAI,CAAC,EAAE,mBAAmB,CAAC,MAAM,CAAC,CAAC;IACnC,MAAM,CAAC,EAAE,mBAAmB,CAAC,QAAQ,CAAC,CAAC;CACxC;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,OAAO,CAAC,OAAO,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B;AAED,wBAAgB,SAAS,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC,CAuG7E"}
|
|
@@ -7,8 +7,18 @@ interface OpenAPI31 {
|
|
|
7
7
|
schemas: Record<string, Record<string, unknown>>;
|
|
8
8
|
securitySchemes?: Record<string, Record<string, unknown>>;
|
|
9
9
|
};
|
|
10
|
+
paths?: Record<string, Record<string, any>>;
|
|
10
11
|
security?: Array<Record<string, string[]>>;
|
|
11
12
|
}
|
|
13
|
+
export interface CoerceOptions {
|
|
14
|
+
body?: boolean;
|
|
15
|
+
query?: boolean;
|
|
16
|
+
path?: boolean;
|
|
17
|
+
header?: boolean;
|
|
18
|
+
cookie?: boolean;
|
|
19
|
+
dateTime?: boolean;
|
|
20
|
+
date?: boolean;
|
|
21
|
+
}
|
|
12
22
|
export interface CreateRouterOptions {
|
|
13
23
|
controllers: Array<new (...args: any[]) => any>;
|
|
14
24
|
artifactsDir?: string;
|
|
@@ -17,6 +27,7 @@ export interface CreateRouterOptions {
|
|
|
17
27
|
auth?: {
|
|
18
28
|
schemes: Record<string, AuthSchemeRuntime>;
|
|
19
29
|
};
|
|
30
|
+
coerce?: CoerceOptions;
|
|
20
31
|
middleware?: {
|
|
21
32
|
global?: Array<string | ((req: any, res: any, next: (err?: any) => void) => any)>;
|
|
22
33
|
named?: Record<string, (req: any, res: any, next: (err?: any) => void) => any>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/adapter/express/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAIjC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mCAAmC,CAAC;AAGpE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAIvE,UAAU,SAAS;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE;QACV,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;QACjD,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;KAC3D,CAAC;IACF,QAAQ,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;CAC5C;AAMD,MAAM,WAAW,mBAAmB;IAClC,WAAW,EAAE,KAAK,CAAC,KAAK,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,CAAC,CAAC;IAChD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,UAAU,CAAC;IACtB,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,IAAI,CAAC,EAAE;QACL,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;KAC5C,CAAC;IACF,UAAU,CAAC,EAAE;QACX,MAAM,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,GAAG,KAAK,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC;QAClF,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,GAAG,KAAK,IAAI,KAAK,GAAG,CAAC,CAAC;KAChF,CAAC;CACH;AAED,MAAM,WAAW,mBAAmB;IAClC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE;QACf,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,OAAO,CAAC,EAAE,KAAK,CAAC;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,WAAW,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QACvD,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;KACpB,CAAC;CACH;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/adapter/express/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAIjC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mCAAmC,CAAC;AAGpE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAIvE,UAAU,SAAS;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE;QACV,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;QACjD,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;KAC3D,CAAC;IACF,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;IAC5C,QAAQ,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;CAC5C;AAMD,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,mBAAmB;IAClC,WAAW,EAAE,KAAK,CAAC,KAAK,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,CAAC,CAAC;IAChD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,UAAU,CAAC;IACtB,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,IAAI,CAAC,EAAE;QACL,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;KAC5C,CAAC;IACF,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,UAAU,CAAC,EAAE;QACX,MAAM,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,GAAG,KAAK,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC;QAClF,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,GAAG,KAAK,IAAI,KAAK,GAAG,CAAC,CAAC;KAChF,CAAC;CACH;AAED,MAAM,WAAW,mBAAmB;IAClC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE;QACf,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,OAAO,CAAC,EAAE,KAAK,CAAC;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,WAAW,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QACvD,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;KACpB,CAAC;CACH;AAcD,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,CAwSvF;AAijBD,OAAO,EAAE,SAAS,EAAE,KAAK,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElE,wBAAgB,YAAY,CAAC,OAAO,GAAE,mBAAwB,GAAG,MAAM,CA4BtE"}
|
package/dist/cli.cjs
CHANGED
|
@@ -214,7 +214,8 @@ function classifyParameters(parameters, httpMethod, pathParamIndices, checker) {
|
|
|
214
214
|
for (let i = 0; i < parameters.length; i++) {
|
|
215
215
|
const param = parameters[i];
|
|
216
216
|
if (usedIndices.has(i)) continue;
|
|
217
|
-
const
|
|
217
|
+
const nonNullableType = checker.getNonNullableType(param.type);
|
|
218
|
+
const typeStr = getTypeName(param.type) || getTypeName(nonNullableType);
|
|
218
219
|
if (typeStr === "Body") {
|
|
219
220
|
bodyParamIndex = i;
|
|
220
221
|
usedIndices.add(i);
|
|
@@ -240,7 +241,7 @@ function classifyParameters(parameters, httpMethod, pathParamIndices, checker) {
|
|
|
240
241
|
usedIndices.add(i);
|
|
241
242
|
continue;
|
|
242
243
|
}
|
|
243
|
-
const isObj = isObjectType(
|
|
244
|
+
const isObj = isObjectType(nonNullableType, checker);
|
|
244
245
|
if (isObj && queryObjectParamIndex === null && !isBodyMethod) {
|
|
245
246
|
queryObjectParamIndex = i;
|
|
246
247
|
usedIndices.add(i);
|
|
@@ -269,6 +270,12 @@ function isObjectType(type, checker) {
|
|
|
269
270
|
if (callSignatures && callSignatures.length > 0) return false;
|
|
270
271
|
return true;
|
|
271
272
|
}
|
|
273
|
+
function getTypeName(type) {
|
|
274
|
+
const aliasSymbol = type.aliasSymbol ?? type.aliasSymbol;
|
|
275
|
+
if (aliasSymbol) return aliasSymbol.getName();
|
|
276
|
+
const symbol = type.getSymbol();
|
|
277
|
+
return symbol?.getName() ?? "";
|
|
278
|
+
}
|
|
272
279
|
function findDecorator(node, name) {
|
|
273
280
|
const decorators = import_typescript2.default.getDecorators(node);
|
|
274
281
|
if (!decorators) return null;
|
|
@@ -303,7 +310,7 @@ function unwrapPromise(type, checker) {
|
|
|
303
310
|
}
|
|
304
311
|
|
|
305
312
|
// src/compiler/schema/openapi.ts
|
|
306
|
-
var
|
|
313
|
+
var import_typescript6 = __toESM(require("typescript"), 1);
|
|
307
314
|
|
|
308
315
|
// src/compiler/schema/typeToJsonSchema.ts
|
|
309
316
|
var import_typescript3 = __toESM(require("typescript"), 1);
|
|
@@ -315,6 +322,9 @@ function typeToJsonSchema(type, ctx, typeNode) {
|
|
|
315
322
|
if (type.flags & import_typescript3.default.TypeFlags.Null) {
|
|
316
323
|
return { type: "null" };
|
|
317
324
|
}
|
|
325
|
+
if (isDateType(type, checker)) {
|
|
326
|
+
return { type: "string", format: "date-time" };
|
|
327
|
+
}
|
|
318
328
|
if (type.flags & import_typescript3.default.TypeFlags.String) {
|
|
319
329
|
return { type: "string" };
|
|
320
330
|
}
|
|
@@ -364,6 +374,19 @@ function typeToJsonSchema(type, ctx, typeNode) {
|
|
|
364
374
|
}
|
|
365
375
|
return {};
|
|
366
376
|
}
|
|
377
|
+
function isDateType(type, checker) {
|
|
378
|
+
const symbol = type.getSymbol();
|
|
379
|
+
const aliasSymbol = type.aliasSymbol;
|
|
380
|
+
if (aliasSymbol && aliasSymbol.flags & import_typescript3.default.SymbolFlags.Alias) {
|
|
381
|
+
const aliased = checker.getAliasedSymbol(aliasSymbol);
|
|
382
|
+
return aliased?.getName() === "Date";
|
|
383
|
+
}
|
|
384
|
+
if (symbol && symbol.flags & import_typescript3.default.SymbolFlags.Alias) {
|
|
385
|
+
const aliased = checker.getAliasedSymbol(symbol);
|
|
386
|
+
return aliased?.getName() === "Date";
|
|
387
|
+
}
|
|
388
|
+
return symbol?.getName() === "Date";
|
|
389
|
+
}
|
|
367
390
|
function isSetType(type, checker) {
|
|
368
391
|
const symbol = type.getSymbol();
|
|
369
392
|
if (!symbol) return false;
|
|
@@ -761,6 +784,56 @@ function mergeFragments(base, ...frags) {
|
|
|
761
784
|
return result;
|
|
762
785
|
}
|
|
763
786
|
|
|
787
|
+
// src/compiler/analyze/extractQueryStyle.ts
|
|
788
|
+
var import_typescript5 = __toESM(require("typescript"), 1);
|
|
789
|
+
function extractQueryStyleOptions(checker, method) {
|
|
790
|
+
if (!import_typescript5.default.canHaveDecorators(method)) return null;
|
|
791
|
+
const decorators = import_typescript5.default.getDecorators(method);
|
|
792
|
+
if (!decorators || decorators.length === 0) return null;
|
|
793
|
+
for (const decorator of decorators) {
|
|
794
|
+
const expr = decorator.expression;
|
|
795
|
+
const isCall = import_typescript5.default.isCallExpression(expr);
|
|
796
|
+
const callee = isCall ? expr.expression : expr;
|
|
797
|
+
const args = isCall ? expr.arguments : import_typescript5.default.factory.createNodeArray([]);
|
|
798
|
+
const sym = checker.getSymbolAtLocation(callee);
|
|
799
|
+
if (!sym) continue;
|
|
800
|
+
const resolved = sym.flags & import_typescript5.default.SymbolFlags.Alias ? checker.getAliasedSymbol(sym) : sym;
|
|
801
|
+
const name = resolved.getName();
|
|
802
|
+
if (name !== "QueryStyle") continue;
|
|
803
|
+
const optsNode = args[0];
|
|
804
|
+
if (!optsNode || !import_typescript5.default.isObjectLiteralExpression(optsNode)) {
|
|
805
|
+
return {};
|
|
806
|
+
}
|
|
807
|
+
return parseQueryStyleOptions(optsNode);
|
|
808
|
+
}
|
|
809
|
+
return null;
|
|
810
|
+
}
|
|
811
|
+
function parseQueryStyleOptions(node) {
|
|
812
|
+
const opts = {};
|
|
813
|
+
for (const prop of node.properties) {
|
|
814
|
+
if (!import_typescript5.default.isPropertyAssignment(prop)) continue;
|
|
815
|
+
const name = getPropName(prop.name);
|
|
816
|
+
if (!name) continue;
|
|
817
|
+
if (name === "style" && import_typescript5.default.isStringLiteral(prop.initializer)) {
|
|
818
|
+
const style = prop.initializer.text;
|
|
819
|
+
opts.style = style;
|
|
820
|
+
} else if (name === "explode" && isBooleanLiteral(prop.initializer)) {
|
|
821
|
+
opts.explode = prop.initializer.kind === import_typescript5.default.SyntaxKind.TrueKeyword;
|
|
822
|
+
} else if (name === "allowReserved" && isBooleanLiteral(prop.initializer)) {
|
|
823
|
+
opts.allowReserved = prop.initializer.kind === import_typescript5.default.SyntaxKind.TrueKeyword;
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
return opts;
|
|
827
|
+
}
|
|
828
|
+
function getPropName(name) {
|
|
829
|
+
if (import_typescript5.default.isIdentifier(name)) return name.text;
|
|
830
|
+
if (import_typescript5.default.isStringLiteral(name)) return name.text;
|
|
831
|
+
return null;
|
|
832
|
+
}
|
|
833
|
+
function isBooleanLiteral(node) {
|
|
834
|
+
return node.kind === import_typescript5.default.SyntaxKind.TrueKeyword || node.kind === import_typescript5.default.SyntaxKind.FalseKeyword;
|
|
835
|
+
}
|
|
836
|
+
|
|
764
837
|
// src/compiler/schema/openapi.ts
|
|
765
838
|
function generateOpenAPI(controllers, checker, options = {}) {
|
|
766
839
|
const components = /* @__PURE__ */ new Map();
|
|
@@ -852,12 +925,12 @@ function mergeBodySchemaAnnotations(bodyParam, ctx, schema) {
|
|
|
852
925
|
const declarations = typeSymbol.getDeclarations();
|
|
853
926
|
if (!declarations || declarations.length === 0) return schema;
|
|
854
927
|
const classDecl = declarations[0];
|
|
855
|
-
if (!
|
|
928
|
+
if (!import_typescript6.default.isClassDeclaration(classDecl)) return schema;
|
|
856
929
|
const result = { ...schema };
|
|
857
930
|
const props = { ...result.properties };
|
|
858
931
|
for (const member of classDecl.members) {
|
|
859
|
-
if (!
|
|
860
|
-
const propName =
|
|
932
|
+
if (!import_typescript6.default.isPropertyDeclaration(member) || !member.name) continue;
|
|
933
|
+
const propName = import_typescript6.default.isIdentifier(member.name) ? member.name.text : null;
|
|
861
934
|
if (!propName) continue;
|
|
862
935
|
if (!props[propName]) continue;
|
|
863
936
|
const frags = extractPropertySchemaFragments(ctx.checker, member);
|
|
@@ -892,19 +965,36 @@ function buildQueryParameters(operation, ctx, parameters) {
|
|
|
892
965
|
if (operation.queryObjectParamIndex !== null) {
|
|
893
966
|
const queryParam = operation.parameters[operation.queryObjectParamIndex];
|
|
894
967
|
if (!queryParam) return;
|
|
968
|
+
const queryStyle = extractQueryStyleOptions(ctx.checker, operation.methodDeclaration);
|
|
895
969
|
const querySchema = typeToJsonSchema(queryParam.type, ctx);
|
|
896
|
-
if (
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
const serialization = determineQuerySerialization(propSchema.type);
|
|
901
|
-
parameters.push({
|
|
902
|
-
name: propName,
|
|
970
|
+
if (queryStyle?.style === "deepObject") {
|
|
971
|
+
const explode = queryStyle.explode ?? true;
|
|
972
|
+
const deepParam = {
|
|
973
|
+
name: queryParam.name,
|
|
903
974
|
in: "query",
|
|
904
|
-
required:
|
|
905
|
-
schema:
|
|
906
|
-
|
|
907
|
-
|
|
975
|
+
required: !queryParam.isOptional,
|
|
976
|
+
schema: querySchema.$ref ? { $ref: querySchema.$ref } : querySchema,
|
|
977
|
+
style: "deepObject",
|
|
978
|
+
explode
|
|
979
|
+
};
|
|
980
|
+
if (queryStyle.allowReserved !== void 0) {
|
|
981
|
+
deepParam.allowReserved = queryStyle.allowReserved;
|
|
982
|
+
}
|
|
983
|
+
parameters.push(deepParam);
|
|
984
|
+
} else {
|
|
985
|
+
if (!querySchema.properties) return;
|
|
986
|
+
const queryObjProps = querySchema.properties;
|
|
987
|
+
for (const [propName, propSchema] of Object.entries(queryObjProps)) {
|
|
988
|
+
const isRequired = querySchema.required?.includes(propName) ?? false;
|
|
989
|
+
const serialization = determineQuerySerialization(propSchema.type);
|
|
990
|
+
parameters.push({
|
|
991
|
+
name: propName,
|
|
992
|
+
in: "query",
|
|
993
|
+
required: isRequired,
|
|
994
|
+
schema: propSchema,
|
|
995
|
+
...Object.keys(serialization).length > 0 ? serialization : {}
|
|
996
|
+
});
|
|
997
|
+
}
|
|
908
998
|
}
|
|
909
999
|
}
|
|
910
1000
|
for (const paramIndex of operation.queryParamIndices) {
|
|
@@ -974,7 +1064,7 @@ function buildCookieParameters(operation, ctx, parameters) {
|
|
|
974
1064
|
}
|
|
975
1065
|
|
|
976
1066
|
// src/compiler/manifest/emit.ts
|
|
977
|
-
var
|
|
1067
|
+
var import_typescript7 = __toESM(require("typescript"), 1);
|
|
978
1068
|
function generateManifest(controllers, checker, version, validationMode = "ajv-runtime") {
|
|
979
1069
|
const components = /* @__PURE__ */ new Map();
|
|
980
1070
|
const ctx = {
|
|
@@ -995,7 +1085,7 @@ function generateManifest(controllers, checker, version, validationMode = "ajv-r
|
|
|
995
1085
|
generator: {
|
|
996
1086
|
name: "adorn-api",
|
|
997
1087
|
version,
|
|
998
|
-
typescript:
|
|
1088
|
+
typescript: import_typescript7.default.version
|
|
999
1089
|
},
|
|
1000
1090
|
schemas: {
|
|
1001
1091
|
kind: "openapi-3.1",
|
|
@@ -1080,21 +1170,38 @@ function buildQueryArgs(op, ctx, args) {
|
|
|
1080
1170
|
if (op.queryObjectParamIndex !== null) {
|
|
1081
1171
|
const queryParam = op.parameters[op.queryObjectParamIndex];
|
|
1082
1172
|
if (queryParam) {
|
|
1173
|
+
const queryStyle = extractQueryStyleOptions(ctx.checker, op.methodDeclaration);
|
|
1083
1174
|
const querySchema = typeToJsonSchema(queryParam.type, ctx);
|
|
1084
|
-
if (
|
|
1085
|
-
|
|
1086
|
-
const isRequired = querySchema.required?.includes(propName) ?? false;
|
|
1087
|
-
let schemaRef = propSchema.$ref;
|
|
1088
|
-
if (!schemaRef) {
|
|
1089
|
-
schemaRef = "#/components/schemas/InlineQueryParam";
|
|
1090
|
-
}
|
|
1175
|
+
if (queryStyle?.style === "deepObject") {
|
|
1176
|
+
const schemaRef = querySchema.$ref ?? "#/components/schemas/InlineQueryParam";
|
|
1091
1177
|
args.query.push({
|
|
1092
|
-
name:
|
|
1178
|
+
name: queryParam.name,
|
|
1093
1179
|
index: queryParam.index,
|
|
1094
|
-
required: !
|
|
1180
|
+
required: !queryParam.isOptional,
|
|
1095
1181
|
schemaRef,
|
|
1096
|
-
schemaType:
|
|
1182
|
+
schemaType: querySchema.type,
|
|
1183
|
+
serialization: {
|
|
1184
|
+
style: "deepObject",
|
|
1185
|
+
explode: queryStyle.explode ?? true,
|
|
1186
|
+
allowReserved: queryStyle.allowReserved
|
|
1187
|
+
}
|
|
1097
1188
|
});
|
|
1189
|
+
} else {
|
|
1190
|
+
if (!querySchema.properties) return;
|
|
1191
|
+
for (const [propName, propSchema] of Object.entries(querySchema.properties)) {
|
|
1192
|
+
const isRequired = querySchema.required?.includes(propName) ?? false;
|
|
1193
|
+
let schemaRef = propSchema.$ref;
|
|
1194
|
+
if (!schemaRef) {
|
|
1195
|
+
schemaRef = "#/components/schemas/InlineQueryParam";
|
|
1196
|
+
}
|
|
1197
|
+
args.query.push({
|
|
1198
|
+
name: propName,
|
|
1199
|
+
index: queryParam.index,
|
|
1200
|
+
required: !isRequired,
|
|
1201
|
+
schemaRef,
|
|
1202
|
+
schemaType: propSchema.type
|
|
1203
|
+
});
|
|
1204
|
+
}
|
|
1098
1205
|
}
|
|
1099
1206
|
}
|
|
1100
1207
|
}
|
|
@@ -1297,7 +1404,7 @@ export function validateResponse(operationId, status, contentType, data) {
|
|
|
1297
1404
|
// src/compiler/cache/isStale.ts
|
|
1298
1405
|
var import_node_fs3 = __toESM(require("fs"), 1);
|
|
1299
1406
|
var import_node_path3 = __toESM(require("path"), 1);
|
|
1300
|
-
var
|
|
1407
|
+
var import_typescript8 = require("typescript");
|
|
1301
1408
|
var import_meta = {};
|
|
1302
1409
|
function readJson(p) {
|
|
1303
1410
|
try {
|
|
@@ -1404,7 +1511,7 @@ async function isStale(params) {
|
|
|
1404
1511
|
// src/compiler/cache/writeCache.ts
|
|
1405
1512
|
var import_node_fs4 = __toESM(require("fs"), 1);
|
|
1406
1513
|
var import_node_path4 = __toESM(require("path"), 1);
|
|
1407
|
-
var
|
|
1514
|
+
var import_typescript9 = __toESM(require("typescript"), 1);
|
|
1408
1515
|
function statMtimeMs2(p) {
|
|
1409
1516
|
return import_node_fs4.default.statSync(p).mtimeMs;
|
|
1410
1517
|
}
|
|
@@ -1437,7 +1544,7 @@ function writeCache(params) {
|
|
|
1437
1544
|
generator: {
|
|
1438
1545
|
name: "adorn-api",
|
|
1439
1546
|
version: params.adornVersion,
|
|
1440
|
-
typescript:
|
|
1547
|
+
typescript: import_typescript9.default.version
|
|
1441
1548
|
},
|
|
1442
1549
|
project: {
|
|
1443
1550
|
tsconfigPath: params.tsconfigAbs,
|
|
@@ -1450,7 +1557,7 @@ function writeCache(params) {
|
|
|
1450
1557
|
}
|
|
1451
1558
|
|
|
1452
1559
|
// src/cli.ts
|
|
1453
|
-
var
|
|
1560
|
+
var import_typescript10 = __toESM(require("typescript"), 1);
|
|
1454
1561
|
var import_node_process = __toESM(require("process"), 1);
|
|
1455
1562
|
var ADORN_VERSION = "0.1.0";
|
|
1456
1563
|
function log(msg) {
|
|
@@ -1478,7 +1585,7 @@ async function buildCommand(args) {
|
|
|
1478
1585
|
outDir: outputDir,
|
|
1479
1586
|
project: projectPath,
|
|
1480
1587
|
adornVersion: ADORN_VERSION,
|
|
1481
|
-
typescriptVersion:
|
|
1588
|
+
typescriptVersion: import_typescript10.default.version
|
|
1482
1589
|
});
|
|
1483
1590
|
if (!stale.stale) {
|
|
1484
1591
|
log("adorn-api: artifacts up-to-date");
|