@bgord/bun 1.8.0 → 1.8.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bgord/bun",
3
- "version": "1.8.0",
3
+ "version": "1.8.2",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "author": "Bartosz Gordon",
@@ -29,7 +29,7 @@
29
29
  "@types/nodemailer": "7.0.4",
30
30
  "@types/yazl": "3.3.0",
31
31
  "cspell": "9.4.0",
32
- "knip": "5.79.0",
32
+ "knip": "5.80.0",
33
33
  "lefthook": "2.0.13",
34
34
  "lockfile-lint": "4.14.1",
35
35
  "lru-cache": "11.2.4",
@@ -1,21 +1,22 @@
1
- import type { Invariant } from "./invariant.service";
1
+ import type { ContentfulStatusCode } from "hono/utils/http-status";
2
+ import { type Invariant, InvariantFailureKind } from "./invariant.service";
2
3
 
3
4
  type BaseInvariantType = Invariant<any>;
4
5
  type InvariantMessageType = BaseInvariantType["message"];
5
- type InvariantCodeType = BaseInvariantType["code"];
6
- type ErrorResponseTupleType = [{ message: InvariantMessageType; _known: true }, InvariantCodeType];
6
+ type ErrorResponseTupleType = [{ message: InvariantMessageType; _known: true }, ContentfulStatusCode];
7
7
 
