@bgord/bun 0.3.3 → 0.3.5
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/package.json +9 -13
- package/src/api-version.ts +1 -4
- package/src/auth-shield.ts +9 -24
- package/src/cache-response.ts +1 -1
- package/src/cache-static-files.ts +3 -12
- package/src/download-file.ts +1 -1
- package/src/etag-extractor.ts +20 -24
- package/src/file-uploader.ts +2 -2
- package/src/graceful-shutdown.ts +2 -7
- package/src/healthcheck.ts +2 -6
- package/src/http-logger.ts +9 -26
- package/src/i18n.ts +15 -34
- package/src/rate-limit-shield.ts +1 -1
- package/src/setup.ts +3 -3
- package/src/time-zone-offset.ts +3 -3
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.3.
|
|
2
|
+
"version": "0.3.5",
|
|
3
3
|
"license": "MIT",
|
|
4
4
|
"name": "@bgord/bun",
|
|
5
5
|
"type": "module",
|
|
@@ -15,22 +15,18 @@
|
|
|
15
15
|
"typings": "./dist/index.d.ts",
|
|
16
16
|
"scripts": {
|
|
17
17
|
"build": "microbundle",
|
|
18
|
-
"prepare": "husky",
|
|
19
18
|
"preinstall": "bunx only-allow bun"
|
|
20
19
|
},
|
|
21
|
-
"files": [
|
|
22
|
-
"dist",
|
|
23
|
-
"src"
|
|
24
|
-
],
|
|
20
|
+
"files": ["dist", "src"],
|
|
25
21
|
"devDependencies": {
|
|
26
22
|
"@biomejs/biome": "1.9.4",
|
|
27
23
|
"@commitlint/cli": "19.5.0",
|
|
28
24
|
"@commitlint/config-conventional": "19.5.0",
|
|
29
|
-
"@types/bun": "1.1.
|
|
30
|
-
"@types/lodash": "4.17.
|
|
31
|
-
"cspell": "8.
|
|
32
|
-
"
|
|
33
|
-
"
|
|
25
|
+
"@types/bun": "1.1.13",
|
|
26
|
+
"@types/lodash": "4.17.13",
|
|
27
|
+
"cspell": "8.16.0",
|
|
28
|
+
"knip": "5.36.7",
|
|
29
|
+
"lefthook": "1.8.2",
|
|
34
30
|
"microbundle": "0.15.1",
|
|
35
31
|
"only-allow": "1.2.1",
|
|
36
32
|
"shellcheck": "3.0.0",
|
|
@@ -38,8 +34,8 @@
|
|
|
38
34
|
"typescript": "5.6.3"
|
|
39
35
|
},
|
|
40
36
|
"dependencies": {
|
|
41
|
-
"@bgord/node": "0.83.
|
|
42
|
-
"hono": "4.6.
|
|
37
|
+
"@bgord/node": "0.83.3",
|
|
38
|
+
"hono": "4.6.9",
|
|
43
39
|
"lodash": "4.17.21",
|
|
44
40
|
"sharp": "0.33.5"
|
|
45
41
|
},
|
package/src/api-version.ts
CHANGED
|
@@ -9,10 +9,7 @@ export class ApiVersion {
|
|
|
9
9
|
static attach = createMiddleware(async (c, next) => {
|
|
10
10
|
const build = await bg.BuildInfoRepository.extract();
|
|
11
11
|
|
|
12
|
-
c.res.headers.set(
|
|
13
|
-
ApiVersion.HEADER_NAME,
|
|
14
|
-
build.BUILD_VERSION ?? ApiVersion.DEFAULT_API_VERSION
|
|
15
|
-
);
|
|
12
|
+
c.res.headers.set(ApiVersion.HEADER_NAME, build.BUILD_VERSION ?? ApiVersion.DEFAULT_API_VERSION);
|
|
16
13
|
|
|
17
14
|
await next();
|
|
18
15
|
});
|
package/src/auth-shield.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import hono from "hono";
|
|
2
1
|
import * as bgn from "@bgord/node";
|
|
3
|
-
import
|
|
2
|
+
import hono from "hono";
|
|
4
3
|
import { createMiddleware } from "hono/factory";
|
|
5
4
|
import { HTTPException } from "hono/http-exception";
|
|
5
|
+
import { Lucia } from "lucia";
|
|
6
6
|
|
|
7
7
|
class SessionId {
|
|
8
8
|
private value: string | null;
|
|
@@ -28,20 +28,15 @@ export const AccessDeniedAuthShieldError = new HTTPException(403, {
|
|
|
28
28
|
message: "access_denied_auth_shield",
|
|
29
29
|
});
|
|
30
30
|
|
|
31
|
-
export class AuthShield<
|
|
32
|
-
T extends { password: bgn.PasswordType; id: bgn.IdType }
|
|
33
|
-
> {
|
|
31
|
+
export class AuthShield<T extends { password: bgn.PasswordType; id: bgn.IdType }> {
|
|
34
32
|
private readonly config: AuthShieldConfigType<T>;
|
|
35
33
|
|
|
36
34
|
constructor(
|
|
37
|
-
overrides: Omit<
|
|
38
|
-
AuthShieldConfigType<T>,
|
|
39
|
-
"Username" | "Password" | "HashedPassword"
|
|
40
|
-
> & {
|
|
35
|
+
overrides: Omit<AuthShieldConfigType<T>, "Username" | "Password" | "HashedPassword"> & {
|
|
41
36
|
Username?: typeof bgn.Username;
|
|
42
37
|
Password?: typeof bgn.Password;
|
|
43
38
|
HashedPassword?: typeof bgn.HashedPassword;
|
|
44
|
-
}
|
|
39
|
+
},
|
|
45
40
|
) {
|
|
46
41
|
const config = {
|
|
47
42
|
Username: overrides.Username ?? bgn.Username,
|
|
@@ -98,15 +93,10 @@ export class AuthShield<
|
|
|
98
93
|
return next();
|
|
99
94
|
}
|
|
100
95
|
|
|
101
|
-
const { session, user } = await this.config.lucia.validateSession(
|
|
102
|
-
sessionId
|
|
103
|
-
);
|
|
96
|
+
const { session, user } = await this.config.lucia.validateSession(sessionId);
|
|
104
97
|
|
|
105
98
|
if (!session) {
|
|
106
|
-
c.res.headers.set(
|
|
107
|
-
"Set-Cookie",
|
|
108
|
-
this.config.lucia.createBlankSessionCookie().serialize()
|
|
109
|
-
);
|
|
99
|
+
c.res.headers.set("Set-Cookie", this.config.lucia.createBlankSessionCookie().serialize());
|
|
110
100
|
c.set("user", null);
|
|
111
101
|
c.set("session", null);
|
|
112
102
|
|
|
@@ -114,10 +104,7 @@ export class AuthShield<
|
|
|
114
104
|
}
|
|
115
105
|
|
|
116
106
|
if (session.fresh) {
|
|
117
|
-
c.res.headers.set(
|
|
118
|
-
"Set-Cookie",
|
|
119
|
-
this.config.lucia.createSessionCookie(session.id).serialize()
|
|
120
|
-
);
|
|
107
|
+
c.res.headers.set("Set-Cookie", this.config.lucia.createSessionCookie(session.id).serialize());
|
|
121
108
|
}
|
|
122
109
|
c.set("user", user);
|
|
123
110
|
c.set("session", session);
|
|
@@ -134,9 +121,7 @@ export class AuthShield<
|
|
|
134
121
|
|
|
135
122
|
const user = await this.config.findUniqueUserOrThrow(username);
|
|
136
123
|
|
|
137
|
-
const hashedPassword = await this.config.HashedPassword.fromHash(
|
|
138
|
-
user.password
|
|
139
|
-
);
|
|
124
|
+
const hashedPassword = await this.config.HashedPassword.fromHash(user.password);
|
|
140
125
|
await hashedPassword.matchesOrThrow(password);
|
|
141
126
|
|
|
142
127
|
const session = await this.config.lucia.createSession(user.id, {});
|
package/src/cache-response.ts
CHANGED
|
@@ -11,22 +11,13 @@ export class CacheStaticFiles {
|
|
|
11
11
|
static handle(strategy: CacheStaticFilesStrategy) {
|
|
12
12
|
return createMiddleware(async (c, next) => {
|
|
13
13
|
if (strategy === CacheStaticFilesStrategy.never) {
|
|
14
|
-
c.res.headers.set(
|
|
15
|
-
"cache-control",
|
|
16
|
-
"private, no-cache, no-store, must-revalidate"
|
|
17
|
-
);
|
|
14
|
+
c.res.headers.set("cache-control", "private, no-cache, no-store, must-revalidate");
|
|
18
15
|
}
|
|
19
16
|
if (strategy === CacheStaticFilesStrategy.always) {
|
|
20
|
-
c.res.headers.set(
|
|
21
|
-
"cache-control",
|
|
22
|
-
`public, max-age=${bg.Time.Days(365).seconds}, immutable`
|
|
23
|
-
);
|
|
17
|
+
c.res.headers.set("cache-control", `public, max-age=${bg.Time.Days(365).seconds}, immutable`);
|
|
24
18
|
}
|
|
25
19
|
if (strategy === CacheStaticFilesStrategy.five_minutes) {
|
|
26
|
-
c.res.headers.set(
|
|
27
|
-
"cache-control",
|
|
28
|
-
`public, max-age=${bg.Time.Minutes(5).seconds}`
|
|
29
|
-
);
|
|
20
|
+
c.res.headers.set("cache-control", `public, max-age=${bg.Time.Minutes(5).seconds}`);
|
|
30
21
|
}
|
|
31
22
|
return next();
|
|
32
23
|
});
|
package/src/download-file.ts
CHANGED
package/src/etag-extractor.ts
CHANGED
|
@@ -7,35 +7,31 @@ export type EtagVariables = {
|
|
|
7
7
|
};
|
|
8
8
|
|
|
9
9
|
export class ETagExtractor {
|
|
10
|
-
static attach = createMiddleware<{ Variables: EtagVariables }>(
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const header = String(c.req.header(bg.ETag.IF_MATCH_HEADER_NAME));
|
|
10
|
+
static attach = createMiddleware<{ Variables: EtagVariables }>(async (c, next) => {
|
|
11
|
+
try {
|
|
12
|
+
const header = String(c.req.header(bg.ETag.IF_MATCH_HEADER_NAME));
|
|
14
13
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
await next();
|
|
14
|
+
if (!header || header === "undefined") c.set("ETag", null);
|
|
15
|
+
else c.set("ETag", bg.ETag.fromHeader(header));
|
|
16
|
+
} catch (error) {
|
|
17
|
+
c.set("ETag", null);
|
|
22
18
|
}
|
|
23
|
-
|
|
19
|
+
|
|
20
|
+
await next();
|
|
21
|
+
});
|
|
24
22
|
}
|
|
25
23
|
|
|
26
24
|
export class WeakETagExtractor {
|
|
27
|
-
static attach = createMiddleware<{ Variables: EtagVariables }>(
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const header = String(c.req.header(bg.WeakETag.IF_MATCH_HEADER_NAME));
|
|
25
|
+
static attach = createMiddleware<{ Variables: EtagVariables }>(async (c, next) => {
|
|
26
|
+
try {
|
|
27
|
+
const header = String(c.req.header(bg.WeakETag.IF_MATCH_HEADER_NAME));
|
|
31
28
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
await next();
|
|
29
|
+
if (!header || header === "undefined") c.set("WeakETag", null);
|
|
30
|
+
else c.set("WeakETag", bg.WeakETag.fromHeader(header));
|
|
31
|
+
} catch (error) {
|
|
32
|
+
c.set("WeakETag", null);
|
|
39
33
|
}
|
|
40
|
-
|
|
34
|
+
|
|
35
|
+
await next();
|
|
36
|
+
});
|
|
41
37
|
}
|
package/src/file-uploader.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as bgn from "@bgord/node";
|
|
2
|
+
import { bodyLimit } from "hono/body-limit";
|
|
2
3
|
import { createMiddleware } from "hono/factory";
|
|
3
4
|
import { HTTPException } from "hono/http-exception";
|
|
4
|
-
import { bodyLimit } from "hono/body-limit";
|
|
5
5
|
|
|
6
6
|
export const InvalidFileMimeTypeError = new HTTPException(400, {
|
|
7
7
|
message: "invalid_file_mime_type_error",
|
|
@@ -37,7 +37,7 @@ export class FileUploader {
|
|
|
37
37
|
|
|
38
38
|
const contentType = new bgn.Mime(file.type);
|
|
39
39
|
const accepted = config.mimeTypes.some((acceptedMimeType) =>
|
|
40
|
-
new bgn.Mime(acceptedMimeType).isSatisfiedBy(contentType)
|
|
40
|
+
new bgn.Mime(acceptedMimeType).isSatisfiedBy(contentType),
|
|
41
41
|
);
|
|
42
42
|
|
|
43
43
|
if (!accepted) throw InvalidFileMimeTypeError;
|
package/src/graceful-shutdown.ts
CHANGED
|
@@ -3,10 +3,7 @@ import * as bg from "@bgord/node";
|
|
|
3
3
|
type ServerType = ReturnType<typeof Bun.serve>;
|
|
4
4
|
|
|
5
5
|
export class GracefulShutdown {
|
|
6
|
-
private static async shutdown(
|
|
7
|
-
server: ServerType,
|
|
8
|
-
callback: () => any = bg.noop
|
|
9
|
-
) {
|
|
6
|
+
private static async shutdown(server: ServerType, callback: () => any = bg.noop) {
|
|
10
7
|
server.stop();
|
|
11
8
|
await callback();
|
|
12
9
|
// biome-ignore lint: lint/suspicious/noConsoleLog
|
|
@@ -30,9 +27,7 @@ export class GracefulShutdown {
|
|
|
30
27
|
|
|
31
28
|
process.on("unhandledRejection", async (event) => {
|
|
32
29
|
// biome-ignore lint: lint/suspicious/noConsoleLog
|
|
33
|
-
console.log(
|
|
34
|
-
"UnhandledPromiseRejectionWarning received: closing HTTP server"
|
|
35
|
-
);
|
|
30
|
+
console.log("UnhandledPromiseRejectionWarning received: closing HTTP server");
|
|
36
31
|
|
|
37
32
|
// biome-ignore lint: lint/suspicious/noConsoleLog
|
|
38
33
|
console.log(JSON.stringify(event));
|
package/src/healthcheck.ts
CHANGED
|
@@ -18,9 +18,7 @@ type HealthcheckResultType = {
|
|
|
18
18
|
} & bg.StopwatchResultType;
|
|
19
19
|
|
|
20
20
|
export class Healthcheck {
|
|
21
|
-
static build = (
|
|
22
|
-
prerequisites: bg.AbstractPrerequisite<bg.BasePrerequisiteConfig>[]
|
|
23
|
-
) =>
|
|
21
|
+
static build = (prerequisites: bg.AbstractPrerequisite<bg.BasePrerequisiteConfig>[]) =>
|
|
24
22
|
handler.createHandlers(async (c) => {
|
|
25
23
|
const stopwatch = new bg.Stopwatch();
|
|
26
24
|
|
|
@@ -33,9 +31,7 @@ export class Healthcheck {
|
|
|
33
31
|
details.push({ label: prerequisite.label, status });
|
|
34
32
|
}
|
|
35
33
|
|
|
36
|
-
const ok = details.every(
|
|
37
|
-
(result) => result.status !== bg.PrerequisiteStatusEnum.failure
|
|
38
|
-
)
|
|
34
|
+
const ok = details.every((result) => result.status !== bg.PrerequisiteStatusEnum.failure)
|
|
39
35
|
? bg.PrerequisiteStatusEnum.success
|
|
40
36
|
: bg.PrerequisiteStatusEnum.failure;
|
|
41
37
|
|
package/src/http-logger.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import * as bg from "@bgord/node";
|
|
2
|
-
import { createMiddleware } from "hono/factory";
|
|
3
2
|
import { getConnInfo } from "hono/bun";
|
|
3
|
+
import { createMiddleware } from "hono/factory";
|
|
4
4
|
import _ from "lodash";
|
|
5
5
|
|
|
6
6
|
export class HttpLogger {
|
|
7
7
|
private static simplify(response: unknown) {
|
|
8
8
|
const result = JSON.stringify(response, (_key, value) =>
|
|
9
|
-
Array.isArray(value) ? { type: "Array", length: value.length } : value
|
|
9
|
+
Array.isArray(value) ? { type: "Array", length: value.length } : value,
|
|
10
10
|
);
|
|
11
11
|
|
|
12
12
|
return JSON.parse(result);
|
|
@@ -42,10 +42,7 @@ export class HttpLogger {
|
|
|
42
42
|
const method = c.req.method;
|
|
43
43
|
|
|
44
44
|
const client = {
|
|
45
|
-
ip:
|
|
46
|
-
c.req.header("x-real-ip") ||
|
|
47
|
-
c.req.header("x-forwarded-for") ||
|
|
48
|
-
info.remote.address,
|
|
45
|
+
ip: c.req.header("x-real-ip") || c.req.header("x-forwarded-for") || info.remote.address,
|
|
49
46
|
userAgent: c.req.header("user-agent"),
|
|
50
47
|
};
|
|
51
48
|
|
|
@@ -57,10 +54,7 @@ export class HttpLogger {
|
|
|
57
54
|
|
|
58
55
|
const httpRequestBeforeMetadata = {
|
|
59
56
|
params: c.req.param(),
|
|
60
|
-
headers: _.omit(
|
|
61
|
-
c.req.raw.clone().headers.toJSON(),
|
|
62
|
-
HttpLogger.uninformativeHeaders
|
|
63
|
-
),
|
|
57
|
+
headers: _.omit(c.req.raw.clone().headers.toJSON(), HttpLogger.uninformativeHeaders),
|
|
64
58
|
body,
|
|
65
59
|
query: c.req.queries(),
|
|
66
60
|
};
|
|
@@ -72,22 +66,14 @@ export class HttpLogger {
|
|
|
72
66
|
method,
|
|
73
67
|
url,
|
|
74
68
|
client,
|
|
75
|
-
metadata: _.pickBy(
|
|
76
|
-
httpRequestBeforeMetadata,
|
|
77
|
-
(value) => !_.isEmpty(value)
|
|
78
|
-
),
|
|
69
|
+
metadata: _.pickBy(httpRequestBeforeMetadata, (value) => !_.isEmpty(value)),
|
|
79
70
|
});
|
|
80
71
|
|
|
81
72
|
await next();
|
|
82
73
|
|
|
83
|
-
const cacheHitHeader = c.res
|
|
84
|
-
.clone()
|
|
85
|
-
.headers.get(bg.CacheResponse.CACHE_HIT_HEADER);
|
|
74
|
+
const cacheHitHeader = c.res.clone().headers.get(bg.CacheResponse.CACHE_HIT_HEADER);
|
|
86
75
|
|
|
87
|
-
const cacheHit =
|
|
88
|
-
cacheHitHeader === bg.CacheHitEnum.hit
|
|
89
|
-
? bg.CacheHitEnum.hit
|
|
90
|
-
: undefined;
|
|
76
|
+
const cacheHit = cacheHitHeader === bg.CacheHitEnum.hit ? bg.CacheHitEnum.hit : undefined;
|
|
91
77
|
|
|
92
78
|
let response: any;
|
|
93
79
|
try {
|
|
@@ -101,12 +87,9 @@ export class HttpLogger {
|
|
|
101
87
|
|
|
102
88
|
const serverTimingMs = c.res.clone().headers.get("Server-Timing");
|
|
103
89
|
|
|
104
|
-
const durationMsMatch =
|
|
105
|
-
serverTimingMs?.match(/dur=([0-9]*\.?[0-9]+)/) ?? undefined;
|
|
90
|
+
const durationMsMatch = serverTimingMs?.match(/dur=([0-9]*\.?[0-9]+)/) ?? undefined;
|
|
106
91
|
|
|
107
|
-
const durationMs = durationMsMatch?.[1]
|
|
108
|
-
? Number(durationMsMatch[1])
|
|
109
|
-
: undefined;
|
|
92
|
+
const durationMs = durationMsMatch?.[1] ? Number(durationMsMatch[1]) : undefined;
|
|
110
93
|
|
|
111
94
|
logger.http({
|
|
112
95
|
operation: "http_request_after",
|
package/src/i18n.ts
CHANGED
|
@@ -1,21 +1,15 @@
|
|
|
1
|
-
import * as bgn from "@bgord/node";
|
|
2
1
|
import path from "node:path";
|
|
3
|
-
import
|
|
2
|
+
import * as bgn from "@bgord/node";
|
|
4
3
|
import { getCookie } from "hono/cookie";
|
|
4
|
+
import { createMiddleware } from "hono/factory";
|
|
5
5
|
|
|
6
6
|
export type TranslationsKeyType = string;
|
|
7
7
|
export type TranslationsValueType = string;
|
|
8
|
-
export type TranslationsType = Record<
|
|
9
|
-
TranslationsKeyType,
|
|
10
|
-
TranslationsValueType
|
|
11
|
-
>;
|
|
8
|
+
export type TranslationsType = Record<TranslationsKeyType, TranslationsValueType>;
|
|
12
9
|
|
|
13
10
|
export type TranslationPlaceholderType = string;
|
|
14
11
|
export type TranslationPlaceholderValueType = string | number;
|
|
15
|
-
export type TranslationVariableType = Record<
|
|
16
|
-
TranslationPlaceholderType,
|
|
17
|
-
TranslationPlaceholderValueType
|
|
18
|
-
>;
|
|
12
|
+
export type TranslationVariableType = Record<TranslationPlaceholderType, TranslationPlaceholderValueType>;
|
|
19
13
|
|
|
20
14
|
export type I18nConfigType = {
|
|
21
15
|
translationsPath?: bgn.Schema.PathType;
|
|
@@ -32,24 +26,19 @@ export type I18nVariablesType = {
|
|
|
32
26
|
export class I18n {
|
|
33
27
|
static LANGUAGE_COOKIE_NAME = "accept-language";
|
|
34
28
|
|
|
35
|
-
static DEFAULT_TRANSLATIONS_PATH =
|
|
36
|
-
bgn.Schema.Path.parse("infra/translations");
|
|
29
|
+
static DEFAULT_TRANSLATIONS_PATH = bgn.Schema.Path.parse("infra/translations");
|
|
37
30
|
|
|
38
31
|
static FALLBACK_LANGUAGE = "en";
|
|
39
32
|
|
|
40
33
|
static applyTo(config: I18nConfigType) {
|
|
41
34
|
return createMiddleware(async (c, next) => {
|
|
42
|
-
const translationsPath =
|
|
43
|
-
config?.translationsPath ?? I18n.DEFAULT_TRANSLATIONS_PATH;
|
|
35
|
+
const translationsPath = config?.translationsPath ?? I18n.DEFAULT_TRANSLATIONS_PATH;
|
|
44
36
|
|
|
45
37
|
const defaultLanguage = config?.defaultLanguage ?? I18n.FALLBACK_LANGUAGE;
|
|
46
38
|
|
|
47
|
-
const chosenLanguage =
|
|
48
|
-
getCookie(c, I18n.LANGUAGE_COOKIE_NAME) ?? defaultLanguage;
|
|
39
|
+
const chosenLanguage = getCookie(c, I18n.LANGUAGE_COOKIE_NAME) ?? defaultLanguage;
|
|
49
40
|
|
|
50
|
-
const language = Object.keys(config.supportedLanguages).find(
|
|
51
|
-
(language) => language === chosenLanguage
|
|
52
|
-
)
|
|
41
|
+
const language = Object.keys(config.supportedLanguages).find((language) => language === chosenLanguage)
|
|
53
42
|
? chosenLanguage
|
|
54
43
|
: I18n.FALLBACK_LANGUAGE;
|
|
55
44
|
|
|
@@ -63,12 +52,10 @@ export class I18n {
|
|
|
63
52
|
|
|
64
53
|
static async getTranslations(
|
|
65
54
|
language: bgn.Schema.LanguageType,
|
|
66
|
-
translationsPath: bgn.Schema.PathType
|
|
55
|
+
translationsPath: bgn.Schema.PathType,
|
|
67
56
|
): Promise<TranslationsType> {
|
|
68
57
|
try {
|
|
69
|
-
return Bun.file(
|
|
70
|
-
I18n.getTranslationPathForLanguage(language, translationsPath)
|
|
71
|
-
).json();
|
|
58
|
+
return Bun.file(I18n.getTranslationPathForLanguage(language, translationsPath)).json();
|
|
72
59
|
} catch (error) {
|
|
73
60
|
// biome-ignore lint: lint/suspicious/noConsoleLog
|
|
74
61
|
console.log("I18n#getTranslations", error);
|
|
@@ -78,10 +65,7 @@ export class I18n {
|
|
|
78
65
|
}
|
|
79
66
|
|
|
80
67
|
static useTranslations(translations: TranslationsType) {
|
|
81
|
-
return function translate(
|
|
82
|
-
key: TranslationsKeyType,
|
|
83
|
-
variables?: TranslationVariableType
|
|
84
|
-
) {
|
|
68
|
+
return function translate(key: TranslationsKeyType, variables?: TranslationVariableType) {
|
|
85
69
|
const translation = translations[key];
|
|
86
70
|
|
|
87
71
|
if (!translation) {
|
|
@@ -92,19 +76,16 @@ export class I18n {
|
|
|
92
76
|
if (!variables) return translation;
|
|
93
77
|
|
|
94
78
|
return Object.entries(variables).reduce(
|
|
95
|
-
(result, [placeholder, value]) =>
|
|
96
|
-
|
|
97
|
-
translation
|
|
79
|
+
(result, [placeholder, value]) => result.replace(`{{${placeholder}}}`, String(value)),
|
|
80
|
+
translation,
|
|
98
81
|
);
|
|
99
82
|
};
|
|
100
83
|
}
|
|
101
84
|
|
|
102
85
|
static getTranslationPathForLanguage(
|
|
103
86
|
language: bgn.Schema.LanguageType,
|
|
104
|
-
translationsPath = I18n.DEFAULT_TRANSLATIONS_PATH
|
|
87
|
+
translationsPath = I18n.DEFAULT_TRANSLATIONS_PATH,
|
|
105
88
|
): bgn.Schema.PathType {
|
|
106
|
-
return bgn.Schema.Path.parse(
|
|
107
|
-
path.join(translationsPath, `${language}.json`)
|
|
108
|
-
);
|
|
89
|
+
return bgn.Schema.Path.parse(path.join(translationsPath, `${language}.json`));
|
|
109
90
|
}
|
|
110
91
|
}
|
package/src/rate-limit-shield.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as bg from "@bgord/node";
|
|
2
|
-
import { HTTPException } from "hono/http-exception";
|
|
3
2
|
import { createMiddleware } from "hono/factory";
|
|
3
|
+
import { HTTPException } from "hono/http-exception";
|
|
4
4
|
|
|
5
5
|
type RateLimitShieldOptionsType = { ms: bg.Schema.TimestampType };
|
|
6
6
|
|
package/src/setup.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import * as bgn from "@bgord/node";
|
|
2
2
|
import { bodyLimit } from "hono/body-limit";
|
|
3
3
|
import { cors } from "hono/cors";
|
|
4
|
-
import { secureHeaders } from "hono/secure-headers";
|
|
5
4
|
import { requestId } from "hono/request-id";
|
|
5
|
+
import { secureHeaders } from "hono/secure-headers";
|
|
6
6
|
import { timing } from "hono/timing";
|
|
7
7
|
|
|
8
8
|
import { ApiVersion } from "./api-version";
|
|
9
|
-
import { TimeZoneOffset } from "./time-zone-offset";
|
|
10
9
|
import { Context } from "./context";
|
|
11
|
-
import {
|
|
10
|
+
import { ETagExtractor, WeakETagExtractor } from "./etag-extractor";
|
|
12
11
|
import { HttpLogger } from "./http-logger";
|
|
12
|
+
import { TimeZoneOffset } from "./time-zone-offset";
|
|
13
13
|
|
|
14
14
|
export const BODY_LIMIT_MAX_SIZE = new bgn.Size({
|
|
15
15
|
value: 128,
|
package/src/time-zone-offset.ts
CHANGED
|
@@ -14,7 +14,7 @@ export class TimeZoneOffset {
|
|
|
14
14
|
|
|
15
15
|
static attach = createMiddleware(async (c, next) => {
|
|
16
16
|
const timeZoneOffsetMinutes = bg.Schema.TimeZoneOffsetHeaderValue.parse(
|
|
17
|
-
c.req.header(TimeZoneOffset.TIME_ZONE_OFFSET_HEADER_NAME)
|
|
17
|
+
c.req.header(TimeZoneOffset.TIME_ZONE_OFFSET_HEADER_NAME),
|
|
18
18
|
);
|
|
19
19
|
|
|
20
20
|
const timeZoneOffset = {
|
|
@@ -30,14 +30,14 @@ export class TimeZoneOffset {
|
|
|
30
30
|
|
|
31
31
|
static adjustTimestamp(
|
|
32
32
|
timestamp: bg.Schema.TimestampType,
|
|
33
|
-
timeZoneOffsetMs: bg.Schema.TimeZoneOffsetValueType
|
|
33
|
+
timeZoneOffsetMs: bg.Schema.TimeZoneOffsetValueType,
|
|
34
34
|
): bg.Schema.TimestampType {
|
|
35
35
|
return timestamp - timeZoneOffsetMs;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
static adjustDate(
|
|
39
39
|
timestamp: bg.Schema.TimestampType,
|
|
40
|
-
timeZoneOffsetMs: bg.Schema.TimeZoneOffsetValueType
|
|
40
|
+
timeZoneOffsetMs: bg.Schema.TimeZoneOffsetValueType,
|
|
41
41
|
): Date {
|
|
42
42
|
return new Date(timestamp - timeZoneOffsetMs);
|
|
43
43
|
}
|