@syncular/server-hono 0.0.6-224 → 0.0.6-227

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
@@ -56,6 +56,32 @@ Use a function origin only when you need dynamic policy logic.
56
56
  When `routes.websocket.allowedOrigins` is unset, realtime websocket upgrades
57
57
  inherit static `routes.cors` origins automatically.
58
58
 
59
+ ## OpenAPI and Scalar
60
+
61
+ Serve a generated OpenAPI document:
62
+
63
+ ```ts
64
+ import { Hono } from 'hono';
65
+ import { createOpenAPIHandler } from '@syncular/server-hono';
66
+
67
+ const app = new Hono();
68
+ app.get('/openapi.json', createOpenAPIHandler(app, { title: 'Syncular API' }));
69
+ ```
70
+
71
+ Or mount both the OpenAPI document and a Scalar reference page:
72
+
73
+ ```ts
74
+ import { Hono } from 'hono';
75
+ import { createOpenAPIDocsRoutes } from '@syncular/server-hono';
76
+
77
+ const app = new Hono();
78
+ app.route('/', createOpenAPIDocsRoutes(app, { title: 'Syncular API' }));
79
+ ```
80
+
81
+ That serves:
82
+ - `/openapi.json`
83
+ - `/spec`
84
+
59
85
  ## Links
60
86
 
61
87
  - GitHub: https://github.com/syncular/syncular
package/dist/openapi.d.ts CHANGED
@@ -3,8 +3,8 @@
3
3
  *
4
4
  * Provides utilities for generating and serving OpenAPI specifications.
5
5
  */
