@bgord/bun 1.4.21 → 1.4.24

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 (69) hide show
  1. package/dist/file-etag.vo.d.ts +8 -0
  2. package/dist/file-etag.vo.d.ts.map +1 -0
  3. package/dist/file-etag.vo.js +9 -0
  4. package/dist/file-etag.vo.js.map +1 -0
  5. package/dist/file-hash-noop.adapter.d.ts +1 -1
  6. package/dist/file-hash-noop.adapter.d.ts.map +1 -1
  7. package/dist/file-hash-noop.adapter.js +2 -1
  8. package/dist/file-hash-noop.adapter.js.map +1 -1
  9. package/dist/file-hash-sha256-bun.adapter.d.ts +1 -1
  10. package/dist/file-hash-sha256-bun.adapter.d.ts.map +1 -1
  11. package/dist/file-hash-sha256-bun.adapter.js +2 -2
  12. package/dist/file-hash-sha256-bun.adapter.js.map +1 -1
  13. package/dist/file-hash.port.d.ts +2 -1
  14. package/dist/file-hash.port.d.ts.map +1 -1
  15. package/dist/index.d.ts +2 -0
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +2 -0
  18. package/dist/index.js.map +1 -1
  19. package/dist/mailer.vo.d.ts +0 -10
  20. package/dist/mailer.vo.d.ts.map +1 -1
  21. package/dist/mailer.vo.js +0 -4
  22. package/dist/mailer.vo.js.map +1 -1
  23. package/dist/prerequisites/outside-connectivity.d.ts +1 -1
  24. package/dist/prerequisites/outside-connectivity.d.ts.map +1 -1
  25. package/dist/prerequisites/outside-connectivity.js +2 -2
  26. package/dist/prerequisites/outside-connectivity.js.map +1 -1
  27. package/dist/remote-file-storage-noop.adapter.d.ts +4 -4
  28. package/dist/remote-file-storage-noop.adapter.d.ts.map +1 -1
  29. package/dist/remote-file-storage-noop.adapter.js +2 -1
  30. package/dist/remote-file-storage-noop.adapter.js.map +1 -1
  31. package/dist/shield-captcha-recaptcha.adapter.d.ts +1 -0
  32. package/dist/shield-captcha-recaptcha.adapter.d.ts.map +1 -1
  33. package/dist/shield-captcha-recaptcha.adapter.js +3 -1
  34. package/dist/shield-captcha-recaptcha.adapter.js.map +1 -1
  35. package/dist/timekeeper-google.adapter.d.ts +1 -1
  36. package/dist/timekeeper-google.adapter.d.ts.map +1 -1
  37. package/dist/timekeeper-google.adapter.js +1 -1
  38. package/dist/timekeeper-google.adapter.js.map +1 -1
  39. package/dist/tsconfig.tsbuildinfo +1 -1
  40. package/dist/visitor-id-hash-hono.adapter.d.ts +1 -1
  41. package/dist/visitor-id-hash-hono.adapter.d.ts.map +1 -1
  42. package/dist/visitor-id-hash-hono.adapter.js +2 -1
  43. package/dist/visitor-id-hash-hono.adapter.js.map +1 -1
  44. package/dist/visitor-id-hash.adapter.d.ts +1 -1
  45. package/dist/visitor-id-hash.adapter.d.ts.map +1 -1
  46. package/dist/visitor-id-hash.adapter.js +3 -1
  47. package/dist/visitor-id-hash.adapter.js.map +1 -1
  48. package/dist/visitor-id.port.d.ts +1 -1
  49. package/dist/visitor-id.port.d.ts.map +1 -1
  50. package/dist/visitor-id.vo.d.ts +9 -0
  51. package/dist/visitor-id.vo.d.ts.map +1 -0
  52. package/dist/visitor-id.vo.js +12 -0
  53. package/dist/visitor-id.vo.js.map +1 -0
  54. package/package.json +2 -2
  55. package/readme.md +2 -0
  56. package/src/file-etag.vo.ts +13 -0
  57. package/src/file-hash-noop.adapter.ts +2 -1
  58. package/src/file-hash-sha256-bun.adapter.ts +2 -2
  59. package/src/file-hash.port.ts +2 -1
  60. package/src/index.ts +2 -0
  61. package/src/mailer.vo.ts +0 -8
  62. package/src/prerequisites/outside-connectivity.ts +2 -2
  63. package/src/remote-file-storage-noop.adapter.ts +4 -2
  64. package/src/shield-captcha-recaptcha.adapter.ts +6 -1
  65. package/src/timekeeper-google.adapter.ts +1 -1
  66. package/src/visitor-id-hash-hono.adapter.ts +2 -1
  67. package/src/visitor-id-hash.adapter.ts +4 -1
  68. package/src/visitor-id.port.ts +1 -1
  69. package/src/visitor-id.vo.ts +15 -0