8
8
  export class InvariantErrorHandler {
9
- error: BaseInvariantType | undefined = undefined;
10
-
11
- constructor(private readonly invariants: BaseInvariantType[]) {}
12
-
13
- detect(error: unknown) {
14
- this.error = this.invariants.find((invariant) => error instanceof invariant.error);
15
- return this;
9
+ static detect(invariants: BaseInvariantType[], error: unknown): BaseInvariantType | undefined {
10
+ return invariants.find((invariant) => error instanceof invariant.error);
16
11
  }
17
12
 
18
13
  static respond(error: BaseInvariantType): ErrorResponseTupleType {
19
- return [{ message: error.message, _known: true }, error.code];
14
+ const code: Record<InvariantFailureKind, ContentfulStatusCode> = {
15
+ [InvariantFailureKind.forbidden]: 403,
16
+ [InvariantFailureKind.not_found]: 404,
17
+ [InvariantFailureKind.precondition]: 400,
18
+ };
19
+
20
+ return [{ message: error.message, _known: true }, code[error.kind]];
20
21
  }
21
22
  }
@@ -1,26 +1,27 @@
1
1
  import type { Constructor } from "@bgord/tools";
2
- import type { ContentfulStatusCode } from "hono/utils/http-status";
3
2
 
4
3
  type BaseInvariantConfig = Record<string, unknown>;
5
4
 
5
+ export enum InvariantFailureKind {
6
+ forbidden = "forbidden",
7
+ precondition = "precondition",
8
+ not_found = "not_found",
9
+ }
10
+
6
11
  export abstract class Invariant<T extends BaseInvariantConfig> {
7
- abstract fails(config: T): boolean;
12
+ abstract passes(config: T): boolean;
8
13
 
9
14
  abstract error: Constructor<Error>;
10
15
 
11
16
  abstract message: string;
12
17
 
13
- abstract code: ContentfulStatusCode;
18
+ abstract kind: InvariantFailureKind;
14
19
 
15
20
  throw() {
16
21
  throw new this.error();
17
22
  }
18
23
 
19
- perform(config: T) {
20
- if (this.fails(config)) this.throw();
21
- }
22
-
23
- passes(config: T) {
24
- return !this.fails(config);
24
+ enforce(config: T) {
25
+ if (!this.passes(config)) this.throw();
25
26
  }
26
27
  }
@@ -24,7 +24,7 @@ export const handleSetUserLanguageCommand =
24
24
  const candidate = supported.ensure(command.payload.language);
25
25
  const current = await deps.UserLanguageQuery.get(command.payload.userId);
26
26
 
27
- if (Invariants.UserLanguageHasChanged.fails({ current, candidate: command.payload.language })) return;
27
+ if (!Invariants.UserLanguageHasChanged.passes({ current, candidate: command.payload.language })) return;
28
28
 
29
29
  const event = Events.UserLanguageSetEvent.parse({
30
30
  ...createEventEnvelope(`preferences_${command.payload.userId}`, deps),
@@ -1,6 +1,5 @@
1
1
  import type * as tools from "@bgord/tools";
2
- import type { ContentfulStatusCode } from "hono/utils/http-status";
3
- import { Invariant } from "../../../invariant.service";
2
+ import { Invariant, InvariantFailureKind } from "../../../invariant.service";
4
3
 
5
4
  class UserLanguageHasChangedError extends Error {
6
5
  constructor() {
@@ -15,13 +14,13 @@ type UserLanguageHasChangedConfigType = {
15
14
  };
16
15
 
17
16
  class UserLanguageHasChangedFactory extends Invariant<UserLanguageHasChangedConfigType> {
18
- fails(config: UserLanguageHasChangedConfigType) {
19
- return config.current === config.candidate;
17
+ passes(config: UserLanguageHasChangedConfigType) {
18
+ return config.current !== config.candidate;
20
19
  }
21
20
 
22
- message = "UserLanguageHasChanged";
21
+ message = "user.language.has.changed";
22
+ kind = InvariantFailureKind.precondition;
23
23
  error = UserLanguageHasChangedError;
24
- code = 403 as ContentfulStatusCode;
25
24
  }
26
25
 
27
26
  export const UserLanguageHasChanged = new UserLanguageHasChangedFactory();
@@ -19,13 +19,11 @@ import { MaintenanceMode, type MaintenanceModeConfigType } from "./maintenance-m
19
19
  import { TimeZoneOffset } from "./time-zone-offset.middleware";
20
20
  import { WeakETagExtractor } from "./weak-etag-extractor.middleware";
21
21
 
22
- export const BODY_LIMIT_MAX_SIZE = tools.Size.fromKb(128).toBytes();
23
-
24
22
  type SetupOverridesType = {
25
23
  cors?: Parameters<typeof cors>[0];
26
- secureHeaders?: Parameters<typeof secureHeaders>[0];
27
24
  httpLogger?: HttpLoggerOptions;
28
25
  maintenanceMode?: MaintenanceModeConfigType;
26
+ BODY_LIMIT_MAX_SIZE?: tools.Size;
29
27
  };
30
28
 
31
29
  type Dependencies = {
@@ -38,32 +36,33 @@ type Dependencies = {
38
36
 
39
37
  export class Setup {
40
38
  static essentials(deps: Dependencies, overrides?: SetupOverridesType) {
41
- // Stryker disable all
42
- const corsOptions = overrides?.cors ?? { origin: "*" };
43
- // Stryker restore all
44
- const secureHeadersOptions = { crossOriginResourcePolicy: "cross-origin", ...overrides?.secureHeaders };
39
+ const BODY_LIMIT_MAX_SIZE = overrides?.BODY_LIMIT_MAX_SIZE ?? tools.Size.fromKb(128);
45
40
 
46
41
  return [
47
42
  MaintenanceMode.build(overrides?.maintenanceMode),
48
- secureHeaders(secureHeadersOptions),
49
- // Stryker disable all
50
- bodyLimit({ maxSize: BODY_LIMIT_MAX_SIZE }),
51
- // Stryker restore all
43
+ secureHeaders({
44
+ crossOriginResourcePolicy: "same-origin",
45
+ contentSecurityPolicy: {
46
+ defaultSrc: ["'none'"],
47
+ scriptSrc: ["'self'"],
48
+ styleSrc: ["'self'"],
49
+ imgSrc: ["'self'"],
50
+ },
51
+ }),
52
+ bodyLimit({ maxSize: BODY_LIMIT_MAX_SIZE.toBytes() }),
52
53
  ApiVersion.build({ Clock: deps.Clock, FileReaderJson: deps.FileReaderJson }),
53
- cors(corsOptions),
54
- // Stryker disable all
54
+ cors(overrides?.cors),
55
55
  languageDetector({
56
56
  supportedLanguages: Object.keys(deps.I18n.supportedLanguages),
57
57
  fallbackLanguage: deps.I18n.defaultLanguage,
58
+ // Stryker disable all
58
59
  caches: false,
60
+ // Stryker restore all
59
61
  }),
60
- // Stryker restore all
61
62
  requestId({
62
63
  limitLength: 36,
63
64
  headerName: "x-correlation-id",
64
- // Stryker disable all
65
65
  generator: () => deps.IdProvider.generate(),
66
- // Stryker restore all
67
66
  }),
68
67
  TimeZoneOffset.attach,
69
68
  Context.attach,