@singhak/nodeui-fastify 0.3.0 → 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 CHANGED
@@ -1,52 +1,52 @@
1
- # @singhak/nodeui-fastify
2
-
3
- Fastify plugin adapter for the NodeUI developer console. Serves the bundled React
4
- console and its API under `{path}` (default `/nodeui`), records app requests in
5
- the request-log panel and lists your Fastify routes in the routes panel.
6
-
7
- Supports Fastify 4 and 5.
8
-
9
- ## Install
10
-
11
- ```bash
12
- npm install @singhak/nodeui-fastify
13
- ```
14
-
15
- ## Usage
16
-
17
- ```ts
18
- import Fastify from 'fastify';
19
- import { nodeui } from '@singhak/nodeui-fastify';
20
-
21
- const app = Fastify();
22
- const ui = nodeui();
23
-
24
- // Register before your routes so they appear in the routes panel.
25
- await app.register(ui);
26
-
27
- app.get('/hello', async () => ({ hello: 'world' }));
28
-
29
- await app.listen({ port: 3000, host: '127.0.0.1' });
30
- ui.server.mark('listening');
31
- ```
32
-
33
- Open `http://127.0.0.1:3000/nodeui` for the console and
34
- `http://127.0.0.1:3000/nodeui/api/config` for the API.
35
-
36
- The plugin is not encapsulated (equivalent to `fastify-plugin`), shuts the
37
- server down on `app.close()`, and does nothing when NodeUI is disabled (for
38
- example when `NODE_ENV=production`).
39
-
40
- ## Options
41
-
42
- `nodeui(options?)` accepts any `@singhak/nodeui-core` `NodeUIOptions` (path, host
43
- allow-lists, auth token, plugins, health checks, etc.). See the
44
- `@singhak/nodeui-core` README for the full configuration table and safety model.
45
-
46
- ## Development
47
-
48
- ```bash
49
- npm run typecheck
50
- npm test
51
- npm run build
52
- ```
1
+ # @singhak/nodeui-fastify
2
+
3
+ Fastify plugin adapter for the NodeUI developer console. Serves the bundled React
4
+ console and its API under `{path}` (default `/nodeui`), records app requests in
5
+ the request-log panel and lists your Fastify routes in the routes panel.
6
+
7
+ Supports Fastify 4 and 5.
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ npm install @singhak/nodeui-fastify
13
+ ```
14
+
15
+ ## Usage
16
+
17
+ ```ts
18
+ import Fastify from 'fastify';
19
+ import { nodeui } from '@singhak/nodeui-fastify';
20
+
21
+ const app = Fastify();
22
+ const ui = nodeui();
23
+
24
+ // Register before your routes so they appear in the routes panel.
25
+ await app.register(ui);
26
+
27
+ app.get('/hello', async () => ({ hello: 'world' }));
28
+
29
+ await app.listen({ port: 3000, host: '127.0.0.1' });
30
+ ui.server.mark('listening');
31
+ ```
32
+
33
+ Open `http://127.0.0.1:3000/nodeui` for the console and
34
+ `http://127.0.0.1:3000/nodeui/api/config` for the API.
35
+
36
+ The plugin is not encapsulated (equivalent to `fastify-plugin`), shuts the
37
+ server down on `app.close()`, and does nothing when NodeUI is disabled (for
38
+ example when `NODE_ENV=production`).
39
+
40
+ ## Options
41
+
42
+ `nodeui(options?)` accepts any `@singhak/nodeui-core` `NodeUIOptions` (path, host
43
+ allow-lists, auth token, plugins, health checks, etc.). See the
44
+ `@singhak/nodeui-core` README for the full configuration table and safety model.
45
+
46
+ ## Development
47
+
48
+ ```bash
49
+ npm run typecheck
50
+ npm test
51
+ npm run build
52
+ ```
package/dist/index.cjs CHANGED
@@ -64,7 +64,16 @@ function nodeui(options) {
64
64
  await server.handle(req.raw, reply.raw);
65
65
  return;
66
66
  }
67
+ const pattern = req.routeOptions?.url ?? req.routerPath;
68
+ if (pattern) req.raw.nodeuiRoute = pattern;
67
69
  recorder(req.raw, reply.raw, () => void 0);
70
+ (0, import_nodeui_core.enterRequestContext)(req.raw);
71
+ });
72
+ app.addHook("onError", (req, _reply, error, hookDone) => {
73
+ server.recordError(error, {
74
+ route: req.routeOptions?.url
75
+ });
76
+ hookDone();
68
77
  });