@@ -3,6 +3,6 @@ import type { VisitorIdPort } from "./visitor-id.port";
3
3
  export declare class VisitorIdHashHonoAdapter implements VisitorIdPort {
4
4
  private readonly delegate;
5
5
  constructor(context: Context);
6
- get(): Promise<string>;
6
+ get(): Promise<string & import("zod").$brand<"VisitorId">>;
7
7
  }
8
8
  //# sourceMappingURL=visitor-id-hash-hono.adapter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"visitor-id-hash-hono.adapter.d.ts","sourceRoot":"","sources":["../src/visitor-id-hash-hono.adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAEpC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAGvD,qBAAa,wBAAyB,YAAW,aAAa;IAC5D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAuB;gBAEpC,OAAO,EAAE,OAAO;IAItB,GAAG;CAGV"}
1
+ {"version":3,"file":"visitor-id-hash-hono.adapter.d.ts","sourceRoot":"","sources":["../src/visitor-id-hash-hono.adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAEpC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAIvD,qBAAa,wBAAyB,YAAW,aAAa;IAC5D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAuB;gBAEpC,OAAO,EAAE,OAAO;IAItB,GAAG;CAGV"}
@@ -1,4 +1,5 @@
1
1
  import { ClientFromHonoAdapter } from "./client-from-hono.adapter";
2
+ import { VisitorId } from "./visitor-id.vo";
2
3
  import { VisitorIdHashAdapter } from "./visitor-id-hash.adapter";
3
4
  export class VisitorIdHashHonoAdapter {
4
5
  delegate;
@@ -6,7 +7,7 @@ export class VisitorIdHashHonoAdapter {
6
7
  this.delegate = new VisitorIdHashAdapter(ClientFromHonoAdapter.extract(context));
7
8
  }
8
9
  async get() {
9
- return this.delegate.get();
10
+ return VisitorId.parse(await this.delegate.get());
10
11
  }
11
12
  }
12
13
  //# sourceMappingURL=visitor-id-hash-hono.adapter.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"visitor-id-hash-hono.adapter.js","sourceRoot":"","sources":["../src/visitor-id-hash-hono.adapter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAEnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAEjE,MAAM,OAAO,wBAAwB;IAClB,QAAQ,CAAuB;IAEhD,YAAY,OAAgB;QAC1B,IAAI,CAAC,QAAQ,GAAG,IAAI,oBAAoB,CAAC,qBAAqB,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;IACnF,CAAC;IAED,KAAK,CAAC,GAAG;QACP,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC;IAC7B,CAAC;CACF"}
1
+ {"version":3,"file":"visitor-id-hash-hono.adapter.js","sourceRoot":"","sources":["../src/visitor-id-hash-hono.adapter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAEnE,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAEjE,MAAM,OAAO,wBAAwB;IAClB,QAAQ,CAAuB;IAEhD,YAAY,OAAgB;QAC1B,IAAI,CAAC,QAAQ,GAAG,IAAI,oBAAoB,CAAC,qBAAqB,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;IACnF,CAAC;IAED,KAAK,CAAC,GAAG;QACP,OAAO,SAAS,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC;IACpD,CAAC;CACF"}
@@ -3,6 +3,6 @@ import type { VisitorIdPort } from "./visitor-id.port";
3
3
  export declare class VisitorIdHashAdapter implements VisitorIdPort {
4
4
  private readonly client;
5
5
  constructor(client: Client);
6
- get(): Promise<string>;
6
+ get(): Promise<string & import("zod").$brand<"VisitorId">>;
7
7
  }
8
8
  //# sourceMappingURL=visitor-id-hash.adapter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"visitor-id-hash.adapter.d.ts","sourceRoot":"","sources":["../src/visitor-id-hash.adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEvD,qBAAa,oBAAqB,YAAW,aAAa;IAC5C,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,MAAM;IAErC,GAAG;CAWV"}
1
+ {"version":3,"file":"visitor-id-hash.adapter.d.ts","sourceRoot":"","sources":["../src/visitor-id-hash.adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAGvD,qBAAa,oBAAqB,YAAW,aAAa;IAC5C,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,MAAM;IAErC,GAAG;CAaV"}
@@ -1,3 +1,4 @@
1
+ import { VisitorId } from "./visitor-id.vo";
1
2
  export class VisitorIdHashAdapter {
2
3
  client;
3
4
  constructor(client) {
@@ -7,10 +8,11 @@ export class VisitorIdHashAdapter {
7
8
  const { ip, ua } = this.client.toJSON();
8
9
  const value = `${ip}|${ua}`;
9
10
  const buffer = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(value));
10
- return Array.from(new Uint8Array(buffer))
11
+ const result = Array.from(new Uint8Array(buffer))
11
12
  .slice(0, 8)
12
13
  .map((b) => b.toString(16).padStart(2, "0"))
13
14
  .join("");
15
+ return VisitorId.parse(result);
14
16
  }
15
17
  }
16
18
  //# sourceMappingURL=visitor-id-hash.adapter.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"visitor-id-hash.adapter.js","sourceRoot":"","sources":["../src/visitor-id-hash.adapter.ts"],"names":[],"mappings":"AAGA,MAAM,OAAO,oBAAoB;IACF;IAA7B,YAA6B,MAAc;QAAd,WAAM,GAAN,MAAM,CAAQ;IAAG,CAAC;IAE/C,KAAK,CAAC,GAAG;QACP,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QAExC,MAAM,KAAK,GAAG,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC;QAC5B,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QAEtF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;aACtC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;aACX,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;aAC3C,IAAI,CAAC,EAAE,CAAC,CAAC;IACd,CAAC;CACF"}
1
+ {"version":3,"file":"visitor-id-hash.adapter.js","sourceRoot":"","sources":["../src/visitor-id-hash.adapter.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,MAAM,OAAO,oBAAoB;IACF;IAA7B,YAA6B,MAAc;QAAd,WAAM,GAAN,MAAM,CAAQ;IAAG,CAAC;IAE/C,KAAK,CAAC,GAAG;QACP,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QAExC,MAAM,KAAK,GAAG,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC;QAC5B,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QAEtF,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;aAC9C,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;aACX,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;aAC3C,IAAI,CAAC,EAAE,CAAC,CAAC;QAEZ,OAAO,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACjC,CAAC;CACF"}
@@ -1,4 +1,4 @@
1
- export type VisitorIdType = string;
1
+ import type { VisitorIdType } from "./visitor-id.vo";
2
2
  export interface VisitorIdPort {
3
3
  get(): Promise<VisitorIdType>;
4
4
  }
@@ -1 +1 @@
1
- {"version":3,"file":"visitor-id.port.d.ts","sourceRoot":"","sources":["../src/visitor-id.port.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC;AAEnC,MAAM,WAAW,aAAa;IAC5B,GAAG,IAAI,OAAO,CAAC,aAAa,CAAC,CAAC;CAC/B"}
1
+ {"version":3,"file":"visitor-id.port.d.ts","sourceRoot":"","sources":["../src/visitor-id.port.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAErD,MAAM,WAAW,aAAa;IAC5B,GAAG,IAAI,OAAO,CAAC,aAAa,CAAC,CAAC;CAC/B"}
@@ -0,0 +1,9 @@
1
+ import { z } from "zod/v4";
2
+ export declare const VisitorIdError: {
3
+ readonly Type: "visitor.id.type";
4
+ readonly Empty: "visitor.id.empty";
5
+ readonly TooLong: "visitor.id.too.long";
6
+ };
7
+ export declare const VisitorId: z.core.$ZodBranded<z.ZodString, "VisitorId">;
8
+ export type VisitorIdType = z.infer<typeof VisitorId>;
9
+ //# sourceMappingURL=visitor-id.vo.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,12 @@
1
+ import { z } from "zod/v4";
2
+ export const VisitorIdError = {
3
+ Type: "visitor.id.type",
4
+ Empty: "visitor.id.empty",
5
+ TooLong: "visitor.id.too.long",
6
+ };
7
+ export const VisitorId = z
8
+ .string(VisitorIdError.Type)
9
+ .min(1, VisitorIdError.Empty)
10
+ .max(16, VisitorIdError.TooLong)
11
+ .brand("VisitorId");
12
+ //# sourceMappingURL=visitor-id.vo.js.map
@@ -0,0 +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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bgord/bun",
3
- "version": "1.4.21",
3
+ "version": "1.4.24",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "author": "Bartosz Gordon",
@@ -38,7 +38,7 @@
38
38
  },
39
39
  "dependencies": {
40
40
  "@axiomhq/winston": "1.3.1",
41
- "@bgord/tools": "1.1.20",
41
+ "@bgord/tools": "1.2.0",
42
42
  "@hono/ua-blocker": "0.1.21",
43
43
  "better-auth": "1.4.7",
44
44
  "croner": "9.1.0",
package/readme.md CHANGED
@@ -88,6 +88,7 @@ src/
88
88
  ├── file-cleaner.port.ts
89
89
  ├── file-draft-zip.service.ts
90
90
  ├── file-draft.service.ts
91
+ ├── file-etag.vo.ts
91
92
  ├── file-hash-noop.adapter.ts
92
93
  ├── file-hash-sha256-bun.adapter.ts
93
94
  ├── file-hash.port.ts
@@ -266,6 +267,7 @@ src/
266
267
  ├── visitor-id-hash-hono.adapter.ts
267
268
  ├── visitor-id-hash.adapter.ts
268
269
  ├── visitor-id.port.ts
270
+ ├── visitor-id.vo.ts
269
271
  └── weak-etag-extractor.middleware.ts
270
272
  ```
271
273
 
@@ -0,0 +1,13 @@
1
+ import { z } from "zod/v4";
2
+
3
+ export const FileEtagError = { Type: "file.etag.type", InvalidHex: "file.etag.invalid.hex" } as const;
4
+
5
+ // 64 hex chars allowed
6
+ const CHARS_WHITELIST = /^[a-fA-F0-9]{64}$/;
7
+
8
+ export const FileEtag = z
9
+ .string(FileEtagError.Type)
10
+ .regex(CHARS_WHITELIST, FileEtagError.InvalidHex)
11
+ .brand("FileEtag");
12
+
13
+ export type FileEtagType = z.infer<typeof FileEtag>;
@@ -1,10 +1,11 @@
1
1
  import * as tools from "@bgord/tools";
2
+ import { FileEtag } from "./file-etag.vo";
2
3
  import type { FileHashPort } from "./file-hash.port";
3
4
 
4
5
  export class FileHashNoopAdapter implements FileHashPort {
5
6
  async hash(_path: tools.FilePathAbsolute | tools.FilePathRelative) {
6
7
  return {
7
- etag: "noop",
8
+ etag: FileEtag.parse("0000000000000000000000000000000000000000000000000000000000000000"),
8
9
  size: tools.Size.fromBytes(10),
9
10
  lastModified: tools.Timestamp.fromNumber(1000),
10
11
  mime: tools.MIMES.text,
@@ -1,4 +1,5 @@
1
1
  import * as tools from "@bgord/tools";
2
+ import { FileEtag } from "./file-etag.vo";
2
3
  import type { FileHashPort } from "./file-hash.port";
3
4
 
4
5
  export class FileHashSha256BunAdapter implements FileHashPort {
@@ -8,10 +9,9 @@ export class FileHashSha256BunAdapter implements FileHashPort {
8
9
 
9
10
  const arrayBuffer = await file.arrayBuffer();
10
11
  const digest = await crypto.subtle.digest("SHA-256", arrayBuffer);
11
- const etag = Buffer.from(digest).toString("hex");
12
12
 
13
13
  return {
14
- etag,
14
+ etag: FileEtag.parse(Buffer.from(digest).toString("hex")),
15
15
  size: tools.Size.fromBytes(arrayBuffer.byteLength),
16
16
  lastModified: tools.Timestamp.fromNumber(file.lastModified),
17
17
  mime: tools.Mime.fromExtension(extension),
@@ -1,7 +1,8 @@
1
1
  import type * as tools from "@bgord/tools";
2
+ import type { FileEtagType } from "./file-etag.vo";
2
3
 
3
4
  export type FileHashResult = {
4
- etag: string;
5
+ etag: FileEtagType;
5
6
  size: tools.Size;
6
7
  lastModified: tools.Timestamp;
7
8
  mime: tools.Mime;
package/src/index.ts CHANGED
@@ -59,6 +59,7 @@ export * from "./file-cleaner-bun-forgiving.adapter";
59
59
  export * from "./file-cleaner-noop.adapter";
60
60
  export * from "./file-draft.service";
61
61
  export * from "./file-draft-zip.service";
62
+ export * from "./file-etag.vo";
62
63
  export * from "./file-hash.port";
63
64
  export * from "./file-hash-noop.adapter";
64
65
  export * from "./file-hash-sha256-bun.adapter";
@@ -185,6 +186,7 @@ export * from "./translations.service";
185
186
  export * from "./uptime.service";
186
187
  export * from "./uuid.vo";
187
188
  export * from "./visitor-id.port";
189
+ export * from "./visitor-id.vo";
188
190
  export * from "./visitor-id-hash.adapter";
189
191
  export * from "./visitor-id-hash-hono.adapter";
190
192
  export * from "./weak-etag-extractor.middleware";
package/src/mailer.vo.ts CHANGED
@@ -27,12 +27,4 @@ export const EmailContentHtml = z
27
27
  .max(10_000, EmailContentHtmlError.Invalid);
28
28
  export type EmailContentHtmlType = z.infer<typeof EmailContentHtml>;
29
29
 
30
- export const EmailFromError = { Invalid: "email.from.invalid" } as const;
31
- export const EmailFrom = z.email(EmailFromError.Invalid);
32
- export type EmailFromType = z.infer<typeof EmailFrom>;
33
-
34
- export const EmailToError = { Invalid: "email.to.invalid" } as const;
35
- export const EmailTo = z.email(EmailToError.Invalid);
36
- export type EmailToType = z.infer<typeof EmailTo>;
37
-
38
30
  export type EmailAttachmentType = { filename: string; path: string };
@@ -8,7 +8,7 @@ export class PrerequisiteOutsideConnectivity implements prereqs.Prerequisite {
8
8
  readonly label: prereqs.PrerequisiteLabelType;
9
9
  readonly enabled?: boolean = true;
10
10
 
11
- private readonly url = "https://google.com";
11
+ private static readonly URL = tools.UrlWithoutSlash.parse("https://google.com");
12
12
  readonly timeout: tools.Duration;
13
13
 
14
14
  constructor(config: prereqs.PrerequisiteConfigType & { timeout?: tools.Duration }) {
@@ -25,7 +25,7 @@ export class PrerequisiteOutsideConnectivity implements prereqs.Prerequisite {
25
25
  if (!this.enabled) return prereqs.Verification.undetermined(stopwatch.stop());
26
26
 
27
27
  const response = await Timeout.cancellable(
28
- (signal: AbortSignal) => fetch(this.url, { method: "HEAD", signal }),
28
+ (signal: AbortSignal) => fetch(PrerequisiteOutsideConnectivity.URL, { method: "HEAD", signal }),
29
29
  this.timeout,
30
30
  );
31
31
 
@@ -1,5 +1,6 @@
1
1
  import * as tools from "@bgord/tools";
2
2
  import type { ClockPort } from "./clock.port";
3
+ import { FileEtag } from "./file-etag.vo";
3
4
  import type { LoggerPort } from "./logger.port";
4
5
  import type {
5
6
  RemoteFileStoragePort,
@@ -8,9 +9,10 @@ import type {
8
9
  RemotePutFromPathResult,
9
10
  } from "./remote-file-storage.port";
10
11
 
11
- type RemoteFileStorageNoopConfig = { root: tools.DirectoryPathAbsoluteType; publicBaseUrl?: string };
12
12
  type Dependencies = { Logger: LoggerPort; Clock: ClockPort };
13
13
 
14
+ type RemoteFileStorageNoopConfig = { root: tools.DirectoryPathAbsoluteType; publicBaseUrl?: string };
15
+
14
16
  export class RemoteFileStorageNoopAdapter implements RemoteFileStoragePort {
15
17
  private readonly base = { component: "infra", operation: "RemoteFileStorageNoopAdapter" };
16
18
 
@@ -32,7 +34,7 @@ export class RemoteFileStorageNoopAdapter implements RemoteFileStoragePort {
32
34
  });
33
35
 
34
36
  return {
35
- etag: "noop",
37
+ etag: FileEtag.parse("0000000000000000000000000000000000000000000000000000000000000000"),
36
38
  size: tools.Size.fromBytes(10),
37
39
  lastModified: this.deps.Clock.now(),
38
40
  mime: tools.MIMES.text,
@@ -1,3 +1,4 @@
1
+ import * as tools from "@bgord/tools";
1
2
  import { createMiddleware } from "hono/factory";
2
3
  import { HTTPException } from "hono/http-exception";
3
4
  import type { RecaptchaSecretKeyType } from "./recaptcha-secret-key.vo";
@@ -9,6 +10,10 @@ export type RecaptchaResultType = { success: boolean; score: number };
9
10
  export const AccessDeniedRecaptchaError = new HTTPException(403, { message: "access_denied_recaptcha" });
10
11
 
11
12
  export class ShieldCaptchaRecaptchaAdapter implements ShieldPort {
13
+ private static readonly URL = tools.UrlWithoutSlash.parse(
14
+ "https://www.google.com/recaptcha/api/siteverify",
15
+ );
16
+
12
17
  constructor(private readonly config: RecaptchaVerifierConfigType) {}
13
18
 
14
19
  verify = createMiddleware(async (c, next) => {
@@ -26,7 +31,7 @@ export class ShieldCaptchaRecaptchaAdapter implements ShieldPort {
26
31
 
27
32
  const params = new URLSearchParams({ secret: this.config.secretKey, response: token, remoteip });
28
33
 
29
- const response = await fetch("https://www.google.com/recaptcha/api/siteverify", {
34
+ const response = await fetch(ShieldCaptchaRecaptchaAdapter.URL, {
30
35
  method: "POST",
31
36
  headers: { "Content-Type": "application/x-www-form-urlencoded" },
32
37
  body: params,
@@ -2,7 +2,7 @@ import * as tools from "@bgord/tools";
2
2
  import type { TimekeeperPort } from "./timekeeper.port";
3
3
 
4
4
  export class TimekeeperGoogleAdapter implements TimekeeperPort {
5
- static URL = "https://www.google.com/generate_204";
5
+ static URL = tools.UrlWithoutSlash.parse("https://www.google.com/generate_204");
6
6
 
7
7
  async get(signal?: AbortSignal) {
8
8
  try {
@@ -1,6 +1,7 @@
1
1
  import type { Context } from "hono";
2
2
  import { ClientFromHonoAdapter } from "./client-from-hono.adapter";
3
3
  import type { VisitorIdPort } from "./visitor-id.port";
4
+ import { VisitorId } from "./visitor-id.vo";
4
5
  import { VisitorIdHashAdapter } from "./visitor-id-hash.adapter";
5
6
 
6
7
  export class VisitorIdHashHonoAdapter implements VisitorIdPort {
@@ -11,6 +12,6 @@ export class VisitorIdHashHonoAdapter implements VisitorIdPort {
11
12
  }
12
13
 
13
14
  async get() {
14
- return this.delegate.get();
15
+ return VisitorId.parse(await this.delegate.get());
15
16
  }
16
17
  }
@@ -1,5 +1,6 @@
1
1
  import type { Client } from "./client.vo";
2
2
  import type { VisitorIdPort } from "./visitor-id.port";
3
+ import { VisitorId } from "./visitor-id.vo";
3
4
 
4
5
  export class VisitorIdHashAdapter implements VisitorIdPort {
5
6
  constructor(private readonly client: Client) {}
@@ -10,9 +11,11 @@ export class VisitorIdHashAdapter implements VisitorIdPort {
10
11
  const value = `${ip}|${ua}`;
11
12
  const buffer = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(value));
12
13
 
13
- return Array.from(new Uint8Array(buffer))
14
+ const result = Array.from(new Uint8Array(buffer))
14
15
  .slice(0, 8)
15
16
  .map((b) => b.toString(16).padStart(2, "0"))
16
17
  .join("");
18
+
19
+ return VisitorId.parse(result);
17
20
  }
18
21
  }
@@ -1,4 +1,4 @@
1
- export type VisitorIdType = string;
1
+ import type { VisitorIdType } from "./visitor-id.vo";
2
2
 
3
3
  export interface VisitorIdPort {
4
4
  get(): Promise<VisitorIdType>;
@@ -0,0 +1,15 @@
1
+ import { z } from "zod/v4";
2
+
3
+ export const VisitorIdError = {
4
+ Type: "visitor.id.type",
5
+ Empty: "visitor.id.empty",
6
+ TooLong: "visitor.id.too.long",
7
+ } as const;
8
+
9
+ export const VisitorId = z
10
+ .string(VisitorIdError.Type)
11
+ .min(1, VisitorIdError.Empty)
12
+ .max(16, VisitorIdError.TooLong)
13
+ .brand("VisitorId");
14
+
15
+ export type VisitorIdType = z.infer<typeof VisitorId>;