6
- import type { Hono } from 'hono';
7
- interface OpenAPIConfig {
6
+ import { type Handler, Hono } from 'hono';
7
+ export interface OpenAPIConfig {
8
8
  title?: string;
9
9
  version?: string;
10
10
  description?: string;
@@ -13,6 +13,15 @@ interface OpenAPIConfig {
13
13
  description?: string;
14
14
  }>;
15
15
  }
16
+ export interface ScalarReferenceConfig {
17
+ url?: string;
18
+ cdn?: string;
19
+ }
20
+ export interface OpenAPIDocsRoutesConfig extends OpenAPIConfig {
21
+ openAPIPath?: string;
22
+ scalarPath?: string;
23
+ scalar?: ScalarReferenceConfig;
24
+ }
16
25
  /**
17
26
  * Create an OpenAPI spec handler that can be used with Hono routes.
18
27
  *
@@ -36,10 +45,28 @@ interface OpenAPIConfig {
36
45
  * ```
37
46
  */
38
47
  export declare function createOpenAPIHandler(app: Hono, config?: OpenAPIConfig): import("hono").MiddlewareHandler<import("hono/types").BlankEnv, "/", import("hono/types").BlankInput>;
48
+ /**
49
+ * Create a Scalar API reference handler that points at an OpenAPI document.
50
+ */
51
+ export declare function createScalarReferenceHandler(config?: ScalarReferenceConfig): Handler;
52
+ /**
53
+ * Create a small Hono app that serves both `/openapi.json` and `/spec`.
54
+ *
55
+ * @example
56
+ * ```typescript
57
+ * import { Hono } from 'hono';
58
+ * import { createOpenAPIDocsRoutes } from '@syncular/server-hono';
59
+ *
60
+ * const app = new Hono();
61
+ * app.route('/', createOpenAPIDocsRoutes(app, {
62
+ * title: 'Syncular API',
63
+ * }));
64
+ * ```
65
+ */
66
+ export declare function createOpenAPIDocsRoutes(app: Hono, config?: OpenAPIDocsRoutesConfig): Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
39
67
  /**
40
68
  * Generate OpenAPI document from a Hono app instance.
41
69
  * This is useful for build-time spec generation.
42
70
  */
43
71
  export declare function generateOpenAPIDocument(app: Hono, config?: OpenAPIConfig): Promise<unknown>;
44
- export {};
45
72
  //# sourceMappingURL=openapi.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"openapi.d.ts","sourceRoot":"","sources":["../src/openapi.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAGjC,UAAU,aAAa;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACxD;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,GAAE,aAAkB,yGAazE;AAED;;;GAGG;AACH,wBAAsB,uBAAuB,CAC3C,GAAG,EAAE,IAAI,EACT,MAAM,GAAE,aAAkB,GACzB,OAAO,CAAC,OAAO,CAAC,CAalB"}
1
+ {"version":3,"file":"openapi.d.ts","sourceRoot":"","sources":["../src/openapi.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,KAAK,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAM1C,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACxD;AAED,MAAM,WAAW,qBAAqB;IACpC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,uBAAwB,SAAQ,aAAa;IAC5D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,qBAAqB,CAAC;CAChC;AAMD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,GAAE,aAAkB,yGAazE;AAED;;GAEG;AACH,wBAAgB,4BAA4B,CAC1C,MAAM,GAAE,qBAA0B,GACjC,OAAO,CAKT;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,IAAI,EACT,MAAM,GAAE,uBAA4B,8EAgBrC;AAED;;;GAGG;AACH,wBAAsB,uBAAuB,CAC3C,GAAG,EAAE,IAAI,EACT,MAAM,GAAE,aAAkB,GACzB,OAAO,CAAC,OAAO,CAAC,CAalB"}
package/dist/openapi.js CHANGED
@@ -3,7 +3,13 @@
3
3
  *
4
4
  * Provides utilities for generating and serving OpenAPI specifications.
5
5
  */
6
+ import { Scalar } from '@scalar/hono-api-reference';
7
+ import { Hono } from 'hono';
6
8
  import { generateSpecs, openAPIRouteHandler } from 'hono-openapi';
9
+ const DEFAULT_SCALAR_CDN = 'https://cdn.jsdelivr.net/npm/@scalar/api-reference@latest';
10
+ function normalizeRoutePath(path) {
11
+ return path.startsWith('/') ? path : `/${path}`;
12
+ }
7
13
  /**
8
14
  * Create an OpenAPI spec handler that can be used with Hono routes.
9
15
  *
@@ -39,6 +45,40 @@ export function createOpenAPIHandler(app, config = {}) {
39
45
  },
40
46
  });
41
47
  }
48
+ /**
49
+ * Create a Scalar API reference handler that points at an OpenAPI document.
50
+ */
51
+ export function createScalarReferenceHandler(config = {}) {
52
+ return Scalar({
53
+ url: config.url ?? '/openapi.json',
54
+ cdn: config.cdn ?? DEFAULT_SCALAR_CDN,
55
+ });
56
+ }
57
+ /**
58
+ * Create a small Hono app that serves both `/openapi.json` and `/spec`.
59
+ *
60
+ * @example
61
+ * ```typescript
62
+ * import { Hono } from 'hono';
63
+ * import { createOpenAPIDocsRoutes } from '@syncular/server-hono';
64
+ *
65
+ * const app = new Hono();
66
+ * app.route('/', createOpenAPIDocsRoutes(app, {
67
+ * title: 'Syncular API',
68
+ * }));
69
+ * ```
70
+ */
71
+ export function createOpenAPIDocsRoutes(app, config = {}) {
72
+ const docsApp = new Hono();
73
+ const openAPIPath = normalizeRoutePath(config.openAPIPath ?? '/openapi.json');
74
+ const scalarPath = normalizeRoutePath(config.scalarPath ?? '/spec');
75
+ docsApp.get(openAPIPath, createOpenAPIHandler(app, config));
76
+ docsApp.get(scalarPath, createScalarReferenceHandler({
77
+ url: config.scalar?.url ?? openAPIPath,
78
+ cdn: config.scalar?.cdn,
79
+ }));
80
+ return docsApp;
81
+ }
42
82
  /**
43
83
  * Generate OpenAPI document from a Hono app instance.
44
84
  * This is useful for build-time spec generation.
@@ -1 +1 @@
1
- {"version":3,"file":"openapi.js","sourceRoot":"","sources":["../src/openapi.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AASlE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,oBAAoB,CAAC,GAAS,EAAE,MAAM,GAAkB,EAAE,EAAE;IAC1E,OAAO,mBAAmB,CAAC,GAAG,EAAE;QAC9B,aAAa,EAAE;YACb,IAAI,EAAE;gBACJ,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,cAAc;gBACrC,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,OAAO;gBAClC,WAAW,EACT,MAAM,CAAC,WAAW;oBAClB,4DAA4D;aAC/D;YACD,OAAO,EAAE,MAAM,CAAC,OAAO;SACxB;KACF,CAAC,CAAC;AAAA,CACJ;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,GAAS,EACT,MAAM,GAAkB,EAAE,EACR;IAClB,OAAO,aAAa,CAAC,GAAG,EAAE;QACxB,aAAa,EAAE;YACb,IAAI,EAAE;gBACJ,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,cAAc;gBACrC,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,OAAO;gBAClC,WAAW,EACT,MAAM,CAAC,WAAW;oBAClB,4DAA4D;aAC/D;YACD,OAAO,EAAE,MAAM,CAAC,OAAO;SACxB;KACF,CAAC,CAAC;AAAA,CACJ"}
1
+ {"version":3,"file":"openapi.js","sourceRoot":"","sources":["../src/openapi.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AACpD,OAAO,EAAgB,IAAI,EAAE,MAAM,MAAM,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAElE,MAAM,kBAAkB,GACtB,2DAA2D,CAAC;AAoB9D,SAAS,kBAAkB,CAAC,IAAY,EAAU;IAChD,OAAO,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;AAAA,CACjD;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,oBAAoB,CAAC,GAAS,EAAE,MAAM,GAAkB,EAAE,EAAE;IAC1E,OAAO,mBAAmB,CAAC,GAAG,EAAE;QAC9B,aAAa,EAAE;YACb,IAAI,EAAE;gBACJ,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,cAAc;gBACrC,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,OAAO;gBAClC,WAAW,EACT,MAAM,CAAC,WAAW;oBAClB,4DAA4D;aAC/D;YACD,OAAO,EAAE,MAAM,CAAC,OAAO;SACxB;KACF,CAAC,CAAC;AAAA,CACJ;AAED;;GAEG;AACH,MAAM,UAAU,4BAA4B,CAC1C,MAAM,GAA0B,EAAE,EACzB;IACT,OAAO,MAAM,CAAC;QACZ,GAAG,EAAE,MAAM,CAAC,GAAG,IAAI,eAAe;QAClC,GAAG,EAAE,MAAM,CAAC,GAAG,IAAI,kBAAkB;KACtC,CAAC,CAAC;AAAA,CACJ;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,uBAAuB,CACrC,GAAS,EACT,MAAM,GAA4B,EAAE,EACpC;IACA,MAAM,OAAO,GAAG,IAAI,IAAI,EAAE,CAAC;IAC3B,MAAM,WAAW,GAAG,kBAAkB,CAAC,MAAM,CAAC,WAAW,IAAI,eAAe,CAAC,CAAC;IAC9E,MAAM,UAAU,GAAG,kBAAkB,CAAC,MAAM,CAAC,UAAU,IAAI,OAAO,CAAC,CAAC;IAEpE,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,oBAAoB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC;IAC5D,OAAO,CAAC,GAAG,CACT,UAAU,EACV,4BAA4B,CAAC;QAC3B,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,IAAI,WAAW;QACtC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG;KACxB,CAAC,CACH,CAAC;IAEF,OAAO,OAAO,CAAC;AAAA,CAChB;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,GAAS,EACT,MAAM,GAAkB,EAAE,EACR;IAClB,OAAO,aAAa,CAAC,GAAG,EAAE;QACxB,aAAa,EAAE;YACb,IAAI,EAAE;gBACJ,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,cAAc;gBACrC,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,OAAO;gBAClC,WAAW,EACT,MAAM,CAAC,WAAW;oBAClB,4DAA4D;aAC/D;YACD,OAAO,EAAE,MAAM,CAAC,OAAO;SACxB;KACF,CAAC,CAAC;AAAA,CACJ"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@syncular/server-hono",
3
- "version": "0.0.6-224",
3
+ "version": "0.0.6-227",
4
4
  "description": "Hono adapter for the Syncular server with OpenAPI support",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Benjamin Kniffler",
@@ -59,20 +59,21 @@
59
59
  "release": "bunx syncular-publish"
60
60
  },
61
61
  "dependencies": {
62
+ "@scalar/hono-api-reference": "^0.10.2",
62
63
  "@hono/standard-validator": "^0.2.2",
63
64
  "@standard-community/standard-json": "^0.3.5",
64
65
  "@standard-community/standard-openapi": "^0.2.9",
65
- "@syncular/console": "0.0.6-224",
66
- "@syncular/core": "0.0.6-224",
67
- "@syncular/server": "0.0.6-224",
66
+ "@syncular/console": "0.0.6-227",
67
+ "@syncular/core": "0.0.6-227",
68
+ "@syncular/server": "0.0.6-227",
68
69
  "@types/json-schema": "^7.0.15",
69
70
  "hono-openapi": "^1.2.0",
70
71
  "openapi-types": "^12.1.3"
71
72
  },
72
73
  "devDependencies": {
73
74
  "@syncular/config": "0.0.0",
74
- "@syncular/dialect-pglite": "0.0.6-224",
75
- "@syncular/server-dialect-postgres": "0.0.6-224",
75
+ "@syncular/dialect-pglite": "0.0.6-227",
76
+ "@syncular/server-dialect-postgres": "0.0.6-227",
76
77
  "kysely": "*",
77
78
  "zod": "*"
78
79
  },
@@ -0,0 +1,78 @@
1
+ import { describe, expect, it } from 'bun:test';
2
+ import { Hono } from 'hono';
3
+ import {
4
+ createOpenAPIDocsRoutes,
5
+ createOpenAPIHandler,
6
+ createScalarReferenceHandler,
7
+ } from '../openapi';
8
+
9
+ describe('OpenAPI helpers', () => {
10
+ it('serves an OpenAPI document with configured metadata', async () => {
11
+ const sourceApp = new Hono();
12
+ const app = new Hono();
13
+
14
+ app.get(
15
+ '/openapi.json',
16
+ createOpenAPIHandler(sourceApp, {
17
+ title: 'Test API',
18
+ version: '1.2.3',
19
+ description: 'Test description',
20
+ })
21
+ );
22
+
23
+ const response = await app.request('/openapi.json');
24
+ expect(response.status).toBe(200);
25
+
26
+ const document = await response.json();
27
+ expect(document.info).toEqual({
28
+ title: 'Test API',
29
+ version: '1.2.3',
30
+ description: 'Test description',
31
+ });
32
+ });
33
+
34
+ it('serves a Scalar API reference that points at the configured document', async () => {
35
+ const app = new Hono();
36
+ app.get(
37
+ '/spec',
38
+ createScalarReferenceHandler({
39
+ url: '/docs/openapi.json',
40
+ })
41
+ );
42
+
43
+ const response = await app.request('/spec');
44
+ expect(response.status).toBe(200);
45
+
46
+ const html = await response.text();
47
+ expect(html).toContain('/docs/openapi.json');
48
+ });
49
+
50
+ it('creates paired OpenAPI and Scalar routes', async () => {
51
+ const sourceApp = new Hono();
52
+ const app = new Hono();
53
+
54
+ app.route(
55
+ '/',
56
+ createOpenAPIDocsRoutes(sourceApp, {
57
+ title: 'Docs API',
58
+ version: '9.9.9',
59
+ openAPIPath: '/api-docs.json',
60
+ scalarPath: '/docs',
61
+ })
62
+ );
63
+
64
+ const openApiResponse = await app.request('/api-docs.json');
65
+ expect(openApiResponse.status).toBe(200);
66
+ const document = await openApiResponse.json();
67
+ expect(document.info).toEqual({
68
+ title: 'Docs API',
69
+ version: '9.9.9',
70
+ description: 'Sync infrastructure API for real-time data synchronization',
71
+ });
72
+
73
+ const docsResponse = await app.request('/docs');
74
+ expect(docsResponse.status).toBe(200);
75
+ const html = await docsResponse.text();
76
+ expect(html).toContain('/api-docs.json');
77
+ });
78
+ });
package/src/openapi.ts CHANGED
@@ -4,16 +4,35 @@
4
4
  * Provides utilities for generating and serving OpenAPI specifications.
5
5
  */
6
6
 
7
- import type { Hono } from 'hono';
7
+ import { Scalar } from '@scalar/hono-api-reference';
8
+ import { type Handler, Hono } from 'hono';
8
9
  import { generateSpecs, openAPIRouteHandler } from 'hono-openapi';
9
10
 
10
- interface OpenAPIConfig {
11
+ const DEFAULT_SCALAR_CDN =
12
+ 'https://cdn.jsdelivr.net/npm/@scalar/api-reference@latest';
13
+
14
+ export interface OpenAPIConfig {
11
15
  title?: string;
12
16
  version?: string;
13
17
  description?: string;
14
18
  servers?: Array<{ url: string; description?: string }>;
15
19
  }
16
20
 
21
+ export interface ScalarReferenceConfig {
22
+ url?: string;
23
+ cdn?: string;
24
+ }
25
+
26
+ export interface OpenAPIDocsRoutesConfig extends OpenAPIConfig {
27
+ openAPIPath?: string;
28
+ scalarPath?: string;
29
+ scalar?: ScalarReferenceConfig;
30
+ }
31
+
32
+ function normalizeRoutePath(path: string): string {
33
+ return path.startsWith('/') ? path : `/${path}`;
34
+ }
35
+
17
36
  /**
18
37
  * Create an OpenAPI spec handler that can be used with Hono routes.
19
38
  *
@@ -51,6 +70,52 @@ export function createOpenAPIHandler(app: Hono, config: OpenAPIConfig = {}) {
51
70
  });
52
71
  }
53
72
 
73
+ /**
74
+ * Create a Scalar API reference handler that points at an OpenAPI document.
75
+ */
76
+ export function createScalarReferenceHandler(
77
+ config: ScalarReferenceConfig = {}
78
+ ): Handler {
79
+ return Scalar({
80
+ url: config.url ?? '/openapi.json',
81
+ cdn: config.cdn ?? DEFAULT_SCALAR_CDN,
82
+ });
83
+ }
84
+
85
+ /**
86
+ * Create a small Hono app that serves both `/openapi.json` and `/spec`.
87
+ *
88
+ * @example
89
+ * ```typescript
90
+ * import { Hono } from 'hono';
91
+ * import { createOpenAPIDocsRoutes } from '@syncular/server-hono';
92
+ *
93
+ * const app = new Hono();
94
+ * app.route('/', createOpenAPIDocsRoutes(app, {
95
+ * title: 'Syncular API',
96
+ * }));
97
+ * ```
98
+ */
99
+ export function createOpenAPIDocsRoutes(
100
+ app: Hono,
101
+ config: OpenAPIDocsRoutesConfig = {}
102
+ ) {
103
+ const docsApp = new Hono();
104
+ const openAPIPath = normalizeRoutePath(config.openAPIPath ?? '/openapi.json');
105
+ const scalarPath = normalizeRoutePath(config.scalarPath ?? '/spec');
106
+
107
+ docsApp.get(openAPIPath, createOpenAPIHandler(app, config));
108
+ docsApp.get(
109
+ scalarPath,
110
+ createScalarReferenceHandler({
111
+ url: config.scalar?.url ?? openAPIPath,
112
+ cdn: config.scalar?.cdn,
113
+ })
114
+ );
115
+
116
+ return docsApp;
117
+ }
118
+
54
119
  /**
55
120
  * Generate OpenAPI document from a Hono app instance.
56
121
  * This is useful for build-time spec generation.