@bgord/bun 1.5.1 → 1.5.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.
Files changed (96) hide show
  1. package/dist/basic-auth-password.vo.d.ts +3 -3
  2. package/dist/basic-auth-password.vo.d.ts.map +1 -1
  3. package/dist/basic-auth-password.vo.js.map +1 -1
  4. package/dist/basic-auth-username.vo.d.ts +3 -3
  5. package/dist/basic-auth-username.vo.d.ts.map +1 -1
  6. package/dist/basic-auth-username.vo.js.map +1 -1
  7. package/dist/binary.vo.d.ts +4 -4
  8. package/dist/binary.vo.d.ts.map +1 -1
  9. package/dist/binary.vo.js.map +1 -1
  10. package/dist/crypto-key-provider-file.adapter.d.ts +11 -0
  11. package/dist/crypto-key-provider-file.adapter.d.ts.map +1 -0
  12. package/dist/crypto-key-provider-file.adapter.js +19 -0
  13. package/dist/crypto-key-provider-file.adapter.js.map +1 -0
  14. package/dist/crypto-key-provider-memory.adapter.d.ts +8 -0
  15. package/dist/crypto-key-provider-memory.adapter.d.ts.map +1 -0
  16. package/dist/crypto-key-provider-memory.adapter.js +11 -0
  17. package/dist/crypto-key-provider-memory.adapter.js.map +1 -0
  18. package/dist/encryption-bun.adapter.d.ts +3 -0
  19. package/dist/encryption-bun.adapter.d.ts.map +1 -1
  20. package/dist/encryption-bun.adapter.js +21 -14
  21. package/dist/encryption-bun.adapter.js.map +1 -1
  22. package/dist/encryption-key-value.vo.d.ts +8 -0
  23. package/dist/encryption-key-value.vo.d.ts.map +1 -0
  24. package/dist/encryption-key-value.vo.js +12 -0
  25. package/dist/encryption-key-value.vo.js.map +1 -0
  26. package/dist/encryption-key.vo.d.ts +13 -5
  27. package/dist/encryption-key.vo.d.ts.map +1 -1
  28. package/dist/encryption-key.vo.js +38 -11
  29. package/dist/encryption-key.vo.js.map +1 -1
  30. package/dist/encryption-noop.adapter.d.ts +2 -0
  31. package/dist/encryption-noop.adapter.d.ts.map +1 -1
  32. package/dist/encryption-noop.adapter.js +5 -1
  33. package/dist/encryption-noop.adapter.js.map +1 -1
  34. package/dist/environment-loader-encrypted.adapter.d.ts +22 -0
  35. package/dist/environment-loader-encrypted.adapter.d.ts.map +1 -0
  36. package/dist/environment-loader-encrypted.adapter.js +17 -0
  37. package/dist/environment-loader-encrypted.adapter.js.map +1 -0
  38. package/dist/environment-loader-process.adapter.d.ts +15 -0
  39. package/dist/environment-loader-process.adapter.d.ts.map +1 -0
  40. package/dist/environment-loader-process.adapter.js +16 -0
  41. package/dist/environment-loader-process.adapter.js.map +1 -0
  42. package/dist/event-stream.vo.d.ts +3 -3
  43. package/dist/event-stream.vo.d.ts.map +1 -1
  44. package/dist/event-stream.vo.js.map +1 -1
  45. package/dist/hcaptcha-secret-key.vo.d.ts +2 -2
  46. package/dist/hcaptcha-secret-key.vo.d.ts.map +1 -1
  47. package/dist/hcaptcha-secret-key.vo.js.map +1 -1
  48. package/dist/hcaptcha-site-key.vo.d.ts +2 -2
  49. package/dist/hcaptcha-site-key.vo.d.ts.map +1 -1
  50. package/dist/hcaptcha-site-key.vo.js +1 -4
  51. package/dist/hcaptcha-site-key.vo.js.map +1 -1
  52. package/dist/index.d.ts +5 -2
  53. package/dist/index.d.ts.map +1 -1
  54. package/dist/index.js +5 -2
  55. package/dist/index.js.map +1 -1
  56. package/dist/recaptcha-secret-key.vo.d.ts +2 -2
  57. package/dist/recaptcha-secret-key.vo.d.ts.map +1 -1
  58. package/dist/recaptcha-secret-key.vo.js.map +1 -1
  59. package/dist/recaptcha-site-key.vo.d.ts +2 -2
  60. package/dist/recaptcha-site-key.vo.d.ts.map +1 -1
  61. package/dist/recaptcha-site-key.vo.js +1 -4
  62. package/dist/recaptcha-site-key.vo.js.map +1 -1
  63. package/dist/tsconfig.tsbuildinfo +1 -1
  64. package/dist/visitor-id.vo.d.ts +3 -3
  65. package/dist/visitor-id.vo.d.ts.map +1 -1
  66. package/dist/visitor-id.vo.js.map +1 -1
  67. package/package.json +8 -6
  68. package/readme.md +5 -2
  69. package/src/basic-auth-password.vo.ts +1 -1
  70. package/src/basic-auth-username.vo.ts +1 -1
  71. package/src/binary.vo.ts +1 -1
  72. package/src/crypto-key-provider-file.adapter.ts +28 -0
  73. package/src/crypto-key-provider-memory.adapter.ts +17 -0
  74. package/src/encryption-bun.adapter.ts +28 -24
  75. package/src/encryption-key-value.vo.ts +16 -0
  76. package/src/encryption-key.vo.ts +43 -12
  77. package/src/encryption-noop.adapter.ts +3 -1
  78. package/src/environment-loader-encrypted.adapter.ts +25 -0
  79. package/src/environment-loader-process.adapter.ts +22 -0
  80. package/src/event-stream.vo.ts +1 -1
  81. package/src/hcaptcha-secret-key.vo.ts +1 -1
  82. package/src/hcaptcha-site-key.vo.ts +1 -4
  83. package/src/index.ts +5 -2
  84. package/src/recaptcha-secret-key.vo.ts +1 -1
  85. package/src/recaptcha-site-key.vo.ts +1 -4
  86. package/src/visitor-id.vo.ts +1 -1
  87. package/dist/crypto-key-provider-env.adapter.d.ts +0 -8
  88. package/dist/crypto-key-provider-env.adapter.d.ts.map +0 -1
  89. package/dist/crypto-key-provider-env.adapter.js +0 -14
  90. package/dist/crypto-key-provider-env.adapter.js.map +0 -1
  91. package/dist/environment-loader-process-env.adapter.d.ts +0 -16
  92. package/dist/environment-loader-process-env.adapter.d.ts.map +0 -1
  93. package/dist/environment-loader-process-env.adapter.js +0 -15
  94. package/dist/environment-loader-process-env.adapter.js.map +0 -1
  95. package/src/crypto-key-provider-env.adapter.ts +0 -16
  96. package/src/environment-loader-process-env.adapter.ts +0 -22
