@mergehez/dokie-node 0.1.2

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/dist/index.mjs ADDED
@@ -0,0 +1,242 @@
1
+ import path from "node:path";
2
+ import { fileURLToPath } from "node:url";
3
+ import fs from "node:fs";
4
+ //#region node_modules/tsdown/esm-shims.js
5
+ const getFilename = () => fileURLToPath(import.meta.url);
6
+ const getDirname = () => path.dirname(getFilename());
7
+ const __dirname = /* @__PURE__ */ getDirname();
8
+ const _coreExports = createDokieServerExports(path.resolve(__dirname, "../assets/dokie.html"), (assetPath) => fs.readFileSync(assetPath, "utf8"));
9
+ function normalizeFetchRequest(req) {
10
+ const url = new URL(req.url);
11
+ return {
12
+ get(name) {
13
+ return req.headers.get(name) ?? void 0;
14
+ },
15
+ header(name) {
16
+ return req.headers.get(name) ?? void 0;
17
+ },
18
+ headers: Object.fromEntries(req.headers.entries()),
19
+ hostname: url.host,
20
+ protocol: url.protocol.replace(/:$/, "")
21
+ };
22
+ }
23
+ function forCustomServer() {
24
+ return {
25
+ createDokieHandler: _coreExports.createDokieHandler,
26
+ createDokieMiddleware: _coreExports.createDokieHandler,
27
+ getDokieAssetPath: _coreExports.getDokieAssetPath,
28
+ registerDokieRoute: _coreExports.registerDokieRoute,
29
+ renderDokieHtml: _coreExports.renderDokieHtml
30
+ };
31
+ }
32
+ function forFetch() {
33
+ function renderDokieHtmlForFetch(options = {}, req) {
34
+ return _coreExports.renderDokieHtml(options, req ? normalizeFetchRequest(req) : void 0);
35
+ }
36
+ function createDokieHandlerForFetch(options = {}) {
37
+ return function dokieFetchHandler(req) {
38
+ const html = renderDokieHtmlForFetch(options, req);
39
+ return new Response(html, { headers: { "Content-Type": "text/html; charset=utf-8" } });
40
+ };
41
+ }
42
+ return {
43
+ createDokieHandler: createDokieHandlerForFetch,
44
+ createDokieMiddleware: createDokieHandlerForFetch,
45
+ getDokieAssetPath: _coreExports.getDokieAssetPath,
46
+ renderDokieHtml: renderDokieHtmlForFetch
47
+ };
48
+ }
49
+ function escapeHtml(value) {
50
+ return String(value).replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;").replaceAll("'", "&#39;");
51
+ }
52
+ function safeJson(value) {
53
+ return JSON.stringify(value).replaceAll("<", "\\u003c").replaceAll(">", "\\u003e").replaceAll("&", "\\u0026").replaceAll("\u2028", "\\u2028").replaceAll("\u2029", "\\u2029");
54
+ }
55
+ function trimTrailingSlash(value) {
56
+ return typeof value === "string" ? value.replace(/\/+$/, "") : value;
57
+ }
58
+ function getRequestHeader(req, name) {
59
+ if (!req) return;
60
+ if (typeof req.get === "function") return req.get(name);
61
+ if (typeof req.header === "function") return req.header(name);
62
+ const headers = req.headers ?? {};
63
+ const value = headers[name.toLowerCase()] ?? headers[name];
64
+ return Array.isArray(value) ? value[0] : value;
65
+ }
66
+ function getOpenApiJsonUrl(currentHostname, options = {}) {
67
+ const configuredUrl = options.openApiJsonUrl ?? "/openapi.json";
68
+ if (typeof configuredUrl !== "string" || configuredUrl.length === 0) return "/openapi.json";
69
+ if (/^[a-z]+:\/\//i.test(configuredUrl)) return configuredUrl;
70
+ if (configuredUrl.startsWith("/") && currentHostname) return `${currentHostname}${configuredUrl}`;
71
+ return configuredUrl;
72
+ }
73
+ function buildDokieConfig(options = {}, req) {
74
+ const getProtocol = () => {
75
+ if (!req) return "http";
76
+ if (typeof req.protocol === "string" && req.protocol.length > 0) return req.protocol;
77
+ const forwardedProto = getRequestHeader(req, "x-forwarded-proto");
78
+ if (typeof forwardedProto === "string" && forwardedProto.length > 0) return forwardedProto.split(",")[0].trim();
79
+ if (req.socket?.encrypted || req.connection?.encrypted) return "https";
80
+ return "http";
81
+ };
82
+ const currentHost = (() => {
83
+ if (!req) return;
84
+ if (typeof req.hostname === "string" && req.hostname.length > 0) {
85
+ const port = req.socket?.localPort;
86
+ return req.hostname.includes(":") || !port ? req.hostname : `${req.hostname}:${port}`;
87
+ }
88
+ const forwardedHost = getRequestHeader(req, "x-forwarded-host");
89
+ if (typeof forwardedHost === "string" && forwardedHost.length > 0) return forwardedHost.split(",")[0].trim();
90
+ const host = getRequestHeader(req, "host");
91
+ return typeof host === "string" && host.length > 0 ? host : void 0;
92
+ })();
93
+ const currentHostname = typeof options.currentHostname === "string" && options.currentHostname.length > 0 ? trimTrailingSlash(options.currentHostname) ?? "" : currentHost ? `${getProtocol()}://${currentHost}` : "";
94
+ return {
95
+ currentHostname,
96
+ openApiJsonUrl: getOpenApiJsonUrl(currentHostname, options),
97
+ hostnames: Array.isArray(options.hostnames) && options.hostnames.length > 0 ? options.hostnames : currentHostname ? [currentHostname] : [],
98
+ variables: options.variables ?? {},
99
+ headers: options.headers ?? {},
100
+ postscripts: options.postscripts ?? {},
101
+ bodies: options.bodies ?? {},
102
+ favorites: options.favorites ?? []
103
+ };
104
+ }
105
+ function sendHtml(res, html) {
106
+ if (!res) throw new TypeError("A response object is required.");
107
+ if (typeof res.type === "function") res.type("html");
108
+ if (typeof res.setHeader === "function") res.setHeader("Content-Type", "text/html; charset=utf-8");
109
+ if (typeof res.send === "function") return res.send(html);
110
+ if (typeof res.end === "function") return res.end(html);
111
+ throw new TypeError("The response object must support send() or end().");
112
+ }
113
+ function createDokieServerExports(defaultAssetPath, readAsset) {
114
+ function getDokieAssetPath() {
115
+ return defaultAssetPath;
116
+ }
117
+ function renderDokieHtml(options = {}, req) {
118
+ const html = readAsset(options.assetPath ?? defaultAssetPath);
119
+ const dokieConfig = buildDokieConfig(options, req);
120
+ const finalHtml = html.replace("/*dokie-inject-area*/", `window.dokie = ${safeJson(dokieConfig)};`);
121
+ if (typeof options.title === "string" && options.title.trim().length > 0) return finalHtml.replace("<title>Dokie</title>", `<title>${escapeHtml(options.title)}</title>`);
122
+ return finalHtml;
123
+ }
124
+ function createDokieHandler(options = {}) {
125
+ return function dokieHandler(req, res) {
126
+ return sendHtml(res, renderDokieHtml(options, req));
127
+ };
128
+ }
129
+ function registerDokieRoute(app, options = {}) {
130
+ if (!app || typeof app.get !== "function") throw new TypeError("registerDokieRoute expects an app with a get(path, handler) method.");
131
+ const routePath = options.routePath ?? "/dokie";
132
+ const handler = createDokieHandler(options);
133
+ app.get(routePath, handler);
134
+ return handler;
135
+ }
136
+ return {
137
+ createDokieHandler,
138
+ createDokieMiddleware: createDokieHandler,
139
+ getDokieAssetPath,
140
+ registerDokieRoute,
141
+ renderDokieHtml
142
+ };
143
+ }
144
+ //#endregion
145
+ //#region src/express.ts
146
+ function forExpress() {
147
+ return {
148
+ createDokieHandler: _coreExports.createDokieHandler,
149
+ createDokieMiddleware: _coreExports.createDokieHandler,
150
+ getDokieAssetPath: _coreExports.getDokieAssetPath,
151
+ registerDokieRoute: _coreExports.registerDokieRoute,
152
+ renderDokieHtml: _coreExports.renderDokieHtml
153
+ };
154
+ }
155
+ //#endregion
156
+ //#region src/fastify.ts
157
+ function forFastify() {
158
+ function normalizeFastifyRequest(req) {
159
+ const header = (name) => {
160
+ const rawHeader = req.raw?.header?.(name);
161
+ if (typeof rawHeader === "string" && rawHeader.length > 0) return rawHeader;
162
+ const directHeader = req.header?.(name);
163
+ if (typeof directHeader === "string" && directHeader.length > 0) return directHeader;
164
+ const headers = req.headers ?? req.raw?.headers;
165
+ const value = headers?.[name.toLowerCase()] ?? headers?.[name];
166
+ return Array.isArray(value) ? value[0] : value;
167
+ };
168
+ return {
169
+ connection: req.raw?.connection,
170
+ get: req.raw?.get?.bind(req.raw),
171
+ header,
172
+ headers: req.headers ?? req.raw?.headers,
173
+ hostname: req.hostname ?? req.raw?.hostname,
174
+ protocol: req.protocol ?? req.raw?.protocol,
175
+ socket: req.raw?.socket
176
+ };
177
+ }
178
+ const renderDokieHtmlForFastify = (options = {}, req) => {
179
+ return _coreExports.renderDokieHtml(options, req ? normalizeFastifyRequest(req) : void 0);
180
+ };
181
+ function createDokieHandlerForFastify(options = {}) {
182
+ return function dokieFastifyHandler(req, reply) {
183
+ const html = renderDokieHtmlForFastify(options, req);
184
+ if (typeof reply.type === "function") reply.type("text/html; charset=utf-8");
185
+ if (typeof reply.header === "function") reply.header("Content-Type", "text/html; charset=utf-8");
186
+ if (typeof reply.send === "function") return reply.send(html);
187
+ throw new TypeError("The Fastify reply object must support send().");
188
+ };
189
+ }
190
+ return {
191
+ createDokieHandler: createDokieHandlerForFastify,
192
+ createDokieMiddleware: createDokieHandlerForFastify,
193
+ getDokieAssetPath: _coreExports.getDokieAssetPath,
194
+ renderDokieHtml: renderDokieHtmlForFastify,
195
+ registerDokieRoute(app, options = {}) {
196
+ if (!app || typeof app.get !== "function") throw new TypeError("registerDokieRoute expects a Fastify instance with a get(path, handler) method.");
197
+ const routePath = options.routePath ?? "/dokie";
198
+ const handler = createDokieHandlerForFastify(options);
199
+ app.get(routePath, handler);
200
+ return handler;
201
+ }
202
+ };
203
+ }
204
+ //#endregion
205
+ //#region src/hono.ts
206
+ function forHono() {
207
+ function normalizeHonoRequest(req) {
208
+ const url = new URL(req.url);
209
+ return {
210
+ get: req.get?.bind(req),
211
+ header: req.header?.bind(req),
212
+ headers: req.raw ? Object.fromEntries(req.raw.headers.entries()) : void 0,
213
+ hostname: url.host,
214
+ protocol: url.protocol.replace(/:$/, "")
215
+ };
216
+ }
217
+ function renderDokieHtmlForHono(options = {}, req) {
218
+ return _coreExports.renderDokieHtml(options, req ? normalizeHonoRequest(req) : void 0);
219
+ }
220
+ function createDokieHandlerForHono(options = {}) {
221
+ return function dokieHonoHandler(context) {
222
+ const html = renderDokieHtmlForHono(options, context.req);
223
+ if (typeof context.html === "function") return context.html(html);
224
+ return new Response(html, { headers: { "Content-Type": "text/html; charset=utf-8" } });
225
+ };
226
+ }
227
+ return {
228
+ createDokieHandler: createDokieHandlerForHono,
229
+ createDokieMiddleware: createDokieHandlerForHono,
230
+ getDokieAssetPath: _coreExports.getDokieAssetPath,
231
+ renderDokieHtml: renderDokieHtmlForHono,
232
+ registerDokieRoute(app, options = {}) {
233
+ if (!app || typeof app.get !== "function") throw new TypeError("registerDokieRoute expects a Hono app with a get(path, handler) method.");
234
+ const routePath = options.routePath ?? "/dokie";
235
+ const handler = createDokieHandlerForHono(options);
236
+ app.get(routePath, handler);
237
+ return handler;
238
+ }
239
+ };
240
+ }
241
+ //#endregion
242
+ export { forCustomServer, forExpress, forFastify, forFetch, forHono };
package/package.json ADDED
@@ -0,0 +1,72 @@
1
+ {
2
+ "name": "@mergehez/dokie-node",
3
+ "version": "0.1.2",
4
+ "description": "A Node.js package for serving the Dokie API documentation UI across generic servers, Express, Fastify, and Hono.",
5
+ "license": "MIT",
6
+ "author": "mergehez",
7
+ "type": "module",
8
+ "main": "./dist/index.cjs",
9
+ "module": "./dist/index.mjs",
10
+ "types": "./dist/index.d.mts",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.mts",
14
+ "import": "./dist/index.mjs",
15
+ "require": "./dist/index.cjs"
16
+ }
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "assets",
21
+ "README.md",
22
+ "LICENSE"
23
+ ],
24
+ "sideEffects": false,
25
+ "engines": {
26
+ "node": ">=18"
27
+ },
28
+ "homepage": "https://github.com/mergehez/dokie/tree/main/packages/node",
29
+ "bugs": {
30
+ "url": "https://github.com/mergehez/dokie/issues"
31
+ },
32
+ "publishConfig": {
33
+ "access": "public"
34
+ },
35
+ "keywords": [
36
+ "openapi",
37
+ "swagger",
38
+ "swagger-ui",
39
+ "api",
40
+ "api-docs",
41
+ "api-client",
42
+ "documentation",
43
+ "dokie",
44
+ "openapi-ui",
45
+ "rest-client",
46
+ "node",
47
+ "http",
48
+ "express",
49
+ "fastify",
50
+ "hono",
51
+ "nextjs",
52
+ "nuxt",
53
+ "remix",
54
+ "astro",
55
+ "fetch"
56
+ ],
57
+ "scripts": {
58
+ "build": "vp check --fix && tsdown",
59
+ "test": "bun test"
60
+ },
61
+ "devDependencies": {
62
+ "@types/bun": "^1.3.12",
63
+ "@types/node": "^25.6.0",
64
+ "tsdown": "^0.21.7",
65
+ "typescript": "^6.0.2"
66
+ },
67
+ "repository": {
68
+ "type": "git",
69
+ "url": "git+https://github.com/mergehez/dokie.git",
70
+ "directory": "packages/node"
71
+ }
72
+ }