@bgord/bun 0.3.4 → 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 CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.3.4",
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.12",
30
- "@types/lodash": "4.17.12",
31
- "cspell": "8.15.4",
32
- "husky": "9.1.6",
33
- "knip": "5.34.0",
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",
@@ -39,7 +35,7 @@
39
35
  },
40
36
  "dependencies": {
41
37
  "@bgord/node": "0.83.3",
42
- "hono": "4.6.6",
38
+ "hono": "4.6.9",
43
39
  "lodash": "4.17.21",
44
40
  "sharp": "0.33.5"
45
41
  },
@@ -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
  });
@@ -1,8 +1,8 @@
1
- import hono from "hono";
2
1
  import * as bgn from "@bgord/node";
3
- import { Lucia } from "lucia";
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, {});
@@ -1,6 +1,6 @@
1
1
  import { createMiddleware } from "hono/factory";
2
- import NodeCache from "node-cache";
3
2
  import _ from "lodash";
3
+ import NodeCache from "node-cache";
4
4
 
5
5
  export enum CacheHitEnum {
6
6
  hit = "hit",
@@ -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
  });
@@ -1,5 +1,5 @@
1
- import * as bgn from "@bgord/node";
2
1
  import type { PathLike } from "node:fs";
2
+ import * as bgn from "@bgord/node";
3
3
 
4
4
  export type DownloadFileConfigType = { filename: PathLike; mime: bgn.Mime };
5
5
 
@@ -7,35 +7,31 @@ export type EtagVariables = {
7
7
  };
8
8
 
9
9
  export class ETagExtractor {
10
- static attach = createMiddleware<{ Variables: EtagVariables }>(
11
- async (c, next) => {
12
- try {
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
- if (!header || header === "undefined") c.set("ETag", null);
16
- else c.set("ETag", bg.ETag.fromHeader(header));
17
- } catch (error) {
18
- c.set("ETag", null);
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
- async (c, next) => {
29
- try {
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
- if (!header || header === "undefined") c.set("WeakETag", null);
33
- else c.set("WeakETag", bg.WeakETag.fromHeader(header));
34
- } catch (error) {
35
- c.set("WeakETag", null);
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
  }
@@ -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;
@@ -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));
@@ -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
 
@@ -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 { createMiddleware } from "hono/factory";
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
- result.replace(`{{${placeholder}}}`, String(value)),
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
  }
@@ -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 { WeakETagExtractor, ETagExtractor } from "./etag-extractor";
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,
@@ -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
  }