@@ -1,8 +1,8 @@
1
1
  import { z } from "zod/v4";
2
2
  export declare const VisitorIdError: {
3
- readonly Type: "visitor.id.type";
4
- readonly Empty: "visitor.id.empty";
5
- readonly TooLong: "visitor.id.too.long";
3
+ Type: string;
4
+ Empty: string;
5
+ TooLong: string;
6
6
  };
7
7
  export declare const VisitorId: z.core.$ZodBranded<z.ZodString, "VisitorId">;
8
8
  export type VisitorIdType = z.infer<typeof VisitorId>;
@@ -1 +1 @@
1
- {"version":3,"file":"visitor-id.vo.d.ts","sourceRoot":"","sources":["../src/visitor-id.vo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AAE3B,eAAO,MAAM,cAAc;;;;CAIjB,CAAC;AAEX,eAAO,MAAM,SAAS,8CAID,CAAC;AAEtB,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,CAAC"}
1
+ {"version":3,"file":"visitor-id.vo.d.ts","sourceRoot":"","sources":["../src/visitor-id.vo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AAE3B,eAAO,MAAM,cAAc;;;;CAI1B,CAAC;AAEF,eAAO,MAAM,SAAS,8CAID,CAAC;AAEtB,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"visitor-id.vo.js","sourceRoot":"","sources":["../src/visitor-id.vo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AAE3B,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,IAAI,EAAE,iBAAiB;IACvB,KAAK,EAAE,kBAAkB;IACzB,OAAO,EAAE,qBAAqB;CACtB,CAAC;AAEX,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC;KACvB,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC;KAC3B,GAAG,CAAC,CAAC,EAAE,cAAc,CAAC,KAAK,CAAC;KAC5B,GAAG,CAAC,EAAE,EAAE,cAAc,CAAC,OAAO,CAAC;KAC/B,KAAK,CAAC,WAAW,CAAC,CAAC"}
1
+ {"version":3,"file":"visitor-id.vo.js","sourceRoot":"","sources":["../src/visitor-id.vo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AAE3B,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,IAAI,EAAE,iBAAiB;IACvB,KAAK,EAAE,kBAAkB;IACzB,OAAO,EAAE,qBAAqB;CAC/B,CAAC;AAEF,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC;KACvB,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC;KAC3B,GAAG,CAAC,CAAC,EAAE,cAAc,CAAC,KAAK,CAAC;KAC5B,GAAG,CAAC,EAAE,EAAE,cAAc,CAAC,OAAO,CAAC;KAC/B,KAAK,CAAC,WAAW,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bgord/bun",
3
- "version": "1.5.1",
3
+ "version": "1.5.2",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "author": "Bartosz Gordon",
@@ -20,7 +20,7 @@
20
20
  "preinstall": "bunx only-allow bun"
21
21
  },
22
22
  "devDependencies": {
23
- "@biomejs/biome": "2.3.8",
23
+ "@biomejs/biome": "2.3.9",
24
24
  "@commitlint/cli": "20.2.0",
25
25
  "@commitlint/config-conventional": "20.2.0",
26
26
  "@types/bun": "1.3.4",
@@ -28,21 +28,23 @@
28
28
  "@types/nodemailer": "7.0.4",
29
29
  "@types/yazl": "3.3.0",
30
30
  "cspell": "9.4.0",
31
- "knip": "5.73.4",
31
+ "knip": "5.74.0",
32
32
  "lefthook": "2.0.12",
33
+ "lockfile-lint": "4.14.1",
33
34
  "only-allow": "1.2.2",
34
35
  "sharp": "0.34.5",
35
36
  "shellcheck": "4.1.0",
36
37
  "typescript": "5.9.3",
37
- "zod": "4.2.0"
38
+ "zod": "4.2.1"
38
39
  },
39
40
  "dependencies": {
40
41
  "@axiomhq/winston": "1.3.1",
41
42
  "@bgord/tools": "1.2.1",
42
- "@hono/ua-blocker": "0.1.21",
43
+ "@hono/ua-blocker": "0.1.22",
43
44
  "better-auth": "1.4.7",
44
45
  "croner": "9.1.0",
45
46
  "csv": "6.4.1",
47
+ "dotenv": "17.2.3",
46
48
  "hcaptcha": "0.2.0",
47
49
  "hono": "4.11.1",
48
50
  "isomorphic-dompurify": "2.34.0",
@@ -54,7 +56,7 @@
54
56
  "yazl": "3.3.1"
55
57
  },
56
58
  "peerDependencies": {
57
- "zod": "4.2.0",
59
+ "zod": "4.2.1",
58
60
  "sharp": "0.34.5"
59
61
  },
60
62
  "peerDependenciesMeta": {
package/readme.md CHANGED
@@ -56,7 +56,8 @@ src/
56
56
  ├── context.middleware.ts
57
57
  ├── correlation-id.vo.ts
58
58
  ├── correlation-storage.service.ts
59
- ├── crypto-key-provider-env.adapter.ts
59
+ ├── crypto-key-provider-file.adapter.ts
60
+ ├── crypto-key-provider-memory.adapter.ts
60
61
  ├── crypto-key-provider-noop.adapter.ts
61
62
  ├── crypto-key-provider.port.ts
62
63
  ├── csv-stringifier.adapter.ts
@@ -68,10 +69,12 @@ src/
68
69
  ├── dispatching-event-store.ts
69
70
  ├── encryption-bun.adapter.ts
70
71
  ├── encryption-iv.vo.ts
72
+ ├── encryption-key-value.vo.ts
71
73
  ├── encryption-key.vo.ts
72
74
  ├── encryption-noop.adapter.ts
73
75
  ├── encryption.port.ts
74
- ├── environment-loader-process-env.adapter.ts
76
+ ├── environment-loader-encrypted.adapter.ts
77
+ ├── environment-loader-process.adapter.ts
75
78
  ├── environment-loader.port.ts
76
79
  ├── etag-extractor.middleware.ts
77
80
  ├── event-bus-like.types.ts
@@ -4,7 +4,7 @@ export const BasicAuthPasswordError = {
4
4
  Type: "basic.auth.password.type",
5
5
  Empty: "basic.auth.password.empty",
6
6
  TooLong: "basic.auth.password.too.long",
7
- } as const;
7
+ };
8
8
 
9
9
  export const BasicAuthPassword = z
10
10
  .string(BasicAuthPasswordError.Type)
@@ -4,7 +4,7 @@ export const BasicAuthUsernameError = {
4
4
  Type: "basic.auth.username.type",
5
5
  Empty: "basic.auth.username.empty",
6
6
  TooLong: "basic.auth.username.too.long",
7
- } as const;
7
+ };
8
8
 
9
9
  export const BasicAuthUsername = z
10
10
  .string(BasicAuthUsernameError.Type)
package/src/binary.vo.ts CHANGED
@@ -5,7 +5,7 @@ export const BinaryError = {
5
5
  Empty: "binary.empty",
6
6
  TooLong: "binary.too.long",
7
7
  BadChars: "binary.bad.chars",
8
- } as const;
8
+ };
9
9
 
10
10
  // One to sixty four letters, digits, hyphens, or underscores
11
11
  const BINARY_WHITELIST = /^[a-zA-Z0-9-_]{1,64}$/;
@@ -0,0 +1,28 @@
1
+ import type * as tools from "@bgord/tools";
2
+ import type { CryptoKeyProviderPort } from "./crypto-key-provider.port";
3
+ import { EncryptionKey } from "./encryption-key.vo";
4
+
5
+ export const CryptoKeyProviderFileAdapterError = {
6
+ MissingFile: "crypto.key.provider.file.adapter.missing.file",
7
+ };
8
+
9
+ export class CryptoKeyProviderFileAdapter implements CryptoKeyProviderPort {
10
+ constructor(private readonly path: tools.FilePathAbsolute) {}
11
+
12
+ async get() {
13
+ const file = Bun.file(this.path.get());
14
+ const exists = await file.exists();
15
+
16
+ if (!exists) throw new Error(CryptoKeyProviderFileAdapterError.MissingFile);
17
+
18
+ const encryptionKey = EncryptionKey.fromString(await file.text());
19
+
20
+ return crypto.subtle.importKey(
21
+ "raw",
22
+ encryptionKey.toBuffer() as BufferSource,
23
+ { name: "AES-GCM" },
24
+ false,
25
+ ["encrypt", "decrypt"],
26
+ );
27
+ }
28
+ }
@@ -0,0 +1,17 @@
1
+ import type { CryptoKeyProviderPort } from "./crypto-key-provider.port";
2
+ import { EncryptionKey } from "./encryption-key.vo";
3
+ import type { EncryptionKeyValueType } from "./encryption-key-value.vo";
4
+
5
+ export class CryptoKeyProviderMemoryAdapter implements CryptoKeyProviderPort {
6
+ constructor(private readonly ENCRYPTION_KEY_VALUE: EncryptionKeyValueType) {}
7
+
8
+ async get(): Promise<CryptoKey> {
9
+ return crypto.subtle.importKey(
10
+ "raw",
11
+ EncryptionKey.fromString(this.ENCRYPTION_KEY_VALUE).toBuffer() as BufferSource,
12
+ { name: "AES-GCM" },
13
+ false,
14
+ ["encrypt", "decrypt"],
15
+ );
16
+ }
17
+ }
@@ -3,26 +3,35 @@ import type { CryptoKeyProviderPort } from "./crypto-key-provider.port";
3
3
  import type { EncryptionPort, EncryptionRecipe } from "./encryption.port";
4
4
  import { EncryptionIV } from "./encryption-iv.vo";
5
5
 
6
- export const EncryptionBunAdapterError = { InvalidPayload: "encryption.bun.adapter.invalid.payload" };
6
+ export const EncryptionBunAdapterError = {
7
+ InvalidPayload: "encryption.bun.adapter.invalid.payload",
8
+ MissingFile: "encryption.bun.adapter.missing.file",
9
+ };
7
10
 
8
11
  type Dependencies = { CryptoKeyProvider: CryptoKeyProviderPort };
9
12
 
10
13
  export class EncryptionBunAdapter implements EncryptionPort {
14
+ private static ALGORITHM = "AES-GCM";
15
+
11
16
  constructor(private readonly deps: Dependencies) {}
12
17
 
13
18
  async encrypt(recipe: EncryptionRecipe) {
14
19
  const key = await this.deps.CryptoKeyProvider.get();
15
20
  const iv = EncryptionIV.generate();
16
21
 
17
- const plaintext = await Bun.file(recipe.input.get()).arrayBuffer();
22
+ const file = Bun.file(recipe.input.get());
23
+ if (!(await file.exists())) throw new Error(EncryptionBunAdapterError.MissingFile);
24
+
25
+ const plaintext = await file.arrayBuffer();
18
26
 
19
27
  const encrypted = await crypto.subtle.encrypt(
20
- { name: "AES-GCM", iv: iv.buffer as ArrayBuffer },
28
+ { name: EncryptionBunAdapter.ALGORITHM, iv: iv.buffer as ArrayBuffer },
21
29
  key,
22
30
  plaintext,
23
31
  );
24
32
 
25
33
  const ciphertext = new Uint8Array(encrypted);
34
+ // Combine IV + Ciphertext
26
35
  const output = new Uint8Array(iv.length + ciphertext.length);
27
36
  output.set(iv, 0);
28
37
  output.set(ciphertext, iv.length);
@@ -33,44 +42,39 @@ export class EncryptionBunAdapter implements EncryptionPort {
33
42
  }
34
43
 
35
44
  async decrypt(recipe: EncryptionRecipe) {
36
- const key = await this.deps.CryptoKeyProvider.get();
37
-
38
- const bytes = new Uint8Array(await Bun.file(recipe.input.get()).arrayBuffer());
39
- if (bytes.length < EncryptionIV.LENGTH + 1) throw new Error(EncryptionBunAdapterError.InvalidPayload);
40
-
41
- const iv = bytes.subarray(0, EncryptionIV.LENGTH);
42
- const ivBuffer = iv.buffer.slice(iv.byteOffset, iv.byteOffset + iv.byteLength);
43
-
44
- const ciphertext = bytes.subarray(EncryptionIV.LENGTH);
45
-
46
- const ciphertextBuffer = ciphertext.buffer.slice(
47
- ciphertext.byteOffset,
48
- ciphertext.byteOffset + ciphertext.byteLength,
49
- );
50
-
51
- const decrypted = await crypto.subtle.decrypt({ name: "AES-GCM", iv: ivBuffer }, key, ciphertextBuffer);
52
-
45
+ const decrypted = await this.decryptFile(recipe.input);
53
46
  await Bun.write(recipe.output.get(), new Uint8Array(decrypted));
54
-
55
47
  return recipe.output;
56
48
  }
57
49
 
58
50
  async view(input: tools.FilePathRelative | tools.FilePathAbsolute) {
51
+ return this.decryptFile(input);
52
+ }
53
+
54
+ private async decryptFile(input: tools.FilePathRelative | tools.FilePathAbsolute): Promise<ArrayBuffer> {
59
55
  const key = await this.deps.CryptoKeyProvider.get();
60
56
 
61
- const bytes = new Uint8Array(await Bun.file(input.get()).arrayBuffer());
57
+ const file = Bun.file(input.get());
58
+ if (!(await file.exists())) throw new Error(EncryptionBunAdapterError.MissingFile);
59
+
60
+ const bytes = new Uint8Array(await file.arrayBuffer());
61
+
62
+ // Payload must be at least IV length + 1 byte of content/tag
62
63
  if (bytes.length < EncryptionIV.LENGTH + 1) throw new Error(EncryptionBunAdapterError.InvalidPayload);
63
64
 
64
65
  const iv = bytes.subarray(0, EncryptionIV.LENGTH);
65
66
  const ivBuffer = iv.buffer.slice(iv.byteOffset, iv.byteOffset + iv.byteLength);
66
67
 
67
68
  const ciphertext = bytes.subarray(EncryptionIV.LENGTH);
68
-
69
69
  const ciphertextBuffer = ciphertext.buffer.slice(
70
70
  ciphertext.byteOffset,
71
71
  ciphertext.byteOffset + ciphertext.byteLength,
72
72
  );
73
73
 
74
- return crypto.subtle.decrypt({ name: "AES-GCM", iv: ivBuffer }, key, ciphertextBuffer);
74
+ return crypto.subtle.decrypt(
75
+ { name: EncryptionBunAdapter.ALGORITHM, iv: ivBuffer },
76
+ key,
77
+ ciphertextBuffer,
78
+ );
75
79
  }
76
80
  }
@@ -0,0 +1,16 @@
1
+ import { z } from "zod/v4";
2
+
3
+ export const EncryptionKeyValueError = {
4
+ Type: "encryption.key.value.type",
5
+ InvalidHex: "encryption.key.value.invalid.hex",
6
+ };
7
+
8
+ // 64 hex chars allowed
9
+ const CHARS_WHITELIST = /^[a-fA-F0-9]{64}$/;
10
+
11
+ export const EncryptionKeyValue = z
12
+ .string(EncryptionKeyValueError.Type)
13
+ .regex(CHARS_WHITELIST, EncryptionKeyValueError.InvalidHex)
14
+ .brand("EncryptionKeyValue");
15
+
16
+ export type EncryptionKeyValueType = z.infer<typeof EncryptionKeyValue>;
@@ -1,16 +1,47 @@
1
- import { z } from "zod/v4";
1
+ import { EncryptionKeyValue, type EncryptionKeyValueType } from "./encryption-key-value.vo";
2
2
 
3
- export const EncryptionKeyError = {
4
- Type: "encryption.key.type",
5
- InvalidHex: "encryption.key.invalid.hex",
6
- } as const;
3
+ export const EncryptionKeyError = { InvalidBuffer: "encryption.key.invalid.buffer" };
7
4
 
8
- // 64 hex chars allowed
9
- const CHARS_WHITELIST = /^[a-fA-F0-9]{64}$/;
5
+ export class EncryptionKey {
6
+ private constructor(private readonly value: EncryptionKeyValueType) {}
10
7
 
11
- export const EncryptionKey = z
12
- .string(EncryptionKeyError.Type)
13
- .regex(CHARS_WHITELIST, EncryptionKeyError.InvalidHex)
14
- .brand("EncryptionKey");
8
+ static fromStringSafe(value: EncryptionKeyValueType): EncryptionKey {
9
+ return new EncryptionKey(value);
10
+ }
15
11
 
16
- export type EncryptionKeyType = z.infer<typeof EncryptionKey>;
12
+ static fromString(candidate: string): EncryptionKey {
13
+ return new EncryptionKey(EncryptionKeyValue.parse(candidate));
14
+ }
15
+
16
+ static fromBuffer(buffer: Uint8Array): EncryptionKey {
17
+ if (buffer.length !== 32) throw new Error(EncryptionKeyError.InvalidBuffer);
18
+
19
+ const hex = Array.from(buffer)
20
+ .map((byte) => byte.toString(16).padStart(2, "0"))
21
+ .join("");
22
+
23
+ return EncryptionKey.fromString(hex);
24
+ }
25
+
26
+ equals(another: EncryptionKey): boolean {
27
+ return this.value === another.value;
28
+ }
29
+
30
+ toBuffer(): Uint8Array {
31
+ const bytes = new Uint8Array(32);
32
+
33
+ for (let i = 0; i < 32; i++) {
34
+ bytes[i] = Number.parseInt(this.value.slice(i * 2, i * 2 + 2), 16);
35
+ }
36
+
37
+ return bytes;
38
+ }
39
+
40
+ toString(): string {
41
+ return "EncryptionKey";
42
+ }
43
+
44
+ toJSON(): string {
45
+ return "EncryptionKey";
46
+ }
47
+ }
@@ -2,6 +2,8 @@ import type * as tools from "@bgord/tools";
2
2
  import type { EncryptionPort, EncryptionRecipe } from "./encryption.port";
3
3
 
4
4
  export class EncryptionNoopAdapter implements EncryptionPort {
5
+ constructor(private readonly buffer: ArrayBuffer = new TextEncoder().encode("noop").buffer) {}
6
+
5
7
  async encrypt(recipe: EncryptionRecipe) {
6
8
  return recipe.output;
7
9
  }
@@ -11,6 +13,6 @@ export class EncryptionNoopAdapter implements EncryptionPort {
11
13
  }
12
14
 
13
15
  async view(_input: tools.FilePathRelative | tools.FilePathAbsolute) {
14
- return new TextEncoder().encode("noop").buffer;
16
+ return this.buffer;
15
17
  }
16
18
  }
@@ -0,0 +1,25 @@
1
+ import type * as tools from "@bgord/tools";
2
+ import { parse } from "dotenv";
3
+ import type { z } from "zod/v4";
4
+ import type { NodeEnvironmentEnum } from "../src/node-env.vo";
5
+ import type { EncryptionPort } from "./encryption.port";
6
+ import type { EnvironmentLoaderPort } from "./environment-loader.port";
7
+
8
+ type Dependencies = { Encryption: EncryptionPort };
9
+
10
+ export class EnvironmentLoaderEncryptedAdapter<Schema extends z.ZodObject<any>>
11
+ implements EnvironmentLoaderPort<Schema>
12
+ {
13
+ constructor(
14
+ private readonly config: { type: NodeEnvironmentEnum; Schema: Schema },
15
+ private path: tools.FilePathRelative,
16
+ private readonly deps: Dependencies,
17
+ ) {}
18
+
19
+ async load() {
20
+ const file = await this.deps.Encryption.view(this.path);
21
+ const content = new TextDecoder().decode(file);
22
+
23
+ return Object.freeze({ ...this.config.Schema.parse(parse(content)), type: this.config.type });
24
+ }
25
+ }
@@ -0,0 +1,22 @@
1
+ import type { z } from "zod/v4";
2
+ import type { NodeEnvironmentEnum } from "../src/node-env.vo";
3
+ import type { EnvironmentLoaderPort } from "./environment-loader.port";
4
+
5
+ export class EnvironmentLoaderProcessAdapter<Schema extends z.ZodObject<any>>
6
+ implements EnvironmentLoaderPort<Schema>
7
+ {
8
+ constructor(
9
+ private readonly config: { type: NodeEnvironmentEnum; Schema: Schema },
10
+ private env: NodeJS.ProcessEnv,
11
+ ) {}
12
+
13
+ async load() {
14
+ const result = this.config.Schema.parse(this.env);
15
+
16
+ for (const key of Object.keys(result)) {
17
+ delete process.env[key];
18
+ }
19
+
20
+ return Object.freeze({ ...result, type: this.config.type });
21
+ }
22
+ }
@@ -4,7 +4,7 @@ export const EventStreamError = {
4
4
  Type: "event.store.type",
5
5
  Empty: "event.stream.empty",
6
6
  TooLong: "event.stream.too.long",
7
- } as const;
7
+ };
8
8
 
9
9
  export const EventStream = z
10
10
  .string(EventStreamError.Type)
@@ -3,7 +3,7 @@ import { z } from "zod/v4";
3
3
  export const HCaptchaSecretKeyError = {
4
4
  Type: "hcaptcha.secret.key.type",
5
5
  Length: "hcaptcha.secret.key.length",
6
- } as const;
6
+ };
7
7
 
8
8
  export const HCaptchaSecretKey = z
9
9
  .string(HCaptchaSecretKeyError.Type)
@@ -1,9 +1,6 @@
1
1
  import { z } from "zod/v4";
2
2
 
3
- export const HCaptchaSiteKeyError = {
4
- Type: "hcaptcha.site.key.type",
5
- Length: "hcaptcha.site.key.length",
6
- } as const;
3
+ export const HCaptchaSiteKeyError = { Type: "hcaptcha.site.key.type", Length: "hcaptcha.site.key.length" };
7
4
 
8
5
  export const HCaptchaSiteKey = z
9
6
  .string(HCaptchaSiteKeyError.Type)
package/src/index.ts CHANGED
@@ -30,7 +30,8 @@ export * from "./context.middleware";
30
30
  export * from "./correlation-id.vo";
31
31
  export * from "./correlation-storage.service";
32
32
  export * from "./crypto-key-provider.port";
33
- export * from "./crypto-key-provider-env.adapter";
33
+ export * from "./crypto-key-provider-file.adapter";
34
+ export * from "./crypto-key-provider-memory.adapter";
34
35
  export * from "./crypto-key-provider-noop.adapter";
35
36
  export * from "./csv-stringifier.adapter";
36
37
  export * from "./csv-stringifier.port";
@@ -42,9 +43,11 @@ export * from "./dispatching-event-store";
42
43
  export * from "./encryption.port";
43
44
  export * from "./encryption-bun.adapter";
44
45
  export * from "./encryption-key.vo";
46
+ export * from "./encryption-key-value.vo";
45
47
  export * from "./encryption-noop.adapter";
46
48
  export * from "./environment-loader.port";
47
- export * from "./environment-loader-process-env.adapter";
49
+ export * from "./environment-loader-encrypted.adapter";
50
+ export * from "./environment-loader-process.adapter";
48
51
  export * from "./etag-extractor.middleware";
49
52
  export * from "./event.types";
50
53
  export * from "./event-bus-like.types";
@@ -3,7 +3,7 @@ import { z } from "zod/v4";
3
3
  export const RecaptchaSecretKeyError = {
4
4
  Type: "recaptcha.secret.key.type",
5
5
  Length: "recaptcha.secret.key.length",
6
- } as const;
6
+ };
7
7
 
