@apollo-deploy/tesseract 0.4.3 → 0.5.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 +330 -11
- package/dist/adapters/types.d.ts +2 -1
- package/dist/adapters/types.d.ts.map +1 -1
- package/dist/adapters/typescript/index.d.ts +2 -1
- package/dist/adapters/typescript/index.d.ts.map +1 -1
- package/dist/adapters/typescript/index.js +27 -10
- package/dist/adapters/typescript/index.js.map +1 -1
- package/dist/cli.js +19 -1
- package/dist/cli.js.map +1 -1
- package/dist/collector.d.ts +135 -0
- package/dist/collector.d.ts.map +1 -0
- package/dist/collector.js +159 -0
- package/dist/collector.js.map +1 -0
- package/dist/elysia.d.ts +65 -0
- package/dist/elysia.d.ts.map +1 -0
- package/dist/elysia.js +70 -0
- package/dist/elysia.js.map +1 -0
- package/dist/express.d.ts +74 -0
- package/dist/express.d.ts.map +1 -0
- package/dist/express.js +73 -0
- package/dist/express.js.map +1 -0
- package/dist/fastify.d.ts +135 -0
- package/dist/fastify.d.ts.map +1 -0
- package/dist/fastify.js +188 -0
- package/dist/fastify.js.map +1 -0
- package/dist/helpers/handlebars.d.ts.map +1 -1
- package/dist/helpers/handlebars.js +6 -2
- package/dist/helpers/handlebars.js.map +1 -1
- package/dist/hono.d.ts +65 -0
- package/dist/hono.d.ts.map +1 -0
- package/dist/hono.js +64 -0
- package/dist/hono.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/koa.d.ts +69 -0
- package/dist/koa.d.ts.map +1 -0
- package/dist/koa.js +68 -0
- package/dist/koa.js.map +1 -0
- package/dist/nestjs.d.ts +107 -0
- package/dist/nestjs.d.ts.map +1 -0
- package/dist/nestjs.js +143 -0
- package/dist/nestjs.js.map +1 -0
- package/dist/pipeline/intake.d.ts.map +1 -1
- package/dist/pipeline/intake.js +40 -21
- package/dist/pipeline/intake.js.map +1 -1
- package/dist/pipeline/write.d.ts +1 -0
- package/dist/pipeline/write.d.ts.map +1 -1
- package/dist/pipeline/write.js +21 -3
- package/dist/pipeline/write.js.map +1 -1
- package/dist/types/config.d.ts +13 -3
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js +5 -2
- package/dist/types/config.js.map +1 -1
- package/dist/types/ir.d.ts +14 -0
- package/dist/types/ir.d.ts.map +1 -1
- package/dist/types/manifest.d.ts +5 -23
- package/dist/types/manifest.d.ts.map +1 -1
- package/dist/types/manifest.js.map +1 -1
- package/dist/types/sdk-module.d.ts +128 -0
- package/dist/types/sdk-module.d.ts.map +1 -0
- package/dist/types/sdk-module.js +50 -0
- package/dist/types/sdk-module.js.map +1 -0
- package/package.json +63 -1
- package/templates/typescript/client-class.hbs +136 -0
- package/templates/typescript/domain-class.hbs +188 -0
- package/templates/typescript/domain.hbs +10 -6
- package/templates/typescript/index-class.hbs +68 -0
- package/templates/typescript/transport-sse.hbs +3 -3
package/dist/koa.d.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tesseract Koa integration.
|
|
3
|
+
*
|
|
4
|
+
* Re-exports `SDKCollector` and adds a `KoaSDKCollector` subclass with a
|
|
5
|
+
* `.koaMiddleware()` helper that registers a route with the collector and
|
|
6
|
+
* returns a no-op Koa middleware for inline placement with `@koa/router`.
|
|
7
|
+
*
|
|
8
|
+
* Because Koa has no route-registration hook, routes are registered with the
|
|
9
|
+
* collector explicitly. Generation is triggered by calling
|
|
10
|
+
* `collector.tryGenerate()` after all routes are set up.
|
|
11
|
+
*
|
|
12
|
+
* ```ts
|
|
13
|
+
* import Koa from 'koa';
|
|
14
|
+
* import Router from '@koa/router';
|
|
15
|
+
* import { KoaSDKCollector } from '@apollo-deploy/tesseract/koa';
|
|
16
|
+
*
|
|
17
|
+
* const app = new Koa();
|
|
18
|
+
* const router = new Router();
|
|
19
|
+
* const collector = new KoaSDKCollector({
|
|
20
|
+
* info: { title: 'My API', version: '1.0.0', baseUrl: 'https://api.example.com' },
|
|
21
|
+
* output: './packages/api-sdk',
|
|
22
|
+
* });
|
|
23
|
+
*
|
|
24
|
+
* collector.domain('/users', { domain: 'users', description: 'User management' });
|
|
25
|
+
*
|
|
26
|
+
* router.get('/users/:id',
|
|
27
|
+
* collector.koaMiddleware('/users/:id', 'GET', { sdk: { methodName: 'getUser' } }),
|
|
28
|
+
* getUserHandler,
|
|
29
|
+
* );
|
|
30
|
+
*
|
|
31
|
+
* app.use(router.routes());
|
|
32
|
+
*
|
|
33
|
+
* // After all routes are registered:
|
|
34
|
+
* if (await collector.tryGenerate()) process.exit(0);
|
|
35
|
+
*
|
|
36
|
+
* app.listen(3000);
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* **Triggering generation:**
|
|
40
|
+
* ```bash
|
|
41
|
+
* TESSERACT_GENERATE=1 node dist/app.js
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
import { SDKCollector } from './collector.js';
|
|
45
|
+
import type { CollectorRouteConfig } from './collector.js';
|
|
46
|
+
export { SDKCollector } from './collector.js';
|
|
47
|
+
export type { CollectorOptions, CollectorRouteConfig } from './collector.js';
|
|
48
|
+
/** Minimal Koa middleware type — avoids a hard import of the `koa` package. */
|
|
49
|
+
type KoaMiddleware = (ctx: unknown, next: () => Promise<void>) => Promise<void> | void;
|
|
50
|
+
/**
|
|
51
|
+
* `SDKCollector` subclass with a Koa-specific `.koaMiddleware()` helper.
|
|
52
|
+
*
|
|
53
|
+
* `.koaMiddleware()` registers a route with the collector **and** returns a
|
|
54
|
+
* no-op Koa middleware for inline placement with `@koa/router`.
|
|
55
|
+
*/
|
|
56
|
+
export declare class KoaSDKCollector extends SDKCollector {
|
|
57
|
+
/**
|
|
58
|
+
* Register a route with the collector and return a no-op Koa middleware.
|
|
59
|
+
*
|
|
60
|
+
* ```ts
|
|
61
|
+
* router.get('/users/:id',
|
|
62
|
+
* collector.koaMiddleware('/users/:id', 'GET', { sdk: { methodName: 'getUser' } }),
|
|
63
|
+
* getUserHandler,
|
|
64
|
+
* );
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
koaMiddleware(url: string, method: string, config: CollectorRouteConfig): KoaMiddleware;
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=koa.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"koa.d.ts","sourceRoot":"","sources":["../src/koa.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAE3D,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAE7E,+EAA+E;AAC/E,KAAK,aAAa,GAAG,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AAEvF;;;;;GAKG;AACH,qBAAa,eAAgB,SAAQ,YAAY;IAC/C;;;;;;;;;OASG;IACH,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,oBAAoB,GAAG,aAAa;CAIxF"}
|
package/dist/koa.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tesseract Koa integration.
|
|
3
|
+
*
|
|
4
|
+
* Re-exports `SDKCollector` and adds a `KoaSDKCollector` subclass with a
|
|
5
|
+
* `.koaMiddleware()` helper that registers a route with the collector and
|
|
6
|
+
* returns a no-op Koa middleware for inline placement with `@koa/router`.
|
|
7
|
+
*
|
|
8
|
+
* Because Koa has no route-registration hook, routes are registered with the
|
|
9
|
+
* collector explicitly. Generation is triggered by calling
|
|
10
|
+
* `collector.tryGenerate()` after all routes are set up.
|
|
11
|
+
*
|
|
12
|
+
* ```ts
|
|
13
|
+
* import Koa from 'koa';
|
|
14
|
+
* import Router from '@koa/router';
|
|
15
|
+
* import { KoaSDKCollector } from '@apollo-deploy/tesseract/koa';
|
|
16
|
+
*
|
|
17
|
+
* const app = new Koa();
|
|
18
|
+
* const router = new Router();
|
|
19
|
+
* const collector = new KoaSDKCollector({
|
|
20
|
+
* info: { title: 'My API', version: '1.0.0', baseUrl: 'https://api.example.com' },
|
|
21
|
+
* output: './packages/api-sdk',
|
|
22
|
+
* });
|
|
23
|
+
*
|
|
24
|
+
* collector.domain('/users', { domain: 'users', description: 'User management' });
|
|
25
|
+
*
|
|
26
|
+
* router.get('/users/:id',
|
|
27
|
+
* collector.koaMiddleware('/users/:id', 'GET', { sdk: { methodName: 'getUser' } }),
|
|
28
|
+
* getUserHandler,
|
|
29
|
+
* );
|
|
30
|
+
*
|
|
31
|
+
* app.use(router.routes());
|
|
32
|
+
*
|
|
33
|
+
* // After all routes are registered:
|
|
34
|
+
* if (await collector.tryGenerate()) process.exit(0);
|
|
35
|
+
*
|
|
36
|
+
* app.listen(3000);
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* **Triggering generation:**
|
|
40
|
+
* ```bash
|
|
41
|
+
* TESSERACT_GENERATE=1 node dist/app.js
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
import { SDKCollector } from './collector.js';
|
|
45
|
+
export { SDKCollector } from './collector.js';
|
|
46
|
+
/**
|
|
47
|
+
* `SDKCollector` subclass with a Koa-specific `.koaMiddleware()` helper.
|
|
48
|
+
*
|
|
49
|
+
* `.koaMiddleware()` registers a route with the collector **and** returns a
|
|
50
|
+
* no-op Koa middleware for inline placement with `@koa/router`.
|
|
51
|
+
*/
|
|
52
|
+
export class KoaSDKCollector extends SDKCollector {
|
|
53
|
+
/**
|
|
54
|
+
* Register a route with the collector and return a no-op Koa middleware.
|
|
55
|
+
*
|
|
56
|
+
* ```ts
|
|
57
|
+
* router.get('/users/:id',
|
|
58
|
+
* collector.koaMiddleware('/users/:id', 'GET', { sdk: { methodName: 'getUser' } }),
|
|
59
|
+
* getUserHandler,
|
|
60
|
+
* );
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
koaMiddleware(url, method, config) {
|
|
64
|
+
this.route(url, method, config);
|
|
65
|
+
return (_ctx, next) => next();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=koa.js.map
|
package/dist/koa.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"koa.js","sourceRoot":"","sources":["../src/koa.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAG9C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAM9C;;;;;GAKG;AACH,MAAM,OAAO,eAAgB,SAAQ,YAAY;IAC/C;;;;;;;;;OASG;IACH,aAAa,CAAC,GAAW,EAAE,MAAc,EAAE,MAA4B;QACrE,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;QAChC,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC;IAChC,CAAC;CACF"}
|
package/dist/nestjs.d.ts
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tesseract NestJS integration.
|
|
3
|
+
*
|
|
4
|
+
* Provides:
|
|
5
|
+
* - `@SDKMethod(config)` — method decorator to attach SDK metadata to a controller handler
|
|
6
|
+
* - `@SDKDomain(config)` — class decorator to attach domain metadata to a controller
|
|
7
|
+
* - `collectFromNestControllers(controllers, collector)` — reads decorator metadata and
|
|
8
|
+
* registers routes with an `SDKCollector`
|
|
9
|
+
*
|
|
10
|
+
* Requires `reflect-metadata` (already a standard NestJS dependency) and
|
|
11
|
+
* `emitDecoratorMetadata: true` in `tsconfig.json`.
|
|
12
|
+
*
|
|
13
|
+
* ```ts
|
|
14
|
+
* // users.controller.ts
|
|
15
|
+
* import { Controller, Get, Post, Param, Body } from '@nestjs/common';
|
|
16
|
+
* import { SDKMethod, SDKDomain } from '@apollo-deploy/tesseract/nestjs';
|
|
17
|
+
*
|
|
18
|
+
* @Controller('users')
|
|
19
|
+
* @SDKDomain({ domain: 'users', description: 'User management' })
|
|
20
|
+
* export class UsersController {
|
|
21
|
+
* @Get(':id')
|
|
22
|
+
* @SDKMethod({ methodName: 'getUser', schema: { response: { 200: { $ref: 'User' } } } })
|
|
23
|
+
* getUser(@Param('id') id: string) { ... }
|
|
24
|
+
*
|
|
25
|
+
* @Post()
|
|
26
|
+
* @SDKMethod({ methodName: 'createUser' })
|
|
27
|
+
* createUser(@Body() body: CreateUserDto) { ... }
|
|
28
|
+
* }
|
|
29
|
+
*
|
|
30
|
+
* // main.ts
|
|
31
|
+
* import { NestFactory } from '@nestjs/core';
|
|
32
|
+
* import { AppModule } from './app.module';
|
|
33
|
+
* import { SDKCollector, collectFromNestControllers } from '@apollo-deploy/tesseract/nestjs';
|
|
34
|
+
* import { UsersController } from './users/users.controller';
|
|
35
|
+
*
|
|
36
|
+
* async function bootstrap() {
|
|
37
|
+
* const app = await NestFactory.create(AppModule);
|
|
38
|
+
* await app.init();
|
|
39
|
+
*
|
|
40
|
+
* if (process.env.TESSERACT_GENERATE) {
|
|
41
|
+
* const collector = new SDKCollector({
|
|
42
|
+
* info: { title: 'My API', version: '1.0.0', baseUrl: 'https://api.example.com' },
|
|
43
|
+
* output: './packages/api-sdk',
|
|
44
|
+
* });
|
|
45
|
+
* collectFromNestControllers([UsersController], collector);
|
|
46
|
+
* await collector.generate();
|
|
47
|
+
* await app.close();
|
|
48
|
+
* process.exit(0);
|
|
49
|
+
* }
|
|
50
|
+
*
|
|
51
|
+
* await app.listen(3000);
|
|
52
|
+
* }
|
|
53
|
+
* bootstrap();
|
|
54
|
+
* ```
|
|
55
|
+
*
|
|
56
|
+
* **Triggering generation:**
|
|
57
|
+
* ```bash
|
|
58
|
+
* TESSERACT_GENERATE=1 node dist/main.js
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
declare global {
|
|
62
|
+
namespace Reflect {
|
|
63
|
+
function defineMetadata(key: unknown, value: unknown, target: object, propertyKey?: string | symbol): void;
|
|
64
|
+
function getMetadata(key: unknown, target: object, propertyKey?: string | symbol): unknown;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
import { SDKCollector } from './collector.js';
|
|
68
|
+
import type { SDKRouteConfig, SDKModuleConfig } from './types/sdk-module.js';
|
|
69
|
+
import type { ManifestRouteSchema } from './types/manifest.js';
|
|
70
|
+
export { SDKCollector } from './collector.js';
|
|
71
|
+
export type { CollectorOptions, CollectorRouteConfig } from './collector.js';
|
|
72
|
+
export interface SDKMethodConfig extends SDKRouteConfig {
|
|
73
|
+
/** Explicit JSON Schema for this route — params, querystring, body, headers, response. */
|
|
74
|
+
schema?: ManifestRouteSchema;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Method decorator — attach SDK generation config to a NestJS controller handler.
|
|
78
|
+
*
|
|
79
|
+
* ```ts
|
|
80
|
+
* @Get(':id')
|
|
81
|
+
* @SDKMethod({ methodName: 'getUser', schema: { response: { 200: { $ref: 'User' } } } })
|
|
82
|
+
* getUser(@Param('id') id: string) { ... }
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
export declare function SDKMethod(config: SDKMethodConfig): MethodDecorator;
|
|
86
|
+
/**
|
|
87
|
+
* Class decorator — attach domain metadata to a NestJS controller.
|
|
88
|
+
*
|
|
89
|
+
* ```ts
|
|
90
|
+
* @Controller('users')
|
|
91
|
+
* @SDKDomain({ domain: 'users', description: 'User management' })
|
|
92
|
+
* export class UsersController { ... }
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
export declare function SDKDomain(config: Omit<SDKModuleConfig, 'prefix'>): ClassDecorator;
|
|
96
|
+
/**
|
|
97
|
+
* Read `@SDKMethod` and `@SDKDomain` metadata from the given controller classes
|
|
98
|
+
* and register each decorated route with the provided `SDKCollector`.
|
|
99
|
+
*
|
|
100
|
+
* Pass the controller **classes** (not instances).
|
|
101
|
+
*
|
|
102
|
+
* ```ts
|
|
103
|
+
* collectFromNestControllers([UsersController, OrdersController], collector);
|
|
104
|
+
* ```
|
|
105
|
+
*/
|
|
106
|
+
export declare function collectFromNestControllers(controllers: Function[], collector: SDKCollector): void;
|
|
107
|
+
//# sourceMappingURL=nestjs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nestjs.d.ts","sourceRoot":"","sources":["../src/nestjs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2DG;AAKH,OAAO,CAAC,MAAM,CAAC;IAEb,UAAU,OAAO,CAAC;QAChB,SAAS,cAAc,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;QAC3G,SAAS,WAAW,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;KAC5F;CACF;AAED,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE/D,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAyB7E,MAAM,WAAW,eAAgB,SAAQ,cAAc;IACrD,0FAA0F;IAC1F,MAAM,CAAC,EAAE,mBAAmB,CAAC;CAC9B;AAED;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,eAAe,GAAG,eAAe,CAIlE;AAED;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,IAAI,CAAC,eAAe,EAAE,QAAQ,CAAC,GAAG,cAAc,CAIjF;AAID;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CACxC,WAAW,EAAE,QAAQ,EAAE,EACvB,SAAS,EAAE,YAAY,GACtB,IAAI,CAkCN"}
|
package/dist/nestjs.js
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tesseract NestJS integration.
|
|
3
|
+
*
|
|
4
|
+
* Provides:
|
|
5
|
+
* - `@SDKMethod(config)` — method decorator to attach SDK metadata to a controller handler
|
|
6
|
+
* - `@SDKDomain(config)` — class decorator to attach domain metadata to a controller
|
|
7
|
+
* - `collectFromNestControllers(controllers, collector)` — reads decorator metadata and
|
|
8
|
+
* registers routes with an `SDKCollector`
|
|
9
|
+
*
|
|
10
|
+
* Requires `reflect-metadata` (already a standard NestJS dependency) and
|
|
11
|
+
* `emitDecoratorMetadata: true` in `tsconfig.json`.
|
|
12
|
+
*
|
|
13
|
+
* ```ts
|
|
14
|
+
* // users.controller.ts
|
|
15
|
+
* import { Controller, Get, Post, Param, Body } from '@nestjs/common';
|
|
16
|
+
* import { SDKMethod, SDKDomain } from '@apollo-deploy/tesseract/nestjs';
|
|
17
|
+
*
|
|
18
|
+
* @Controller('users')
|
|
19
|
+
* @SDKDomain({ domain: 'users', description: 'User management' })
|
|
20
|
+
* export class UsersController {
|
|
21
|
+
* @Get(':id')
|
|
22
|
+
* @SDKMethod({ methodName: 'getUser', schema: { response: { 200: { $ref: 'User' } } } })
|
|
23
|
+
* getUser(@Param('id') id: string) { ... }
|
|
24
|
+
*
|
|
25
|
+
* @Post()
|
|
26
|
+
* @SDKMethod({ methodName: 'createUser' })
|
|
27
|
+
* createUser(@Body() body: CreateUserDto) { ... }
|
|
28
|
+
* }
|
|
29
|
+
*
|
|
30
|
+
* // main.ts
|
|
31
|
+
* import { NestFactory } from '@nestjs/core';
|
|
32
|
+
* import { AppModule } from './app.module';
|
|
33
|
+
* import { SDKCollector, collectFromNestControllers } from '@apollo-deploy/tesseract/nestjs';
|
|
34
|
+
* import { UsersController } from './users/users.controller';
|
|
35
|
+
*
|
|
36
|
+
* async function bootstrap() {
|
|
37
|
+
* const app = await NestFactory.create(AppModule);
|
|
38
|
+
* await app.init();
|
|
39
|
+
*
|
|
40
|
+
* if (process.env.TESSERACT_GENERATE) {
|
|
41
|
+
* const collector = new SDKCollector({
|
|
42
|
+
* info: { title: 'My API', version: '1.0.0', baseUrl: 'https://api.example.com' },
|
|
43
|
+
* output: './packages/api-sdk',
|
|
44
|
+
* });
|
|
45
|
+
* collectFromNestControllers([UsersController], collector);
|
|
46
|
+
* await collector.generate();
|
|
47
|
+
* await app.close();
|
|
48
|
+
* process.exit(0);
|
|
49
|
+
* }
|
|
50
|
+
*
|
|
51
|
+
* await app.listen(3000);
|
|
52
|
+
* }
|
|
53
|
+
* bootstrap();
|
|
54
|
+
* ```
|
|
55
|
+
*
|
|
56
|
+
* **Triggering generation:**
|
|
57
|
+
* ```bash
|
|
58
|
+
* TESSERACT_GENERATE=1 node dist/main.js
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
export { SDKCollector } from './collector.js';
|
|
62
|
+
// ── Metadata keys ─────────────────────────────────────────────────────────────
|
|
63
|
+
const SDK_METHOD_META = Symbol('tesseract:sdkMethod');
|
|
64
|
+
const SDK_DOMAIN_META = Symbol('tesseract:sdkDomain');
|
|
65
|
+
// NestJS stores route metadata under these well-known string keys
|
|
66
|
+
const NEST_PATH_META = 'path';
|
|
67
|
+
const NEST_METHOD_META = 'method';
|
|
68
|
+
// NestJS RequestMethod enum values
|
|
69
|
+
const NEST_HTTP_METHODS = {
|
|
70
|
+
0: 'GET',
|
|
71
|
+
1: 'POST',
|
|
72
|
+
2: 'PUT',
|
|
73
|
+
3: 'DELETE',
|
|
74
|
+
4: 'PATCH',
|
|
75
|
+
5: 'ALL',
|
|
76
|
+
6: 'OPTIONS',
|
|
77
|
+
7: 'HEAD',
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* Method decorator — attach SDK generation config to a NestJS controller handler.
|
|
81
|
+
*
|
|
82
|
+
* ```ts
|
|
83
|
+
* @Get(':id')
|
|
84
|
+
* @SDKMethod({ methodName: 'getUser', schema: { response: { 200: { $ref: 'User' } } } })
|
|
85
|
+
* getUser(@Param('id') id: string) { ... }
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
export function SDKMethod(config) {
|
|
89
|
+
return (target, propertyKey) => {
|
|
90
|
+
Reflect.defineMetadata(SDK_METHOD_META, config, target, propertyKey);
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Class decorator — attach domain metadata to a NestJS controller.
|
|
95
|
+
*
|
|
96
|
+
* ```ts
|
|
97
|
+
* @Controller('users')
|
|
98
|
+
* @SDKDomain({ domain: 'users', description: 'User management' })
|
|
99
|
+
* export class UsersController { ... }
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
export function SDKDomain(config) {
|
|
103
|
+
return (target) => {
|
|
104
|
+
Reflect.defineMetadata(SDK_DOMAIN_META, config, target);
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
// ── Route collector ───────────────────────────────────────────────────────────
|
|
108
|
+
/**
|
|
109
|
+
* Read `@SDKMethod` and `@SDKDomain` metadata from the given controller classes
|
|
110
|
+
* and register each decorated route with the provided `SDKCollector`.
|
|
111
|
+
*
|
|
112
|
+
* Pass the controller **classes** (not instances).
|
|
113
|
+
*
|
|
114
|
+
* ```ts
|
|
115
|
+
* collectFromNestControllers([UsersController, OrdersController], collector);
|
|
116
|
+
* ```
|
|
117
|
+
*/
|
|
118
|
+
export function collectFromNestControllers(controllers, collector) {
|
|
119
|
+
for (const ControllerClass of controllers) {
|
|
120
|
+
// NestJS stores the controller path prefix under 'path' metadata on the class
|
|
121
|
+
const rawPrefix = (Reflect.getMetadata(NEST_PATH_META, ControllerClass) ?? '');
|
|
122
|
+
const prefix = rawPrefix.startsWith('/') ? rawPrefix : '/' + rawPrefix;
|
|
123
|
+
const domainMeta = Reflect.getMetadata(SDK_DOMAIN_META, ControllerClass);
|
|
124
|
+
collector.domain(prefix, domainMeta);
|
|
125
|
+
const prototype = ControllerClass.prototype;
|
|
126
|
+
for (const key of Object.getOwnPropertyNames(prototype)) {
|
|
127
|
+
if (key === 'constructor')
|
|
128
|
+
continue;
|
|
129
|
+
const sdkConfig = Reflect.getMetadata(SDK_METHOD_META, prototype, key);
|
|
130
|
+
if (!sdkConfig || sdkConfig.exclude)
|
|
131
|
+
continue;
|
|
132
|
+
const nestMethod = Reflect.getMetadata(NEST_METHOD_META, prototype, key);
|
|
133
|
+
const nestPath = (Reflect.getMetadata(NEST_PATH_META, prototype, key) ?? '');
|
|
134
|
+
const method = nestMethod !== undefined ? (NEST_HTTP_METHODS[nestMethod] ?? 'GET') : 'GET';
|
|
135
|
+
const rawTail = nestPath.startsWith('/') ? nestPath : '/' + nestPath;
|
|
136
|
+
const url = prefix === '/' ? rawTail : prefix + (rawTail === '/' ? '' : rawTail);
|
|
137
|
+
const { schema, ...routeSdk } = sdkConfig;
|
|
138
|
+
const routeConfig = { sdk: routeSdk, schema };
|
|
139
|
+
collector.route(url, method, routeConfig);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=nestjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nestjs.js","sourceRoot":"","sources":["../src/nestjs.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"intake.d.ts","sourceRoot":"","sources":["../../src/pipeline/intake.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,KAAK,EAIV,UAAU,EACX,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EACV,KAAK,EAQN,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAKzD,wBAAgB,MAAM,CAAC,MAAM,EAAE,cAAc,GAAG,KAAK,
|
|
1
|
+
{"version":3,"file":"intake.d.ts","sourceRoot":"","sources":["../../src/pipeline/intake.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,KAAK,EAIV,UAAU,EACX,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EACV,KAAK,EAQN,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAKzD,wBAAgB,MAAM,CAAC,MAAM,EAAE,cAAc,GAAG,KAAK,CA+EpD;AAmKD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CA2D3D"}
|
package/dist/pipeline/intake.js
CHANGED
|
@@ -10,16 +10,21 @@ import { validateManifest } from '../types/manifest.js';
|
|
|
10
10
|
// ── Entry point ──────────────────────────────────────────────────────────────
|
|
11
11
|
export function intake(config) {
|
|
12
12
|
let manifest;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
manifest = JSON.parse(raw);
|
|
13
|
+
if (config.manifest) {
|
|
14
|
+
manifest = config.manifest;
|
|
16
15
|
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
16
|
+
else {
|
|
17
|
+
try {
|
|
18
|
+
const raw = readFileSync(config.input, 'utf-8');
|
|
19
|
+
manifest = JSON.parse(raw);
|
|
20
|
+
}
|
|
21
|
+
catch (err) {
|
|
22
|
+
throw new Error(`Failed to read manifest at "${config.input}": ${err instanceof Error ? err.message : String(err)}`);
|
|
23
|
+
}
|
|
24
|
+
const validation = validateManifest(manifest);
|
|
25
|
+
if (!validation.valid) {
|
|
26
|
+
throw new Error(`Invalid manifest:\n ${validation.errors.join('\n ')}`);
|
|
27
|
+
}
|
|
23
28
|
}
|
|
24
29
|
const schemaPackage = manifest.schemaPackage;
|
|
25
30
|
const importPath = schemaPackage?.importPath ?? schemaPackage?.name;
|
|
@@ -47,7 +52,7 @@ export function intake(config) {
|
|
|
47
52
|
const schemas = [...externalSchemas, ...buildSchemas(localDefinitions)];
|
|
48
53
|
const groups = [];
|
|
49
54
|
const allInlineSchemas = [];
|
|
50
|
-
for (const domain of manifest.domains
|
|
55
|
+
for (const domain of manifest.domains) {
|
|
51
56
|
const { group, inlineSchemas } = buildOperationGroup(domain, schemas);
|
|
52
57
|
groups.push(group);
|
|
53
58
|
allInlineSchemas.push(...inlineSchemas);
|
|
@@ -87,6 +92,10 @@ function collectExternalRefNames(manifest) {
|
|
|
87
92
|
scanSchemaForExternalRefs(resp, names);
|
|
88
93
|
}
|
|
89
94
|
}
|
|
95
|
+
// Register sseReturnType as an external ref so it gets an import stub
|
|
96
|
+
if (route.sdk?.sseReturnType) {
|
|
97
|
+
names.add(pascalCase(route.sdk.sseReturnType));
|
|
98
|
+
}
|
|
90
99
|
}
|
|
91
100
|
}
|
|
92
101
|
return names;
|
|
@@ -176,7 +185,7 @@ function jsonSchemaToDefinition(name, schema) {
|
|
|
176
185
|
};
|
|
177
186
|
}
|
|
178
187
|
if (s.type === 'object') {
|
|
179
|
-
const required = new Set(s.required
|
|
188
|
+
const required = new Set(Array.isArray(s.required) ? s.required : []);
|
|
180
189
|
const rawProps = s.properties ?? {};
|
|
181
190
|
const properties = Object.entries(rawProps).map(([propName, propSchema]) => {
|
|
182
191
|
const ps = propSchema;
|
|
@@ -226,7 +235,7 @@ export function jsonSchemaToType(schema) {
|
|
|
226
235
|
return s.anyOf.map(jsonSchemaToType).join(' | ');
|
|
227
236
|
const type = s.type;
|
|
228
237
|
if (type === 'object') {
|
|
229
|
-
const required = new Set(s.required
|
|
238
|
+
const required = new Set(Array.isArray(s.required) ? s.required : []);
|
|
230
239
|
const rawProps = s.properties;
|
|
231
240
|
if (rawProps) {
|
|
232
241
|
const entries = Object.entries(rawProps)
|
|
@@ -298,14 +307,14 @@ function isNullableRefUnion(members) {
|
|
|
298
307
|
function buildOperationGroup(domain, _schemas) {
|
|
299
308
|
const domainName = domain.domain ?? domain.prefix;
|
|
300
309
|
const name = camelCase(domainName);
|
|
301
|
-
const fileName = kebabCase(domainName);
|
|
310
|
+
const fileName = domain.fileName ?? kebabCase(domainName);
|
|
302
311
|
const allInlineSchemas = [];
|
|
303
312
|
const operations = [];
|
|
304
313
|
for (const route of domain.routes) {
|
|
305
|
-
if (route.sdk?.exclude
|
|
314
|
+
if (route.sdk?.exclude)
|
|
306
315
|
continue;
|
|
307
316
|
const { operation, inlineSchemas } = buildOperation(route, name, domain.prefix);
|
|
308
|
-
operations.push(operation);
|
|
317
|
+
operations.push({ ...operation, visibility: route.sdk?.internal ? 'internal' : 'public' });
|
|
309
318
|
for (const s of inlineSchemas) {
|
|
310
319
|
allInlineSchemas.push({
|
|
311
320
|
...s,
|
|
@@ -320,6 +329,7 @@ function buildOperationGroup(domain, _schemas) {
|
|
|
320
329
|
interfaceName: pascalCase(domainName) + 'API',
|
|
321
330
|
factoryName: 'create' + pascalCase(domainName) + 'API',
|
|
322
331
|
description: domain.description,
|
|
332
|
+
visibility: domain.stability === 'internal' ? 'internal' : 'public',
|
|
323
333
|
operations,
|
|
324
334
|
},
|
|
325
335
|
inlineSchemas: allInlineSchemas,
|
|
@@ -336,7 +346,7 @@ function buildOperation(route, groupName, domainPrefix) {
|
|
|
336
346
|
const inlineSchemas = [];
|
|
337
347
|
const name = route.sdk?.methodName ??
|
|
338
348
|
deriveCleanMethodName({
|
|
339
|
-
operationId: route.schema?.operationId,
|
|
349
|
+
operationId: route.schema?.operationId ?? route.sdk?.operationId,
|
|
340
350
|
summary: route.schema?.summary,
|
|
341
351
|
path: route.url.replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g, '{$1}'),
|
|
342
352
|
httpMethod: httpMethod.toLowerCase(),
|
|
@@ -379,7 +389,11 @@ function buildOperation(route, groupName, domainPrefix) {
|
|
|
379
389
|
requestBody = {
|
|
380
390
|
type: bodyType,
|
|
381
391
|
required: true,
|
|
382
|
-
contentType: route.sdk?.transport === 'multipart'
|
|
392
|
+
contentType: route.sdk?.transport === 'multipart'
|
|
393
|
+
? 'multipart/form-data'
|
|
394
|
+
: route.sdk?.transport === 'binary'
|
|
395
|
+
? 'application/octet-stream'
|
|
396
|
+
: 'application/json',
|
|
383
397
|
};
|
|
384
398
|
}
|
|
385
399
|
// Query type
|
|
@@ -431,7 +445,10 @@ function buildOperation(route, groupName, domainPrefix) {
|
|
|
431
445
|
responseType,
|
|
432
446
|
statusCode,
|
|
433
447
|
deprecated: route.sdk?.deprecated ? true : undefined,
|
|
448
|
+
deprecationMessage: typeof route.sdk?.deprecated === 'string' ? route.sdk.deprecated : undefined,
|
|
449
|
+
timeout: route.sdk?.timeout,
|
|
434
450
|
isEventStream: route.sse ?? route.sdk?.transport === 'stream',
|
|
451
|
+
sseReturnType: route.sdk?.sseReturnType,
|
|
435
452
|
},
|
|
436
453
|
inlineSchemas,
|
|
437
454
|
};
|
|
@@ -458,8 +475,10 @@ function extractPathParams(url, paramsSchema) {
|
|
|
458
475
|
if (urlParams.length === 0)
|
|
459
476
|
return [];
|
|
460
477
|
const s = paramsSchema;
|
|
461
|
-
const props = s?.properties
|
|
462
|
-
|
|
478
|
+
const props = typeof s?.properties === 'object' && !Array.isArray(s?.properties)
|
|
479
|
+
? s.properties
|
|
480
|
+
: undefined;
|
|
481
|
+
const required = new Set(Array.isArray(s?.required) ? s.required : []);
|
|
463
482
|
return urlParams.map((paramName) => {
|
|
464
483
|
const propSchema = props?.[paramName];
|
|
465
484
|
return {
|
|
@@ -479,7 +498,7 @@ function extractQueryParams(querySchema) {
|
|
|
479
498
|
const s = querySchema;
|
|
480
499
|
if (s.type !== 'object' || !s.properties)
|
|
481
500
|
return [];
|
|
482
|
-
const required = new Set(s.required
|
|
501
|
+
const required = new Set(Array.isArray(s.required) ? s.required : []);
|
|
483
502
|
const props = s.properties;
|
|
484
503
|
return Object.entries(props).map(([name, schema]) => ({
|
|
485
504
|
name: camelCase(name),
|
|
@@ -504,7 +523,7 @@ function extractHeaderParams(headersSchema) {
|
|
|
504
523
|
const s = headersSchema;
|
|
505
524
|
if (s.type !== 'object' || !s.properties)
|
|
506
525
|
return [];
|
|
507
|
-
const required = new Set(s.required
|
|
526
|
+
const required = new Set(Array.isArray(s.required) ? s.required : []);
|
|
508
527
|
const props = s.properties;
|
|
509
528
|
return Object.entries(props)
|
|
510
529
|
.filter(([name]) => !SYSTEM_HEADERS.has(name.toLowerCase()))
|