@get-enlace/nest 0.0.1 → 0.0.4
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/LICENSE +21 -0
- package/README.md +63 -0
- package/dist/constants.d.ts +3 -1
- package/dist/constants.js +3 -1
- package/dist/enlace.module.d.ts +52 -20
- package/dist/enlace.module.js +84 -38
- package/dist/specRoute.d.ts +27 -0
- package/dist/specRoute.js +67 -0
- package/package.json +2 -2
- package/dist/enlace.controller.d.ts +0 -15
- package/dist/enlace.controller.js +0 -44
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 BugDiver Technologies
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# @get-enlace/nest
|
|
2
|
+
|
|
3
|
+
NestJS adapter for [Enlace](https://github.com/get-enlace/enlace-ui) — a visual,
|
|
4
|
+
chained-execution canvas for any OpenAPI-documented API. This adapter's job is intentionally
|
|
5
|
+
small: it serves the OpenAPI document and the `@get-enlace/ui` static bundle. Everything else
|
|
6
|
+
(wiring up a chain, running it, credentials) happens client-side, in the browser.
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install @get-enlace/nest
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
|
|
16
|
+
### With `@nestjs/swagger` (generated spec)
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
// app.module.ts — just import, no config needed
|
|
20
|
+
import { EnlaceModule } from '@get-enlace/nest';
|
|
21
|
+
|
|
22
|
+
@Module({ imports: [EnlaceModule] })
|
|
23
|
+
export class AppModule {}
|
|
24
|
+
|
|
25
|
+
// main.ts — one line after the app is built
|
|
26
|
+
const app = await NestFactory.create(AppModule);
|
|
27
|
+
const doc = SwaggerModule.createDocument(app, config);
|
|
28
|
+
EnlaceModule.setSpec(app, doc);
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### With a static spec (file path or pre-parsed object)
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
@Module({ imports: [EnlaceModule.forRoot({ spec: './openapi.json' })] })
|
|
35
|
+
export class AppModule {}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`spec` accepts a file path, a URL, or an already-parsed OpenAPI 3.x object.
|
|
39
|
+
|
|
40
|
+
### What Enlace needs from your spec
|
|
41
|
+
|
|
42
|
+
Whatever produces it, `servers[0].url` needs to be your API's real, reachable base URL — Enlace
|
|
43
|
+
sends every request in a chain straight there from the browser. `operationId` is optional (Enlace
|
|
44
|
+
falls back to a synthetic `METHOD /path` label without one) but is what shows on the node and in
|
|
45
|
+
the operation search — `@nestjs/swagger` sets one per route by default (`ControllerName_methodName`).
|
|
46
|
+
|
|
47
|
+
Already serving `@nestjs/swagger`'s own Swagger UI from that same document? Handing it to Enlace
|
|
48
|
+
too doesn't change how that keeps working — they're independent consumers of the same object.
|
|
49
|
+
|
|
50
|
+
### Custom mount path
|
|
51
|
+
|
|
52
|
+
By default the canvas is at `/enlace`. To change it:
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
@Module({ imports: [EnlaceModule.forRoot({ path: 'canvas' })] })
|
|
56
|
+
export class AppModule {}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Learn more
|
|
60
|
+
|
|
61
|
+
See the [`enlace-js` repo](https://github.com/get-enlace/enlace-js) for the other adapters in
|
|
62
|
+
this family, and the [`enlace-ui` repo](https://github.com/get-enlace/enlace-ui) for how Enlace
|
|
63
|
+
itself works.
|
package/dist/constants.d.ts
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
|
-
/** DI token for injecting a module instance's {@link EnlaceOptions} into its
|
|
1
|
+
/** DI token for injecting a module instance's {@link EnlaceOptions} into its spec route. */
|
|
2
2
|
export declare const ENLACE_OPTIONS: unique symbol;
|
|
3
|
+
/** DI token for the mount path this instance serves at (e.g. `'enlace'`, `'canvas'`). */
|
|
4
|
+
export declare const ENLACE_MOUNT_PATH: unique symbol;
|
package/dist/constants.js
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
|
-
/** DI token for injecting a module instance's {@link EnlaceOptions} into its
|
|
1
|
+
/** DI token for injecting a module instance's {@link EnlaceOptions} into its spec route. */
|
|
2
2
|
export const ENLACE_OPTIONS = Symbol('ENLACE_OPTIONS');
|
|
3
|
+
/** DI token for the mount path this instance serves at (e.g. `'enlace'`, `'canvas'`). */
|
|
4
|
+
export const ENLACE_MOUNT_PATH = Symbol('ENLACE_MOUNT_PATH');
|
package/dist/enlace.module.d.ts
CHANGED
|
@@ -1,36 +1,68 @@
|
|
|
1
|
-
import { DynamicModule } from '@nestjs/common';
|
|
1
|
+
import { DynamicModule, type INestApplication } from '@nestjs/common';
|
|
2
2
|
import type { SpecSource } from './specLoader.js';
|
|
3
3
|
export interface EnlaceOptions {
|
|
4
|
-
/** A file/URL path or an already-parsed object — the only input this needs is a valid OpenAPI 3.x document, however it's produced or served. */
|
|
5
|
-
spec: SpecSource;
|
|
6
4
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* A file/URL path or an already-parsed object — the only input this needs
|
|
6
|
+
* is a valid OpenAPI 3.x document, however it's produced or served.
|
|
9
7
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
|
|
8
|
+
* Can be omitted at module-definition time and set later via
|
|
9
|
+
* `EnlaceModule.setSpec(app, spec)` in `main.ts` — useful when the spec
|
|
10
|
+
* is generated by `@nestjs/swagger` (which needs the app instance to exist
|
|
11
|
+
* before it can build the document, later than module imports are evaluated).
|
|
12
|
+
*/
|
|
13
|
+
spec?: SpecSource;
|
|
14
|
+
/**
|
|
15
|
+
* Mount path for the canvas, e.g. `'canvas'` serves the UI at `/canvas`
|
|
16
|
+
* and the spec at `/canvas/api/spec`. Defaults to `'enlace'`.
|
|
17
|
+
*
|
|
18
|
+
* Only available via `forRoot({ path })` — a plain `imports: [EnlaceModule]`
|
|
19
|
+
* always mounts at `/enlace`.
|
|
15
20
|
*/
|
|
16
21
|
path?: string;
|
|
17
22
|
}
|
|
18
23
|
/**
|
|
19
|
-
* Mounts the Enlace canvas.
|
|
24
|
+
* Mounts the Enlace canvas. Import directly for zero-config defaults
|
|
25
|
+
* (mounts at `/enlace`), or call `forRoot()` to customise the mount path:
|
|
20
26
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
27
|
+
* // Zero-config — mounts at /enlace, use setSpec() in main.ts:
|
|
28
|
+
* @Module({ imports: [EnlaceModule] })
|
|
29
|
+
*
|
|
30
|
+
* // With a static spec file:
|
|
31
|
+
* @Module({ imports: [EnlaceModule.forRoot({ spec: './openapi.json' })] })
|
|
32
|
+
*
|
|
33
|
+
* // Custom mount path:
|
|
34
|
+
* @Module({ imports: [EnlaceModule.forRoot({ path: 'canvas' })] })
|
|
23
35
|
*
|
|
24
36
|
* This adapter's job is deliberately small — per ARCHITECTURE.md's MVP
|
|
25
37
|
* model, execution runs entirely client-side in @get-enlace/ui, so there's
|
|
26
38
|
* no `/api/run` or `/api/credentials` here at all. All this does is:
|
|
27
|
-
* - serve the raw OpenAPI document
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
39
|
+
* - serve the raw OpenAPI document at `<mount>/api/spec`, registered
|
|
40
|
+
* directly on the underlying HTTP adapter (see specRoute.ts) rather
|
|
41
|
+
* than as a Nest controller, so it isn't gated behind whatever global
|
|
42
|
+
* guards/interceptors the host app has registered — true zero-config,
|
|
43
|
+
* no exception needed on the host app's side
|
|
44
|
+
* - serve the built UI bundle via @nestjs/serve-static
|
|
33
45
|
*/
|
|
34
46
|
export declare class EnlaceModule {
|
|
35
|
-
|
|
47
|
+
/**
|
|
48
|
+
* Set the OpenAPI spec after the app is built — the one-liner for
|
|
49
|
+
* `@nestjs/swagger` users:
|
|
50
|
+
*
|
|
51
|
+
* EnlaceModule.setSpec(app, SwaggerModule.createDocument(app, config));
|
|
52
|
+
*
|
|
53
|
+
* Works because SpecRouteRegistrar reads `options.spec` fresh on every
|
|
54
|
+
* request (via `loadSpec`), not a snapshot taken at module-definition
|
|
55
|
+
* time — so whatever's assigned here is what gets served.
|
|
56
|
+
*/
|
|
57
|
+
static setSpec(app: INestApplication, spec: SpecSource): void;
|
|
58
|
+
/**
|
|
59
|
+
* Optional explicit configuration — only needed when passing a static
|
|
60
|
+
* spec at module-definition time or customising the mount path. For
|
|
61
|
+
* `@nestjs/swagger` users, prefer the zero-config direct import +
|
|
62
|
+
* `setSpec()`.
|
|
63
|
+
*/
|
|
64
|
+
static forRoot(options?: {
|
|
65
|
+
spec?: SpecSource;
|
|
66
|
+
path?: string;
|
|
67
|
+
}): DynamicModule;
|
|
36
68
|
}
|
package/dist/enlace.module.js
CHANGED
|
@@ -4,67 +4,113 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
4
4
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
|
-
var EnlaceModule_1;
|
|
8
7
|
import { Module } from '@nestjs/common';
|
|
9
|
-
import { RouterModule } from '@nestjs/core';
|
|
10
8
|
import { ServeStaticModule } from '@nestjs/serve-static';
|
|
11
9
|
import path from 'node:path';
|
|
12
10
|
import { createRequire } from 'node:module';
|
|
13
|
-
import { ENLACE_OPTIONS } from './constants.js';
|
|
14
|
-
import {
|
|
11
|
+
import { ENLACE_MOUNT_PATH, ENLACE_OPTIONS } from './constants.js';
|
|
12
|
+
import { SpecRouteRegistrar } from './specRoute.js';
|
|
15
13
|
const require = createRequire(import.meta.url);
|
|
14
|
+
// Resolved once at module load — Node's own module resolution against the
|
|
15
|
+
// installed `@get-enlace/ui` package (a real dependency, see package.json),
|
|
16
|
+
// not a relative path into this monorepo. Works identically whether
|
|
17
|
+
// `@get-enlace/ui` got here via an npm workspace symlink (local dev) or a
|
|
18
|
+
// real `node_modules` install.
|
|
19
|
+
const uiPackageJson = require.resolve('@get-enlace/ui/package.json');
|
|
20
|
+
const uiDist = path.join(path.dirname(uiPackageJson), 'dist');
|
|
16
21
|
/**
|
|
17
|
-
*
|
|
22
|
+
* Internal module class used by `forRoot()` — keeps its DynamicModule
|
|
23
|
+
* metadata isolated from EnlaceModule's own @Module() defaults so
|
|
24
|
+
* providers aren't double-registered.
|
|
25
|
+
* @internal
|
|
26
|
+
*/
|
|
27
|
+
let EnlaceConfiguredModule = class EnlaceConfiguredModule {
|
|
28
|
+
};
|
|
29
|
+
EnlaceConfiguredModule = __decorate([
|
|
30
|
+
Module({})
|
|
31
|
+
], EnlaceConfiguredModule);
|
|
32
|
+
/**
|
|
33
|
+
* Mounts the Enlace canvas. Import directly for zero-config defaults
|
|
34
|
+
* (mounts at `/enlace`), or call `forRoot()` to customise the mount path:
|
|
35
|
+
*
|
|
36
|
+
* // Zero-config — mounts at /enlace, use setSpec() in main.ts:
|
|
37
|
+
* @Module({ imports: [EnlaceModule] })
|
|
18
38
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
39
|
+
* // With a static spec file:
|
|
40
|
+
* @Module({ imports: [EnlaceModule.forRoot({ spec: './openapi.json' })] })
|
|
41
|
+
*
|
|
42
|
+
* // Custom mount path:
|
|
43
|
+
* @Module({ imports: [EnlaceModule.forRoot({ path: 'canvas' })] })
|
|
21
44
|
*
|
|
22
45
|
* This adapter's job is deliberately small — per ARCHITECTURE.md's MVP
|
|
23
46
|
* model, execution runs entirely client-side in @get-enlace/ui, so there's
|
|
24
47
|
* no `/api/run` or `/api/credentials` here at all. All this does is:
|
|
25
|
-
* - serve the raw OpenAPI document
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
48
|
+
* - serve the raw OpenAPI document at `<mount>/api/spec`, registered
|
|
49
|
+
* directly on the underlying HTTP adapter (see specRoute.ts) rather
|
|
50
|
+
* than as a Nest controller, so it isn't gated behind whatever global
|
|
51
|
+
* guards/interceptors the host app has registered — true zero-config,
|
|
52
|
+
* no exception needed on the host app's side
|
|
53
|
+
* - serve the built UI bundle via @nestjs/serve-static
|
|
31
54
|
*/
|
|
32
|
-
let EnlaceModule =
|
|
55
|
+
let EnlaceModule = class EnlaceModule {
|
|
56
|
+
/**
|
|
57
|
+
* Set the OpenAPI spec after the app is built — the one-liner for
|
|
58
|
+
* `@nestjs/swagger` users:
|
|
59
|
+
*
|
|
60
|
+
* EnlaceModule.setSpec(app, SwaggerModule.createDocument(app, config));
|
|
61
|
+
*
|
|
62
|
+
* Works because SpecRouteRegistrar reads `options.spec` fresh on every
|
|
63
|
+
* request (via `loadSpec`), not a snapshot taken at module-definition
|
|
64
|
+
* time — so whatever's assigned here is what gets served.
|
|
65
|
+
*/
|
|
66
|
+
static setSpec(app, spec) {
|
|
67
|
+
const options = app.get(ENLACE_OPTIONS);
|
|
68
|
+
options.spec = spec;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Optional explicit configuration — only needed when passing a static
|
|
72
|
+
* spec at module-definition time or customising the mount path. For
|
|
73
|
+
* `@nestjs/swagger` users, prefer the zero-config direct import +
|
|
74
|
+
* `setSpec()`.
|
|
75
|
+
*/
|
|
33
76
|
static forRoot(options) {
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
// `@get-enlace/ui` package (a real dependency, see package.json) — not a
|
|
37
|
-
// relative path into this monorepo — so this works identically whether
|
|
38
|
-
// `@get-enlace/ui` got here via an npm workspace symlink (local dev) or
|
|
39
|
-
// a real `node_modules` install (anyone who installs @get-enlace/nest on
|
|
40
|
-
// its own). There's nothing to copy or build into this package itself;
|
|
41
|
-
// the bundle lives wherever @get-enlace/ui's own "files" field ships it
|
|
42
|
-
// (dist/), and it must already be built (`npm run build --workspace
|
|
43
|
-
// @get-enlace/ui`) before this resolves.
|
|
44
|
-
const uiPackageJson = require.resolve('@get-enlace/ui/package.json');
|
|
45
|
-
const uiDist = path.join(path.dirname(uiPackageJson), 'dist');
|
|
77
|
+
const opts = { spec: options?.spec, path: options?.path };
|
|
78
|
+
const mountPath = opts.path ?? 'enlace';
|
|
46
79
|
return {
|
|
47
|
-
|
|
80
|
+
// EnlaceConfiguredModule (not EnlaceModule) so the DynamicModule
|
|
81
|
+
// metadata doesn't merge with EnlaceModule's own @Module() defaults.
|
|
82
|
+
module: EnlaceConfiguredModule,
|
|
48
83
|
imports: [
|
|
49
84
|
ServeStaticModule.forRoot({
|
|
50
85
|
rootPath: uiDist,
|
|
51
86
|
serveRoot: `/${mountPath}`,
|
|
52
|
-
// Keeps the static middleware from shadowing EnlaceController's
|
|
53
|
-
// `/api/spec` route. `{*splat}` is path-to-regexp v8's wildcard
|
|
54
|
-
// syntax (the version @nestjs/serve-static v5 bundles) — the
|
|
55
|
-
// older unnamed `(.*)` capture group it replaces silently 500s
|
|
56
|
-
// instead of matching, since it's now invalid syntax rather than
|
|
57
|
-
// just non-matching.
|
|
58
87
|
exclude: [`/${mountPath}/api/{*splat}`],
|
|
59
88
|
}),
|
|
60
|
-
RouterModule.register([{ path: mountPath, module: EnlaceModule_1 }]),
|
|
61
89
|
],
|
|
62
|
-
|
|
63
|
-
|
|
90
|
+
providers: [
|
|
91
|
+
{ provide: ENLACE_OPTIONS, useValue: opts },
|
|
92
|
+
{ provide: ENLACE_MOUNT_PATH, useValue: mountPath },
|
|
93
|
+
SpecRouteRegistrar,
|
|
94
|
+
],
|
|
64
95
|
};
|
|
65
96
|
}
|
|
66
97
|
};
|
|
67
|
-
EnlaceModule =
|
|
68
|
-
Module({
|
|
98
|
+
EnlaceModule = __decorate([
|
|
99
|
+
Module({
|
|
100
|
+
imports: [
|
|
101
|
+
ServeStaticModule.forRoot({
|
|
102
|
+
rootPath: uiDist,
|
|
103
|
+
serveRoot: '/enlace',
|
|
104
|
+
exclude: ['/enlace/api/{*splat}'],
|
|
105
|
+
}),
|
|
106
|
+
],
|
|
107
|
+
// useFactory (not useValue) so each NestFactory.create() gets its own
|
|
108
|
+
// options object — otherwise setSpec() on one app would leak into another.
|
|
109
|
+
providers: [
|
|
110
|
+
{ provide: ENLACE_OPTIONS, useFactory: () => ({ spec: undefined }) },
|
|
111
|
+
{ provide: ENLACE_MOUNT_PATH, useValue: 'enlace' },
|
|
112
|
+
SpecRouteRegistrar,
|
|
113
|
+
],
|
|
114
|
+
})
|
|
69
115
|
], EnlaceModule);
|
|
70
116
|
export { EnlaceModule };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type OnModuleInit } from '@nestjs/common';
|
|
2
|
+
import { HttpAdapterHost } from '@nestjs/core';
|
|
3
|
+
import type { EnlaceOptions } from './enlace.module.js';
|
|
4
|
+
/**
|
|
5
|
+
* Registers `<mount>/api/spec` directly on the underlying HTTP adapter —
|
|
6
|
+
* the same technique `@nestjs/swagger` uses for its own `/docs-json` route
|
|
7
|
+
* — instead of as a Nest `@Controller`.
|
|
8
|
+
*
|
|
9
|
+
* A `@Controller` route is resolved through Nest's full request pipeline,
|
|
10
|
+
* which includes any global guards/interceptors the host app has
|
|
11
|
+
* registered (e.g. an app-wide `APP_GUARD` auth guard). That would gate
|
|
12
|
+
* this adapter's spec endpoint behind the host app's own auth policy,
|
|
13
|
+
* breaking zero-config installs the same way it'd break `@nestjs/swagger`
|
|
14
|
+
* if swagger-ui-express routes went through Nest's router instead of being
|
|
15
|
+
* registered on the raw adapter. Registering here the same way — like
|
|
16
|
+
* `ServeStaticModule` already does for the UI bundle (see
|
|
17
|
+
* enlace.module.test.ts) — keeps this endpoint outside the host app's
|
|
18
|
+
* guard/interceptor pipeline entirely, matching ARCHITECTURE.md's "adapter
|
|
19
|
+
* is thin and never entangled with app policy" model.
|
|
20
|
+
*/
|
|
21
|
+
export declare class SpecRouteRegistrar implements OnModuleInit {
|
|
22
|
+
private readonly adapterHost;
|
|
23
|
+
private readonly options;
|
|
24
|
+
private readonly mountPath;
|
|
25
|
+
constructor(adapterHost: HttpAdapterHost, options: EnlaceOptions, mountPath: string);
|
|
26
|
+
onModuleInit(): void;
|
|
27
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
11
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
12
|
+
};
|
|
13
|
+
import { Inject, Injectable } from '@nestjs/common';
|
|
14
|
+
import { HttpAdapterHost } from '@nestjs/core';
|
|
15
|
+
import { ENLACE_MOUNT_PATH, ENLACE_OPTIONS } from './constants.js';
|
|
16
|
+
import { loadSpec } from './specLoader.js';
|
|
17
|
+
const NO_SPEC_MESSAGE = 'No OpenAPI spec configured — call EnlaceModule.setSpec(app, spec) in main.ts, '
|
|
18
|
+
+ 'or pass { spec } to EnlaceModule.forRoot().';
|
|
19
|
+
/**
|
|
20
|
+
* Registers `<mount>/api/spec` directly on the underlying HTTP adapter —
|
|
21
|
+
* the same technique `@nestjs/swagger` uses for its own `/docs-json` route
|
|
22
|
+
* — instead of as a Nest `@Controller`.
|
|
23
|
+
*
|
|
24
|
+
* A `@Controller` route is resolved through Nest's full request pipeline,
|
|
25
|
+
* which includes any global guards/interceptors the host app has
|
|
26
|
+
* registered (e.g. an app-wide `APP_GUARD` auth guard). That would gate
|
|
27
|
+
* this adapter's spec endpoint behind the host app's own auth policy,
|
|
28
|
+
* breaking zero-config installs the same way it'd break `@nestjs/swagger`
|
|
29
|
+
* if swagger-ui-express routes went through Nest's router instead of being
|
|
30
|
+
* registered on the raw adapter. Registering here the same way — like
|
|
31
|
+
* `ServeStaticModule` already does for the UI bundle (see
|
|
32
|
+
* enlace.module.test.ts) — keeps this endpoint outside the host app's
|
|
33
|
+
* guard/interceptor pipeline entirely, matching ARCHITECTURE.md's "adapter
|
|
34
|
+
* is thin and never entangled with app policy" model.
|
|
35
|
+
*/
|
|
36
|
+
let SpecRouteRegistrar = class SpecRouteRegistrar {
|
|
37
|
+
constructor(adapterHost, options, mountPath) {
|
|
38
|
+
this.adapterHost = adapterHost;
|
|
39
|
+
this.options = options;
|
|
40
|
+
this.mountPath = mountPath;
|
|
41
|
+
}
|
|
42
|
+
onModuleInit() {
|
|
43
|
+
const httpAdapter = this.adapterHost.httpAdapter;
|
|
44
|
+
// `httpAdapter.reply(res, body, statusCode)` — not `res.status().json()`
|
|
45
|
+
// — is the deliberately adapter-agnostic primitive Nest itself uses to
|
|
46
|
+
// send a response from inside its own controller pipeline. It's
|
|
47
|
+
// implemented identically by ExpressAdapter and FastifyAdapter (JSON
|
|
48
|
+
// body + status code, on whichever platform is actually running), so
|
|
49
|
+
// this route works the same whether the host app is Express- or
|
|
50
|
+
// Fastify-based — unlike Express-specific chaining (`res.status(x).json(y)`),
|
|
51
|
+
// which would throw under Fastify (`FastifyReply` has no `.json()`).
|
|
52
|
+
httpAdapter.get(`/${this.mountPath}/api/spec`, (_req, res) => {
|
|
53
|
+
if (this.options.spec == null) {
|
|
54
|
+
httpAdapter.reply(res, { statusCode: 503, message: NO_SPEC_MESSAGE }, 503);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
httpAdapter.reply(res, loadSpec(this.options.spec), 200);
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
SpecRouteRegistrar = __decorate([
|
|
62
|
+
Injectable(),
|
|
63
|
+
__param(1, Inject(ENLACE_OPTIONS)),
|
|
64
|
+
__param(2, Inject(ENLACE_MOUNT_PATH)),
|
|
65
|
+
__metadata("design:paramtypes", [HttpAdapterHost, Object, String])
|
|
66
|
+
], SpecRouteRegistrar);
|
|
67
|
+
export { SpecRouteRegistrar };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@get-enlace/nest",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.4",
|
|
5
5
|
"description": "Simple NestJS adapter for @get-enlace/ui. Early dev preview: no persistence yet (serves the spec + UI bundle only), install via the `dev` dist-tag on GitHub Packages.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"reflect-metadata": "^0.1.13 || ^0.2.0"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@get-enlace/ui": "0.0.
|
|
39
|
+
"@get-enlace/ui": "0.0.4",
|
|
40
40
|
"js-yaml": "^4.1.0"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import type { EnlaceOptions } from './enlace.module.js';
|
|
2
|
-
/**
|
|
3
|
-
* Serves the raw OpenAPI document at `<mount>/api/spec`. Read fresh on each
|
|
4
|
-
* request, not cached — matches the "not stored, read fresh each load" rule
|
|
5
|
-
* from ARCHITECTURE.md §4, same as the express adapter's `/api/spec` route.
|
|
6
|
-
*
|
|
7
|
-
* This is the only route this adapter defines — per ARCHITECTURE.md's MVP
|
|
8
|
-
* model, execution runs entirely client-side in @get-enlace/ui, so there's
|
|
9
|
-
* no `/api/run` or `/api/credentials` here at all.
|
|
10
|
-
*/
|
|
11
|
-
export declare class EnlaceController {
|
|
12
|
-
private readonly options;
|
|
13
|
-
constructor(options: EnlaceOptions);
|
|
14
|
-
getSpec(): Record<string, any>;
|
|
15
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
-
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
-
};
|
|
7
|
-
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
-
};
|
|
10
|
-
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
11
|
-
return function (target, key) { decorator(target, key, paramIndex); }
|
|
12
|
-
};
|
|
13
|
-
import { Controller, Get, Inject } from '@nestjs/common';
|
|
14
|
-
import { ENLACE_OPTIONS } from './constants.js';
|
|
15
|
-
import { loadSpec } from './specLoader.js';
|
|
16
|
-
/**
|
|
17
|
-
* Serves the raw OpenAPI document at `<mount>/api/spec`. Read fresh on each
|
|
18
|
-
* request, not cached — matches the "not stored, read fresh each load" rule
|
|
19
|
-
* from ARCHITECTURE.md §4, same as the express adapter's `/api/spec` route.
|
|
20
|
-
*
|
|
21
|
-
* This is the only route this adapter defines — per ARCHITECTURE.md's MVP
|
|
22
|
-
* model, execution runs entirely client-side in @get-enlace/ui, so there's
|
|
23
|
-
* no `/api/run` or `/api/credentials` here at all.
|
|
24
|
-
*/
|
|
25
|
-
let EnlaceController = class EnlaceController {
|
|
26
|
-
constructor(options) {
|
|
27
|
-
this.options = options;
|
|
28
|
-
}
|
|
29
|
-
getSpec() {
|
|
30
|
-
return loadSpec(this.options.spec);
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
__decorate([
|
|
34
|
-
Get('spec'),
|
|
35
|
-
__metadata("design:type", Function),
|
|
36
|
-
__metadata("design:paramtypes", []),
|
|
37
|
-
__metadata("design:returntype", void 0)
|
|
38
|
-
], EnlaceController.prototype, "getSpec", null);
|
|
39
|
-
EnlaceController = __decorate([
|
|
40
|
-
Controller('api'),
|
|
41
|
-
__param(0, Inject(ENLACE_OPTIONS)),
|
|
42
|
-
__metadata("design:paramtypes", [Object])
|
|
43
|
-
], EnlaceController);
|
|
44
|
-
export { EnlaceController };
|