8
8
  export const RecaptchaSecretKey = z
9
9
  .string(RecaptchaSecretKeyError.Type)
@@ -1,9 +1,6 @@
1
1
  import { z } from "zod/v4";
2
2
 
3
- export const RecaptchaSiteKeyError = {
4
- Type: "recaptcha.site.key.type",
5
- Length: "recaptcha.site.key.length",
6
- } as const;
3
+ export const RecaptchaSiteKeyError = { Type: "recaptcha.site.key.type", Length: "recaptcha.site.key.length" };
7
4
 
8
5
  export const RecaptchaSiteKey = z
9
6
  .string(RecaptchaSiteKeyError.Type)
@@ -4,7 +4,7 @@ export const VisitorIdError = {
4
4
  Type: "visitor.id.type",
5
5
  Empty: "visitor.id.empty",
6
6
  TooLong: "visitor.id.too.long",
7
- } as const;
7
+ };
8
8
 
9
9
  export const VisitorId = z
10
10
  .string(VisitorIdError.Type)
@@ -1,8 +0,0 @@
1
- import type { CryptoKeyProviderPort } from "./crypto-key-provider.port";
2
- import type { EncryptionKeyType } from "./encryption-key.vo";
3
- export declare class CryptoKeyProviderEnvAdapter implements CryptoKeyProviderPort {
4
- private readonly ENCRYPTION_KEY;
5
- constructor(ENCRYPTION_KEY: EncryptionKeyType);
6
- get(): Promise<CryptoKey>;
7
- }
8
- //# sourceMappingURL=crypto-key-provider-env.adapter.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"crypto-key-provider-env.adapter.d.ts","sourceRoot":"","sources":["../src/crypto-key-provider-env.adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACxE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAE7D,qBAAa,2BAA4B,YAAW,qBAAqB;IAC3D,OAAO,CAAC,QAAQ,CAAC,cAAc;gBAAd,cAAc,EAAE,iBAAiB;IAExD,GAAG,IAAI,OAAO,CAAC,SAAS,CAAC;CAShC"}
@@ -1,14 +0,0 @@
1
- export class CryptoKeyProviderEnvAdapter {
2
- ENCRYPTION_KEY;
3
- constructor(ENCRYPTION_KEY) {
4
- this.ENCRYPTION_KEY = ENCRYPTION_KEY;
5
- }
6
- async get() {
7
- const bytes = new Uint8Array(32);
8
- for (let i = 0; i < 32; i++) {
9
- bytes[i] = Number.parseInt(this.ENCRYPTION_KEY.slice(i * 2, i * 2 + 2), 16);
10
- }
11
- return crypto.subtle.importKey("raw", bytes, { name: "AES-GCM" }, false, ["encrypt", "decrypt"]);
12
- }
13
- }
14
- //# sourceMappingURL=crypto-key-provider-env.adapter.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"crypto-key-provider-env.adapter.js","sourceRoot":"","sources":["../src/crypto-key-provider-env.adapter.ts"],"names":[],"mappings":"AAGA,MAAM,OAAO,2BAA2B;IACT;IAA7B,YAA6B,cAAiC;QAAjC,mBAAc,GAAd,cAAc,CAAmB;IAAG,CAAC;IAElE,KAAK,CAAC,GAAG;QACP,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;QAEjC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;YAC5B,KAAK,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC9E,CAAC;QAED,OAAO,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,KAAK,EAAE,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC;IACnG,CAAC;CACF"}
@@ -1,16 +0,0 @@
1
- import type { z } from "zod/v4";
2
- import { type NodeEnvironmentEnum } from "../src/node-env.vo";
3
- import type { EnvironmentLoaderPort } from "./environment-loader.port";
4
- export declare class EnvironmentLoaderProcessEnvAdapter<Schema extends z.ZodObject<any>> implements EnvironmentLoaderPort<Schema> {
5
- private env;
6
- private readonly type;
7
- private readonly schema;
8
- constructor(config: {
9
- type: typeof process.env.NODE_ENV;
10
- schema: Schema;
11
- }, env: NodeJS.ProcessEnv);
12
- load(): Promise<Readonly<z.core.output<Schema> & {
13
- type: NodeEnvironmentEnum;
14
- }>>;
15
- }
16
- //# sourceMappingURL=environment-loader-process-env.adapter.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"environment-loader-process-env.adapter.d.ts","sourceRoot":"","sources":["../src/environment-loader-process-env.adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAmB,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC/E,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAEvE,qBAAa,kCAAkC,CAAC,MAAM,SAAS,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAC7E,YAAW,qBAAqB,CAAC,MAAM,CAAC;IAOtC,OAAO,CAAC,GAAG;IALb,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAsB;IAC3C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAG9B,MAAM,EAAE;QAAE,IAAI,EAAE,OAAO,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EACrD,GAAG,EAAE,MAAM,CAAC,UAAU;IAM1B,IAAI;;;CAGX"}
@@ -1,15 +0,0 @@
1
- import { NodeEnvironment } from "../src/node-env.vo";
2
- export class EnvironmentLoaderProcessEnvAdapter {
3
- env;
4
- type;
5
- schema;
6
- constructor(config, env) {
7
- this.env = env;
8
- this.schema = config.schema;
9
- this.type = NodeEnvironment.parse(config.type);
10
- }
11
- async load() {
12
- return Object.freeze({ ...this.schema.parse(this.env), type: this.type });
13
- }
14
- }
15
- //# sourceMappingURL=environment-loader-process-env.adapter.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"environment-loader-process-env.adapter.js","sourceRoot":"","sources":["../src/environment-loader-process-env.adapter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAA4B,MAAM,oBAAoB,CAAC;AAG/E,MAAM,OAAO,kCAAkC;IAQnC;IALO,IAAI,CAAsB;IAC1B,MAAM,CAAS;IAEhC,YACE,MAA6D,EACrD,GAAsB;QAAtB,QAAG,GAAH,GAAG,CAAmB;QAE9B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;QAC5B,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACjD,CAAC;IAED,KAAK,CAAC,IAAI;QACR,OAAO,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IAC5E,CAAC;CACF"}
@@ -1,16 +0,0 @@
1
- import type { CryptoKeyProviderPort } from "./crypto-key-provider.port";
2
- import type { EncryptionKeyType } from "./encryption-key.vo";
3
-
4
- export class CryptoKeyProviderEnvAdapter implements CryptoKeyProviderPort {
5
- constructor(private readonly ENCRYPTION_KEY: EncryptionKeyType) {}
6
-
7
- async get(): Promise<CryptoKey> {
8
- const bytes = new Uint8Array(32);
9
-
10
- for (let i = 0; i < 32; i++) {
11
- bytes[i] = Number.parseInt(this.ENCRYPTION_KEY.slice(i * 2, i * 2 + 2), 16);
12
- }
13
-
14
- return crypto.subtle.importKey("raw", bytes, { name: "AES-GCM" }, false, ["encrypt", "decrypt"]);
15
- }
16
- }
@@ -1,22 +0,0 @@
1
- import type { z } from "zod/v4";
2
- import { NodeEnvironment, type NodeEnvironmentEnum } from "../src/node-env.vo";
3
- import type { EnvironmentLoaderPort } from "./environment-loader.port";
4
-
5
- export class EnvironmentLoaderProcessEnvAdapter<Schema extends z.ZodObject<any>>
6
- implements EnvironmentLoaderPort<Schema>
7
- {
8
- private readonly type: NodeEnvironmentEnum;
9
- private readonly schema: Schema;
10
-
11
- constructor(
12
- config: { type: typeof process.env.NODE_ENV; schema: Schema },
13
- private env: NodeJS.ProcessEnv,
14
- ) {
15
- this.schema = config.schema;
16
- this.type = NodeEnvironment.parse(config.type);
17
- }
18
-
19
- async load() {
20
- return Object.freeze({ ...this.schema.parse(this.env), type: this.type });
21
- }
22
- }