69
78
  app.addHook("onClose", (_instance, next) => {
70
79
  server.shutdown();
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { FastifyInstance, FastifyPluginCallback, RouteOptions } from 'fastify';\r\nimport {\r\n createNodeUI,\r\n type NodeUIOptions,\r\n type NodeUIServer,\r\n type RouteEntry,\r\n} from '@singhak/nodeui-core';\r\n\r\nexport interface NodeUIFastifyPlugin extends FastifyPluginCallback {\r\n /** Handle to the underlying server (mark, shutdown, config). */\r\n server: NodeUIServer;\r\n /** Pushes an external log entry into the log viewer (logger adapter). */\r\n addLogSource: NodeUIServer['addLogSource'];\r\n}\r\n\r\nfunction pathnameOf(url: string | undefined): string {\r\n try {\r\n return new URL(url ?? '/', 'http://localhost').pathname;\r\n } catch {\r\n return url ?? '/';\r\n }\r\n}\r\n\r\n/**\r\n * Creates the NodeUI Fastify plugin. Register it before your routes so the\r\n * routes panel and request log see them.\r\n *\r\n * @example\r\n * const ui = nodeui();\r\n * await app.register(ui);\r\n * await app.listen({ port: 3000, host: '127.0.0.1' });\r\n * ui.server.mark('listening');\r\n */\r\nexport function nodeui(options?: NodeUIOptions): NodeUIFastifyPlugin {\r\n const server = createNodeUI(options);\r\n const prefix = server.config.path;\r\n const isUnderPrefix = (p: string): boolean => p === prefix || p.startsWith(`${prefix}/`);\r\n const recorder = server.middleware();\r\n const routes = new Map<string, RouteEntry>();\r\n\r\n const plugin = ((app: FastifyInstance, _opts: unknown, done: (err?: Error) => void) => {\r\n if (!server.active) {\r\n done();\r\n return;\r\n }\r\n\r\n server.setRoutes(() =>\r\n [...routes.values()].sort(\r\n (a, b) => a.path.localeCompare(b.path) || a.method.localeCompare(b.method),\r\n ),\r\n );\r\n\r\n app.addHook('onRoute', (route: RouteOptions) => {\r\n const methods = Array.isArray(route.method) ? route.method : [route.method];\r\n const handler = (route.handler as { name?: string } | undefined)?.name || 'anonymous';\r\n for (const m of methods) {\r\n const method = String(m).toUpperCase();\r\n if (isUnderPrefix(route.url)) continue;\r\n routes.set(`${method} ${route.url}`, { method, path: route.url, handler });\r\n }\r\n });\r\n\r\n app.addHook('onRequest', async (req, reply) => {\r\n if (isUnderPrefix(pathnameOf(req.raw.url))) {\r\n reply.hijack();\r\n await server.handle(req.raw, reply.raw);\r\n return;\r\n }\r\n // Core's middleware records the request (finish listener) then calls next.\r\n recorder(req.raw, reply.raw, () => undefined);\r\n });\r\n\r\n app.addHook('onClose', (_instance, next) => {\r\n server.shutdown();\r\n next();\r\n });\r\n done();\r\n }) as NodeUIFastifyPlugin;\r\n\r\n // Same effect as fastify-plugin: expose hooks to the parent scope.\r\n const marks = plugin as unknown as Record<symbol, unknown>;\r\n marks[Symbol.for('skip-override')] = true;\r\n marks[Symbol.for('fastify.display-name')] = 'nodeui-fastify';\r\n plugin.server = server;\r\n plugin.addLogSource = server.addLogSource;\r\n return plugin;\r\n}\r\n\r\nexport * from '@singhak/nodeui-core';\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,yBAKO;AAkFP,0BAAc,iCAxFd;AAeA,SAAS,WAAW,KAAiC;AACnD,MAAI;AACF,WAAO,IAAI,IAAI,OAAO,KAAK,kBAAkB,EAAE;AAAA,EACjD,QAAQ;AACN,WAAO,OAAO;AAAA,EAChB;AACF;AAYO,SAAS,OAAO,SAA8C;AACnE,QAAM,aAAS,iCAAa,OAAO;AACnC,QAAM,SAAS,OAAO,OAAO;AAC7B,QAAM,gBAAgB,CAAC,MAAuB,MAAM,UAAU,EAAE,WAAW,GAAG,MAAM,GAAG;AACvF,QAAM,WAAW,OAAO,WAAW;AACnC,QAAM,SAAS,oBAAI,IAAwB;AAE3C,QAAM,UAAU,CAAC,KAAsB,OAAgB,SAAgC;AACrF,QAAI,CAAC,OAAO,QAAQ;AAClB,WAAK;AACL;AAAA,IACF;AAEA,WAAO;AAAA,MAAU,MACf,CAAC,GAAG,OAAO,OAAO,CAAC,EAAE;AAAA,QACnB,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,KAAK,EAAE,OAAO,cAAc,EAAE,MAAM;AAAA,MAC3E;AAAA,IACF;AAEA,QAAI,QAAQ,WAAW,CAAC,UAAwB;AAC9C,YAAM,UAAU,MAAM,QAAQ,MAAM,MAAM,IAAI,MAAM,SAAS,CAAC,MAAM,MAAM;AAC1E,YAAM,UAAW,MAAM,SAA2C,QAAQ;AAC1E,iBAAW,KAAK,SAAS;AACvB,cAAM,SAAS,OAAO,CAAC,EAAE,YAAY;AACrC,YAAI,cAAc,MAAM,GAAG,EAAG;AAC9B,eAAO,IAAI,GAAG,MAAM,IAAI,MAAM,GAAG,IAAI,EAAE,QAAQ,MAAM,MAAM,KAAK,QAAQ,CAAC;AAAA,MAC3E;AAAA,IACF,CAAC;AAED,QAAI,QAAQ,aAAa,OAAO,KAAK,UAAU;AAC7C,UAAI,cAAc,WAAW,IAAI,IAAI,GAAG,CAAC,GAAG;AAC1C,cAAM,OAAO;AACb,cAAM,OAAO,OAAO,IAAI,KAAK,MAAM,GAAG;AACtC;AAAA,MACF;AAEA,eAAS,IAAI,KAAK,MAAM,KAAK,MAAM,MAAS;AAAA,IAC9C,CAAC;AAED,QAAI,QAAQ,WAAW,CAAC,WAAW,SAAS;AAC1C,aAAO,SAAS;AAChB,WAAK;AAAA,IACP,CAAC;AACD,SAAK;AAAA,EACP;AAGA,QAAM,QAAQ;AACd,QAAM,uBAAO,IAAI,eAAe,CAAC,IAAI;AACrC,QAAM,uBAAO,IAAI,sBAAsB,CAAC,IAAI;AAC5C,SAAO,SAAS;AAChB,SAAO,eAAe,OAAO;AAC7B,SAAO;AACT;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { FastifyInstance, FastifyPluginCallback, RouteOptions } from 'fastify';\r\nimport {\r\n createNodeUI,\r\n enterRequestContext,\r\n type NodeUIOptions,\r\n type NodeUIServer,\r\n type RouteEntry,\r\n} from '@singhak/nodeui-core';\r\n\r\nexport interface NodeUIFastifyPlugin extends FastifyPluginCallback {\r\n /** Handle to the underlying server (mark, shutdown, config). */\r\n server: NodeUIServer;\r\n /** Pushes an external log entry into the log viewer (logger adapter). */\r\n addLogSource: NodeUIServer['addLogSource'];\r\n}\r\n\r\nfunction pathnameOf(url: string | undefined): string {\r\n try {\r\n return new URL(url ?? '/', 'http://localhost').pathname;\r\n } catch {\r\n return url ?? '/';\r\n }\r\n}\r\n\r\n/**\r\n * Creates the NodeUI Fastify plugin. Register it before your routes so the\r\n * routes panel and request log see them.\r\n *\r\n * @example\r\n * const ui = nodeui();\r\n * await app.register(ui);\r\n * await app.listen({ port: 3000, host: '127.0.0.1' });\r\n * ui.server.mark('listening');\r\n */\r\nexport function nodeui(options?: NodeUIOptions): NodeUIFastifyPlugin {\r\n const server = createNodeUI(options);\r\n const prefix = server.config.path;\r\n const isUnderPrefix = (p: string): boolean => p === prefix || p.startsWith(`${prefix}/`);\r\n const recorder = server.middleware();\r\n const routes = new Map<string, RouteEntry>();\r\n\r\n const plugin = ((app: FastifyInstance, _opts: unknown, done: (err?: Error) => void) => {\r\n if (!server.active) {\r\n done();\r\n return;\r\n }\r\n\r\n server.setRoutes(() =>\r\n [...routes.values()].sort(\r\n (a, b) => a.path.localeCompare(b.path) || a.method.localeCompare(b.method),\r\n ),\r\n );\r\n\r\n app.addHook('onRoute', (route: RouteOptions) => {\r\n const methods = Array.isArray(route.method) ? route.method : [route.method];\r\n const handler = (route.handler as { name?: string } | undefined)?.name || 'anonymous';\r\n for (const m of methods) {\r\n const method = String(m).toUpperCase();\r\n if (isUnderPrefix(route.url)) continue;\r\n routes.set(`${method} ${route.url}`, { method, path: route.url, handler });\r\n }\r\n });\r\n\r\n app.addHook('onRequest', async (req, reply) => {\r\n if (isUnderPrefix(pathnameOf(req.raw.url))) {\r\n reply.hijack();\r\n await server.handle(req.raw, reply.raw);\r\n return;\r\n }\r\n // Expose the matched route pattern to core's request recorder.\r\n const pattern =\r\n (req as { routeOptions?: { url?: string } }).routeOptions?.url ??\r\n (req as { routerPath?: string }).routerPath;\r\n if (pattern) (req.raw as { nodeuiRoute?: string }).nodeuiRoute = pattern;\r\n // Core's middleware records the request (finish listener) then calls next.\r\n recorder(req.raw, reply.raw, () => undefined);\r\n // Attribute outgoing calls and logs from the route handler to this request.\r\n enterRequestContext(req.raw);\r\n });\r\n\r\n app.addHook('onError', (req, _reply, error, hookDone) => {\r\n server.recordError(error, {\r\n route: (req as { routeOptions?: { url?: string } }).routeOptions?.url,\r\n });\r\n hookDone();\r\n });\r\n\r\n app.addHook('onClose', (_instance, next) => {\r\n server.shutdown();\r\n next();\r\n });\r\n done();\r\n }) as NodeUIFastifyPlugin;\r\n\r\n // Same effect as fastify-plugin: expose hooks to the parent scope.\r\n const marks = plugin as unknown as Record<symbol, unknown>;\r\n marks[Symbol.for('skip-override')] = true;\r\n marks[Symbol.for('fastify.display-name')] = 'nodeui-fastify';\r\n plugin.server = server;\r\n plugin.addLogSource = server.addLogSource;\r\n return plugin;\r\n}\r\n\r\nexport * from '@singhak/nodeui-core';\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,yBAMO;AAgGP,0BAAc,iCAvGd;AAgBA,SAAS,WAAW,KAAiC;AACnD,MAAI;AACF,WAAO,IAAI,IAAI,OAAO,KAAK,kBAAkB,EAAE;AAAA,EACjD,QAAQ;AACN,WAAO,OAAO;AAAA,EAChB;AACF;AAYO,SAAS,OAAO,SAA8C;AACnE,QAAM,aAAS,iCAAa,OAAO;AACnC,QAAM,SAAS,OAAO,OAAO;AAC7B,QAAM,gBAAgB,CAAC,MAAuB,MAAM,UAAU,EAAE,WAAW,GAAG,MAAM,GAAG;AACvF,QAAM,WAAW,OAAO,WAAW;AACnC,QAAM,SAAS,oBAAI,IAAwB;AAE3C,QAAM,UAAU,CAAC,KAAsB,OAAgB,SAAgC;AACrF,QAAI,CAAC,OAAO,QAAQ;AAClB,WAAK;AACL;AAAA,IACF;AAEA,WAAO;AAAA,MAAU,MACf,CAAC,GAAG,OAAO,OAAO,CAAC,EAAE;AAAA,QACnB,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,KAAK,EAAE,OAAO,cAAc,EAAE,MAAM;AAAA,MAC3E;AAAA,IACF;AAEA,QAAI,QAAQ,WAAW,CAAC,UAAwB;AAC9C,YAAM,UAAU,MAAM,QAAQ,MAAM,MAAM,IAAI,MAAM,SAAS,CAAC,MAAM,MAAM;AAC1E,YAAM,UAAW,MAAM,SAA2C,QAAQ;AAC1E,iBAAW,KAAK,SAAS;AACvB,cAAM,SAAS,OAAO,CAAC,EAAE,YAAY;AACrC,YAAI,cAAc,MAAM,GAAG,EAAG;AAC9B,eAAO,IAAI,GAAG,MAAM,IAAI,MAAM,GAAG,IAAI,EAAE,QAAQ,MAAM,MAAM,KAAK,QAAQ,CAAC;AAAA,MAC3E;AAAA,IACF,CAAC;AAED,QAAI,QAAQ,aAAa,OAAO,KAAK,UAAU;AAC7C,UAAI,cAAc,WAAW,IAAI,IAAI,GAAG,CAAC,GAAG;AAC1C,cAAM,OAAO;AACb,cAAM,OAAO,OAAO,IAAI,KAAK,MAAM,GAAG;AACtC;AAAA,MACF;AAEA,YAAM,UACH,IAA4C,cAAc,OAC1D,IAAgC;AACnC,UAAI,QAAS,CAAC,IAAI,IAAiC,cAAc;AAEjE,eAAS,IAAI,KAAK,MAAM,KAAK,MAAM,MAAS;AAE5C,kDAAoB,IAAI,GAAG;AAAA,IAC7B,CAAC;AAED,QAAI,QAAQ,WAAW,CAAC,KAAK,QAAQ,OAAO,aAAa;AACvD,aAAO,YAAY,OAAO;AAAA,QACxB,OAAQ,IAA4C,cAAc;AAAA,MACpE,CAAC;AACD,eAAS;AAAA,IACX,CAAC;AAED,QAAI,QAAQ,WAAW,CAAC,WAAW,SAAS;AAC1C,aAAO,SAAS;AAChB,WAAK;AAAA,IACP,CAAC;AACD,SAAK;AAAA,EACP;AAGA,QAAM,QAAQ;AACd,QAAM,uBAAO,IAAI,eAAe,CAAC,IAAI;AACrC,QAAM,uBAAO,IAAI,sBAAsB,CAAC,IAAI;AAC5C,SAAO,SAAS;AAChB,SAAO,eAAe,OAAO;AAC7B,SAAO;AACT;","names":[]}
package/dist/index.mjs CHANGED
@@ -1,6 +1,7 @@
1
1
  // src/index.ts
2
2
  import {
3
- createNodeUI
3
+ createNodeUI,
4
+ enterRequestContext
4
5
  } from "@singhak/nodeui-core";
5
6
  export * from "@singhak/nodeui-core";
6
7
  function pathnameOf(url) {
@@ -41,7 +42,16 @@ function nodeui(options) {
41
42
  await server.handle(req.raw, reply.raw);
42
43
  return;
43
44
  }
45
+ const pattern = req.routeOptions?.url ?? req.routerPath;
46
+ if (pattern) req.raw.nodeuiRoute = pattern;
44
47
  recorder(req.raw, reply.raw, () => void 0);
48
+ enterRequestContext(req.raw);
49
+ });
50
+ app.addHook("onError", (req, _reply, error, hookDone) => {
51
+ server.recordError(error, {
52
+ route: req.routeOptions?.url
53
+ });
54
+ hookDone();
45
55
  });
46
56
  app.addHook("onClose", (_instance, next) => {
47
57
  server.shutdown();
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { FastifyInstance, FastifyPluginCallback, RouteOptions } from 'fastify';\r\nimport {\r\n createNodeUI,\r\n type NodeUIOptions,\r\n type NodeUIServer,\r\n type RouteEntry,\r\n} from '@singhak/nodeui-core';\r\n\r\nexport interface NodeUIFastifyPlugin extends FastifyPluginCallback {\r\n /** Handle to the underlying server (mark, shutdown, config). */\r\n server: NodeUIServer;\r\n /** Pushes an external log entry into the log viewer (logger adapter). */\r\n addLogSource: NodeUIServer['addLogSource'];\r\n}\r\n\r\nfunction pathnameOf(url: string | undefined): string {\r\n try {\r\n return new URL(url ?? '/', 'http://localhost').pathname;\r\n } catch {\r\n return url ?? '/';\r\n }\r\n}\r\n\r\n/**\r\n * Creates the NodeUI Fastify plugin. Register it before your routes so the\r\n * routes panel and request log see them.\r\n *\r\n * @example\r\n * const ui = nodeui();\r\n * await app.register(ui);\r\n * await app.listen({ port: 3000, host: '127.0.0.1' });\r\n * ui.server.mark('listening');\r\n */\r\nexport function nodeui(options?: NodeUIOptions): NodeUIFastifyPlugin {\r\n const server = createNodeUI(options);\r\n const prefix = server.config.path;\r\n const isUnderPrefix = (p: string): boolean => p === prefix || p.startsWith(`${prefix}/`);\r\n const recorder = server.middleware();\r\n const routes = new Map<string, RouteEntry>();\r\n\r\n const plugin = ((app: FastifyInstance, _opts: unknown, done: (err?: Error) => void) => {\r\n if (!server.active) {\r\n done();\r\n return;\r\n }\r\n\r\n server.setRoutes(() =>\r\n [...routes.values()].sort(\r\n (a, b) => a.path.localeCompare(b.path) || a.method.localeCompare(b.method),\r\n ),\r\n );\r\n\r\n app.addHook('onRoute', (route: RouteOptions) => {\r\n const methods = Array.isArray(route.method) ? route.method : [route.method];\r\n const handler = (route.handler as { name?: string } | undefined)?.name || 'anonymous';\r\n for (const m of methods) {\r\n const method = String(m).toUpperCase();\r\n if (isUnderPrefix(route.url)) continue;\r\n routes.set(`${method} ${route.url}`, { method, path: route.url, handler });\r\n }\r\n });\r\n\r\n app.addHook('onRequest', async (req, reply) => {\r\n if (isUnderPrefix(pathnameOf(req.raw.url))) {\r\n reply.hijack();\r\n await server.handle(req.raw, reply.raw);\r\n return;\r\n }\r\n // Core's middleware records the request (finish listener) then calls next.\r\n recorder(req.raw, reply.raw, () => undefined);\r\n });\r\n\r\n app.addHook('onClose', (_instance, next) => {\r\n server.shutdown();\r\n next();\r\n });\r\n done();\r\n }) as NodeUIFastifyPlugin;\r\n\r\n // Same effect as fastify-plugin: expose hooks to the parent scope.\r\n const marks = plugin as unknown as Record<symbol, unknown>;\r\n marks[Symbol.for('skip-override')] = true;\r\n marks[Symbol.for('fastify.display-name')] = 'nodeui-fastify';\r\n plugin.server = server;\r\n plugin.addLogSource = server.addLogSource;\r\n return plugin;\r\n}\r\n\r\nexport * from '@singhak/nodeui-core';\r\n"],"mappings":";AACA;AAAA,EACE;AAAA,OAIK;AAkFP,cAAc;AAzEd,SAAS,WAAW,KAAiC;AACnD,MAAI;AACF,WAAO,IAAI,IAAI,OAAO,KAAK,kBAAkB,EAAE;AAAA,EACjD,QAAQ;AACN,WAAO,OAAO;AAAA,EAChB;AACF;AAYO,SAAS,OAAO,SAA8C;AACnE,QAAM,SAAS,aAAa,OAAO;AACnC,QAAM,SAAS,OAAO,OAAO;AAC7B,QAAM,gBAAgB,CAAC,MAAuB,MAAM,UAAU,EAAE,WAAW,GAAG,MAAM,GAAG;AACvF,QAAM,WAAW,OAAO,WAAW;AACnC,QAAM,SAAS,oBAAI,IAAwB;AAE3C,QAAM,UAAU,CAAC,KAAsB,OAAgB,SAAgC;AACrF,QAAI,CAAC,OAAO,QAAQ;AAClB,WAAK;AACL;AAAA,IACF;AAEA,WAAO;AAAA,MAAU,MACf,CAAC,GAAG,OAAO,OAAO,CAAC,EAAE;AAAA,QACnB,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,KAAK,EAAE,OAAO,cAAc,EAAE,MAAM;AAAA,MAC3E;AAAA,IACF;AAEA,QAAI,QAAQ,WAAW,CAAC,UAAwB;AAC9C,YAAM,UAAU,MAAM,QAAQ,MAAM,MAAM,IAAI,MAAM,SAAS,CAAC,MAAM,MAAM;AAC1E,YAAM,UAAW,MAAM,SAA2C,QAAQ;AAC1E,iBAAW,KAAK,SAAS;AACvB,cAAM,SAAS,OAAO,CAAC,EAAE,YAAY;AACrC,YAAI,cAAc,MAAM,GAAG,EAAG;AAC9B,eAAO,IAAI,GAAG,MAAM,IAAI,MAAM,GAAG,IAAI,EAAE,QAAQ,MAAM,MAAM,KAAK,QAAQ,CAAC;AAAA,MAC3E;AAAA,IACF,CAAC;AAED,QAAI,QAAQ,aAAa,OAAO,KAAK,UAAU;AAC7C,UAAI,cAAc,WAAW,IAAI,IAAI,GAAG,CAAC,GAAG;AAC1C,cAAM,OAAO;AACb,cAAM,OAAO,OAAO,IAAI,KAAK,MAAM,GAAG;AACtC;AAAA,MACF;AAEA,eAAS,IAAI,KAAK,MAAM,KAAK,MAAM,MAAS;AAAA,IAC9C,CAAC;AAED,QAAI,QAAQ,WAAW,CAAC,WAAW,SAAS;AAC1C,aAAO,SAAS;AAChB,WAAK;AAAA,IACP,CAAC;AACD,SAAK;AAAA,EACP;AAGA,QAAM,QAAQ;AACd,QAAM,uBAAO,IAAI,eAAe,CAAC,IAAI;AACrC,QAAM,uBAAO,IAAI,sBAAsB,CAAC,IAAI;AAC5C,SAAO,SAAS;AAChB,SAAO,eAAe,OAAO;AAC7B,SAAO;AACT;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { FastifyInstance, FastifyPluginCallback, RouteOptions } from 'fastify';\r\nimport {\r\n createNodeUI,\r\n enterRequestContext,\r\n type NodeUIOptions,\r\n type NodeUIServer,\r\n type RouteEntry,\r\n} from '@singhak/nodeui-core';\r\n\r\nexport interface NodeUIFastifyPlugin extends FastifyPluginCallback {\r\n /** Handle to the underlying server (mark, shutdown, config). */\r\n server: NodeUIServer;\r\n /** Pushes an external log entry into the log viewer (logger adapter). */\r\n addLogSource: NodeUIServer['addLogSource'];\r\n}\r\n\r\nfunction pathnameOf(url: string | undefined): string {\r\n try {\r\n return new URL(url ?? '/', 'http://localhost').pathname;\r\n } catch {\r\n return url ?? '/';\r\n }\r\n}\r\n\r\n/**\r\n * Creates the NodeUI Fastify plugin. Register it before your routes so the\r\n * routes panel and request log see them.\r\n *\r\n * @example\r\n * const ui = nodeui();\r\n * await app.register(ui);\r\n * await app.listen({ port: 3000, host: '127.0.0.1' });\r\n * ui.server.mark('listening');\r\n */\r\nexport function nodeui(options?: NodeUIOptions): NodeUIFastifyPlugin {\r\n const server = createNodeUI(options);\r\n const prefix = server.config.path;\r\n const isUnderPrefix = (p: string): boolean => p === prefix || p.startsWith(`${prefix}/`);\r\n const recorder = server.middleware();\r\n const routes = new Map<string, RouteEntry>();\r\n\r\n const plugin = ((app: FastifyInstance, _opts: unknown, done: (err?: Error) => void) => {\r\n if (!server.active) {\r\n done();\r\n return;\r\n }\r\n\r\n server.setRoutes(() =>\r\n [...routes.values()].sort(\r\n (a, b) => a.path.localeCompare(b.path) || a.method.localeCompare(b.method),\r\n ),\r\n );\r\n\r\n app.addHook('onRoute', (route: RouteOptions) => {\r\n const methods = Array.isArray(route.method) ? route.method : [route.method];\r\n const handler = (route.handler as { name?: string } | undefined)?.name || 'anonymous';\r\n for (const m of methods) {\r\n const method = String(m).toUpperCase();\r\n if (isUnderPrefix(route.url)) continue;\r\n routes.set(`${method} ${route.url}`, { method, path: route.url, handler });\r\n }\r\n });\r\n\r\n app.addHook('onRequest', async (req, reply) => {\r\n if (isUnderPrefix(pathnameOf(req.raw.url))) {\r\n reply.hijack();\r\n await server.handle(req.raw, reply.raw);\r\n return;\r\n }\r\n // Expose the matched route pattern to core's request recorder.\r\n const pattern =\r\n (req as { routeOptions?: { url?: string } }).routeOptions?.url ??\r\n (req as { routerPath?: string }).routerPath;\r\n if (pattern) (req.raw as { nodeuiRoute?: string }).nodeuiRoute = pattern;\r\n // Core's middleware records the request (finish listener) then calls next.\r\n recorder(req.raw, reply.raw, () => undefined);\r\n // Attribute outgoing calls and logs from the route handler to this request.\r\n enterRequestContext(req.raw);\r\n });\r\n\r\n app.addHook('onError', (req, _reply, error, hookDone) => {\r\n server.recordError(error, {\r\n route: (req as { routeOptions?: { url?: string } }).routeOptions?.url,\r\n });\r\n hookDone();\r\n });\r\n\r\n app.addHook('onClose', (_instance, next) => {\r\n server.shutdown();\r\n next();\r\n });\r\n done();\r\n }) as NodeUIFastifyPlugin;\r\n\r\n // Same effect as fastify-plugin: expose hooks to the parent scope.\r\n const marks = plugin as unknown as Record<symbol, unknown>;\r\n marks[Symbol.for('skip-override')] = true;\r\n marks[Symbol.for('fastify.display-name')] = 'nodeui-fastify';\r\n plugin.server = server;\r\n plugin.addLogSource = server.addLogSource;\r\n return plugin;\r\n}\r\n\r\nexport * from '@singhak/nodeui-core';\r\n"],"mappings":";AACA;AAAA,EACE;AAAA,EACA;AAAA,OAIK;AAgGP,cAAc;AAvFd,SAAS,WAAW,KAAiC;AACnD,MAAI;AACF,WAAO,IAAI,IAAI,OAAO,KAAK,kBAAkB,EAAE;AAAA,EACjD,QAAQ;AACN,WAAO,OAAO;AAAA,EAChB;AACF;AAYO,SAAS,OAAO,SAA8C;AACnE,QAAM,SAAS,aAAa,OAAO;AACnC,QAAM,SAAS,OAAO,OAAO;AAC7B,QAAM,gBAAgB,CAAC,MAAuB,MAAM,UAAU,EAAE,WAAW,GAAG,MAAM,GAAG;AACvF,QAAM,WAAW,OAAO,WAAW;AACnC,QAAM,SAAS,oBAAI,IAAwB;AAE3C,QAAM,UAAU,CAAC,KAAsB,OAAgB,SAAgC;AACrF,QAAI,CAAC,OAAO,QAAQ;AAClB,WAAK;AACL;AAAA,IACF;AAEA,WAAO;AAAA,MAAU,MACf,CAAC,GAAG,OAAO,OAAO,CAAC,EAAE;AAAA,QACnB,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,KAAK,EAAE,OAAO,cAAc,EAAE,MAAM;AAAA,MAC3E;AAAA,IACF;AAEA,QAAI,QAAQ,WAAW,CAAC,UAAwB;AAC9C,YAAM,UAAU,MAAM,QAAQ,MAAM,MAAM,IAAI,MAAM,SAAS,CAAC,MAAM,MAAM;AAC1E,YAAM,UAAW,MAAM,SAA2C,QAAQ;AAC1E,iBAAW,KAAK,SAAS;AACvB,cAAM,SAAS,OAAO,CAAC,EAAE,YAAY;AACrC,YAAI,cAAc,MAAM,GAAG,EAAG;AAC9B,eAAO,IAAI,GAAG,MAAM,IAAI,MAAM,GAAG,IAAI,EAAE,QAAQ,MAAM,MAAM,KAAK,QAAQ,CAAC;AAAA,MAC3E;AAAA,IACF,CAAC;AAED,QAAI,QAAQ,aAAa,OAAO,KAAK,UAAU;AAC7C,UAAI,cAAc,WAAW,IAAI,IAAI,GAAG,CAAC,GAAG;AAC1C,cAAM,OAAO;AACb,cAAM,OAAO,OAAO,IAAI,KAAK,MAAM,GAAG;AACtC;AAAA,MACF;AAEA,YAAM,UACH,IAA4C,cAAc,OAC1D,IAAgC;AACnC,UAAI,QAAS,CAAC,IAAI,IAAiC,cAAc;AAEjE,eAAS,IAAI,KAAK,MAAM,KAAK,MAAM,MAAS;AAE5C,0BAAoB,IAAI,GAAG;AAAA,IAC7B,CAAC;AAED,QAAI,QAAQ,WAAW,CAAC,KAAK,QAAQ,OAAO,aAAa;AACvD,aAAO,YAAY,OAAO;AAAA,QACxB,OAAQ,IAA4C,cAAc;AAAA,MACpE,CAAC;AACD,eAAS;AAAA,IACX,CAAC;AAED,QAAI,QAAQ,WAAW,CAAC,WAAW,SAAS;AAC1C,aAAO,SAAS;AAChB,WAAK;AAAA,IACP,CAAC;AACD,SAAK;AAAA,EACP;AAGA,QAAM,QAAQ;AACd,QAAM,uBAAO,IAAI,eAAe,CAAC,IAAI;AACrC,QAAM,uBAAO,IAAI,sBAAsB,CAAC,IAAI;AAC5C,SAAO,SAAS;AAChB,SAAO,eAAe,OAAO;AAC7B,SAAO;AACT;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@singhak/nodeui-fastify",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "description": "Fastify plugin adapter for the NodeUI developer console.",
5
5
  "license": "Apache-2.0",
6
6
  "main": "./dist/index.cjs",
@@ -47,7 +47,7 @@
47
47
  "test": "vitest run"
48
48
  },
49
49
  "dependencies": {
50
- "@singhak/nodeui-core": "^0.3.0"
50
+ "@singhak/nodeui-core": "^0.5.0"
51
51
  },
52
52
  "peerDependencies": {
53
53
  "fastify": "^4.0.0 || ^5.0.0"