@brt-innovation/aether 1.2.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.dev.md ADDED
@@ -0,0 +1,67 @@
1
+ # Publishing Guide
2
+
3
+ This document outlines the steps to publish the `@brt-innovation/aether` package.
4
+
5
+ ## Pre-requisites
6
+
7
+ - Ensure you are logged in to the npm registry:
8
+ ```bash
9
+ npm login
10
+ ```
11
+
12
+ ## Deployment Workflow
13
+
14
+ ### 1. Verify Build (Optional)
15
+
16
+ Before merging, ensure the project builds correctly locally.
17
+
18
+ ```bash
19
+ npm install
20
+ npm run build
21
+ ```
22
+
23
+ ### 2. Merge to Main
24
+
25
+ - Create a Pull Request (PR) with your changes targeting the `main` branch.
26
+ - Review and merge the PR.
27
+
28
+ ### 3. Prepare Release
29
+
30
+ Switch to the `main` branch and pull the latest changes.
31
+
32
+ ```bash
33
+ git checkout main
34
+ git pull origin main
35
+ ```
36
+
37
+ ### 4. Bump Version
38
+
39
+ Update the package version. This command updates `package.json`, `package-lock.json`, creates a git commit, and adds a git tag.
40
+
41
+ ```bash
42
+ npm version <patch|minor|major>
43
+ ```
44
+
45
+ Examples:
46
+
47
+ - `npm version patch` (1.0.0 -> 1.0.1)
48
+ - `npm version minor` (1.0.0 -> 1.1.0)
49
+ - `npm version major` (1.0.0 -> 2.0.0)
50
+
51
+ ### 5. Publish
52
+
53
+ Publish the package to the registry.
54
+
55
+ ```bash
56
+ npm run publish
57
+ ```
58
+
59
+ > **Note:** This runs custom script `npm publish --access public`. The `prepare` script will automatically run the build process before publishing.
60
+
61
+ ### 6. Push Tags
62
+
63
+ Push the new version commit and tags to the repository.
64
+
65
+ ```bash
66
+ git push origin main --tags
67
+ ```
package/README.md ADDED
@@ -0,0 +1,118 @@
1
+ # @brt-innovation/aether
2
+
3
+ Shared library for Aether microservices.
4
+
5
+ This repository contains reusable components such as:
6
+
7
+ - Hono middlewares
8
+ - Common utilities
9
+ - Shared types and constants
10
+
11
+ The library is published as a public npm package and is intended to be shared across Aether microservices.
12
+
13
+ ---
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install @brt-innovation/aether
19
+ ```
20
+ ---
21
+
22
+ ## Usage
23
+
24
+ ### Importing the library
25
+
26
+ ```ts
27
+ import { authMiddleware, responseMiddleware , onAppError } from '@brt-innovation/aether';
28
+ ```
29
+
30
+ ---
31
+
32
+ ## Features
33
+
34
+ ### Hono Middleware
35
+
36
+ #### authMiddleware
37
+
38
+ Authentication middleware for Hono using JWT.
39
+
40
+ **Supported token sources**
41
+ - `Authorization` header (Bearer token)
42
+ - Cookie (fallback if header is missing)
43
+
44
+ **Example (inline usage)**
45
+
46
+ ```ts
47
+ import { Hono } from 'hono';
48
+ import { authMiddleware } from '@brt-innovation/aether';
49
+
50
+ const app = new Hono();
51
+
52
+ app.use('*', authMiddleware({
53
+ jwtSecret: process.env.JWT_SECRET,
54
+ }));
55
+ ```
56
+
57
+ **Example (reusable middleware instance)**
58
+
59
+ ```ts
60
+ import { Hono } from 'hono';
61
+ import { authMiddleware } from '@brt-innovation/aether';
62
+
63
+ const authMid = authMiddleware({
64
+ jwtSecret: process.env.JWT_SECRET,
65
+ });
66
+
67
+ const app = new Hono();
68
+ app.use('*', authMid);
69
+ ```
70
+
71
+ **Behavior**
72
+ - Extracts access token from `Authorization` header or cookies
73
+ - Verifies JWT signature and algorithm
74
+ - Stores `userId` in Hono context (`c.set('userId', payload.sub)`)
75
+ - Returns `401 Unauthorized` for invalid or missing token
76
+
77
+ ---
78
+
79
+ #### responseMiddleware
80
+
81
+ Standard response formatter middleware to ensure consistent API responses across services.
82
+
83
+ **Example**
84
+
85
+ ```ts
86
+ import { Hono } from 'hono';
87
+ import { responseMiddleware } from '@brt-innovation/aether';
88
+
89
+ const app = new Hono();
90
+
91
+ app.use('*', responseMiddleware);
92
+ ```
93
+
94
+ ---
95
+
96
+ #### onAppError
97
+
98
+ Global error handler for Hono applications.
99
+
100
+ This handler catches unhandled errors thrown during request processing
101
+ and transforms them into a standardized API error response.
102
+
103
+ **Example**
104
+
105
+ ```ts
106
+ import { Hono } from 'hono';
107
+ import { onAppError } from '@brt-innovation/aether';
108
+
109
+ const app = new Hono();
110
+
111
+ app.onError(onAppError);
112
+ ```
113
+
114
+ ---
115
+
116
+ ## License
117
+
118
+ UNLICENSED - Internal use only for BRT Innovation
@@ -0,0 +1,34 @@
1
+ import * as hono from 'hono';
2
+ import { Context, MiddlewareHandler } from 'hono';
3
+ import { SignatureAlgorithm } from 'hono/utils/jwt/jwa';
4
+ import { VerifyOptionsWithAlg } from 'hono/utils/jwt/jwt';
5
+
6
+ declare const responseMiddleware: (c: Context, next: () => Promise<void>) => Promise<void>;
7
+ declare const onAppError: (err: any, c: Context) => Response & hono.TypedResponse<{
8
+ message: string;
9
+ }, 200 | 201 | 400 | 401 | 403 | 404 | 409 | 422 | 500 | 502 | 503 | 300 | 100 | 102 | 103 | 202 | 203 | 206 | 207 | 208 | 226 | 301 | 302 | 303 | 305 | 306 | 307 | 308 | 402 | 405 | 406 | 407 | 408 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 421 | 423 | 424 | 425 | 426 | 428 | 429 | 431 | 451 | 501 | 504 | 505 | 506 | 507 | 508 | 510 | 511 | -1, "json">;
10
+
11
+ interface AuthMiddlewareOptions {
12
+ jwtSecret: string;
13
+ algorithm?: SignatureAlgorithm | VerifyOptionsWithAlg;
14
+ tokenCookieName?: string;
15
+ }
16
+ declare const authMiddleware: (options: AuthMiddlewareOptions) => MiddlewareHandler;
17
+
18
+ declare const httpStatusCode: {
19
+ ok: string;
20
+ created: string;
21
+ noContent: string;
22
+ badRequest: string;
23
+ unauthorized: string;
24
+ forbidden: string;
25
+ notFound: string;
26
+ conflict: string;
27
+ unprocessableEntity: string;
28
+ internalServerError: string;
29
+ badGateway: string;
30
+ serviceUnavailable: string;
31
+ };
32
+ declare const httpStatusCodeMapping: Record<number, string>;
33
+
34
+ export { type AuthMiddlewareOptions, authMiddleware, httpStatusCode, httpStatusCodeMapping, onAppError, responseMiddleware };
package/dist/index.js ADDED
@@ -0,0 +1,163 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ authMiddleware: () => authMiddleware,
24
+ httpStatusCode: () => httpStatusCode,
25
+ httpStatusCodeMapping: () => httpStatusCodeMapping,
26
+ onAppError: () => onAppError,
27
+ responseMiddleware: () => responseMiddleware
28
+ });
29
+ module.exports = __toCommonJS(index_exports);
30
+
31
+ // src/constant/response.constant.ts
32
+ var httpStatusCode = {
33
+ ok: "ok",
34
+ created: "created",
35
+ noContent: "no_content",
36
+ badRequest: "bad_request",
37
+ unauthorized: "unauthorized",
38
+ forbidden: "forbidden",
39
+ notFound: "not_found",
40
+ conflict: "conflict",
41
+ unprocessableEntity: "unprocessable_entity",
42
+ internalServerError: "internal_server_error",
43
+ badGateway: "bad_gateway",
44
+ serviceUnavailable: "service_unavailable"
45
+ };
46
+ var httpStatusCodeMapping = {
47
+ 200: httpStatusCode.ok,
48
+ 201: httpStatusCode.created,
49
+ 204: httpStatusCode.noContent,
50
+ 400: httpStatusCode.badRequest,
51
+ 401: httpStatusCode.unauthorized,
52
+ 403: httpStatusCode.forbidden,
53
+ 404: httpStatusCode.notFound,
54
+ 409: httpStatusCode.conflict,
55
+ 422: httpStatusCode.unprocessableEntity,
56
+ 500: httpStatusCode.internalServerError,
57
+ 502: httpStatusCode.badGateway,
58
+ 503: httpStatusCode.serviceUnavailable
59
+ };
60
+
61
+ // src/hono/middlewares/response.middleware.ts
62
+ var import_http_exception = require("hono/http-exception");
63
+ var resolveCodeFromStatus = (status) => {
64
+ if (httpStatusCodeMapping[status]) {
65
+ return httpStatusCodeMapping[status];
66
+ }
67
+ if (status >= 200 && status < 300) return "success";
68
+ if (status >= 400 && status < 500) return "client_error";
69
+ if (status >= 500) return "server_error";
70
+ return "unknown";
71
+ };
72
+ var responseMiddleware = async (c, next) => {
73
+ await next();
74
+ const contentType = c.res.headers.get("content-type") || "";
75
+ if (!contentType.includes("application/json")) return;
76
+ const status = c.res.status;
77
+ const originalBody = await c.res.clone().json();
78
+ const wrapped = {
79
+ statusCode: status,
80
+ code: resolveCodeFromStatus(status),
81
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
82
+ };
83
+ if (status < 400) {
84
+ wrapped.message = "success";
85
+ wrapped.data = originalBody;
86
+ } else {
87
+ wrapped.message = originalBody?.message || "error";
88
+ if (originalBody?.message) delete originalBody.message;
89
+ wrapped.data = originalBody;
90
+ }
91
+ c.res = new Response(JSON.stringify(wrapped), {
92
+ status,
93
+ headers: {
94
+ ...Object.fromEntries(c.res.headers),
95
+ "content-type": "application/json"
96
+ }
97
+ });
98
+ };
99
+ var onAppError = (err, c) => {
100
+ let status = 500;
101
+ let message = "Internal Server Error";
102
+ if (err instanceof import_http_exception.HTTPException) {
103
+ status = err.status;
104
+ message = err.message;
105
+ } else if (err instanceof Error) {
106
+ message = err.message;
107
+ }
108
+ return c.json(
109
+ {
110
+ message
111
+ },
112
+ status
113
+ );
114
+ };
115
+
116
+ // src/hono/middlewares/auth.middleware.ts
117
+ var import_jwt = require("hono/jwt");
118
+ var getToken = (c, cookieName) => {
119
+ const authHeader = c.req.header("authorization");
120
+ if (authHeader?.startsWith("Bearer ")) {
121
+ return authHeader.split(" ")[1];
122
+ }
123
+ const cookieHeader = c.req.header("cookie");
124
+ if (!cookieHeader) return null;
125
+ const cookies = Object.fromEntries(
126
+ cookieHeader.split(";").map((cookie) => {
127
+ const [key, ...value] = cookie.trim().split("=");
128
+ return [key, decodeURIComponent(value.join("="))];
129
+ })
130
+ );
131
+ return cookies[cookieName] ?? null;
132
+ };
133
+ var authMiddleware = (options) => {
134
+ const {
135
+ jwtSecret,
136
+ algorithm = "HS256",
137
+ tokenCookieName = "access_token"
138
+ } = options;
139
+ return async (c, next) => {
140
+ const token = getToken(c, tokenCookieName);
141
+ if (!token) {
142
+ return c.json({ message: "Unauthorized" }, 401);
143
+ }
144
+ if (!jwtSecret) {
145
+ return c.json({ message: "Server misconfigured" }, 500);
146
+ }
147
+ try {
148
+ const payload = await (0, import_jwt.verify)(token, jwtSecret, algorithm);
149
+ c.set("userId", payload.sub);
150
+ await next();
151
+ } catch (e) {
152
+ return c.json({ message: "Invalid token" }, 401);
153
+ }
154
+ };
155
+ };
156
+ // Annotate the CommonJS export names for ESM import in node:
157
+ 0 && (module.exports = {
158
+ authMiddleware,
159
+ httpStatusCode,
160
+ httpStatusCodeMapping,
161
+ onAppError,
162
+ responseMiddleware
163
+ });
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@brt-innovation/aether",
3
+ "version": "1.2.0",
4
+ "description": "Shared utilities and middlewares for Aether microservices",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "files": [
9
+ "dist"
10
+ ],
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "import": "./dist/index.js",
15
+ "require": "./dist/index.js"
16
+ }
17
+ },
18
+ "scripts": {
19
+ "build": "tsup src/index.ts --dts --tsconfig tsconfig.json",
20
+ "prepare": "npm run build",
21
+ "publish": "npm publish --access public"
22
+ },
23
+ "peerDependencies": {
24
+ "hono": "^4.0.0"
25
+ },
26
+ "devDependencies": {
27
+ "tsup": "^8.5.1",
28
+ "typescript": "^5.9.3"
29
+ },
30
+ "engines": {
31
+ "node": ">=20"
32
+ },
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "https://github.com/brtinnovation/aether-lib"
36
+ },
37
+ "publishConfig": {
38
+ "access": "public"
39
+ },
40
+ "license": "UNLICENSED"
41
+ }