@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 +67 -0
- package/README.md +118 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.js +163 -0
- package/package.json +41 -0
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
|
package/dist/index.d.ts
ADDED
|
@@ -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
|
+
}
|