@bgord/bun 1.5.4 → 1.5.8

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 (37) hide show
  1. package/dist/environment-loader-process-safe.adapter.d.ts +14 -0
  2. package/dist/environment-loader-process-safe.adapter.d.ts.map +1 -0
  3. package/dist/environment-loader-process-safe.adapter.js +19 -0
  4. package/dist/environment-loader-process-safe.adapter.js.map +1 -0
  5. package/dist/environment-loader-process.adapter.d.ts +4 -3
  6. package/dist/environment-loader-process.adapter.d.ts.map +1 -1
  7. package/dist/environment-loader-process.adapter.js +1 -8
  8. package/dist/environment-loader-process.adapter.js.map +1 -1
  9. package/dist/index.d.ts +1 -0
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +1 -0
  12. package/dist/index.js.map +1 -1
  13. package/dist/prerequisites/directory.d.ts +7 -5
  14. package/dist/prerequisites/directory.d.ts.map +1 -1
  15. package/dist/prerequisites/directory.js +34 -11
  16. package/dist/prerequisites/directory.js.map +1 -1
  17. package/dist/prerequisites/file.d.ts +21 -0
  18. package/dist/prerequisites/file.d.ts.map +1 -0
  19. package/dist/prerequisites/file.js +56 -0
  20. package/dist/prerequisites/file.js.map +1 -0
  21. package/dist/prerequisites/index.d.ts +1 -0
  22. package/dist/prerequisites/index.d.ts.map +1 -1
  23. package/dist/prerequisites/index.js +1 -0
  24. package/dist/prerequisites/index.js.map +1 -1
  25. package/dist/prerequisites/log-file.d.ts.map +1 -1
  26. package/dist/prerequisites/log-file.js +7 -4
  27. package/dist/prerequisites/log-file.js.map +1 -1
  28. package/dist/tsconfig.tsbuildinfo +1 -1
  29. package/package.json +3 -3
  30. package/readme.md +2 -0
  31. package/src/environment-loader-process-safe.adapter.ts +26 -0
  32. package/src/environment-loader-process.adapter.ts +2 -12
  33. package/src/index.ts +1 -0
  34. package/src/prerequisites/directory.ts +37 -13
  35. package/src/prerequisites/file.ts +69 -0
  36. package/src/prerequisites/index.ts +1 -0
  37. package/src/prerequisites/log-file.ts +7 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bgord/bun",
3
- "version": "1.5.4",
3
+ "version": "1.5.8",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "author": "Bartosz Gordon",
@@ -28,7 +28,7 @@
28
28
  "@types/nodemailer": "7.0.4",
29
29
  "@types/yazl": "3.3.0",
30
30
  "cspell": "9.4.0",
31
- "knip": "5.74.0",
31
+ "knip": "5.75.1",
32
32
  "lefthook": "2.0.12",
33
33
  "lockfile-lint": "4.14.1",
34
34
  "only-allow": "1.2.2",
@@ -39,7 +39,7 @@
39
39
  },
40
40
  "dependencies": {
41
41
  "@axiomhq/winston": "1.3.1",
42
- "@bgord/tools": "1.2.2",
42
+ "@bgord/tools": "1.2.3",
43
43
  "@hono/ua-blocker": "0.1.22",
44
44
  "better-auth": "1.4.7",
45
45
  "croner": "9.1.0",
package/readme.md CHANGED
@@ -74,6 +74,7 @@ src/
74
74
  ├── encryption-noop.adapter.ts
75
75
  ├── encryption.port.ts
76
76
  ├── environment-loader-encrypted.adapter.ts
77
+ ├── environment-loader-process-safe.adapter.ts
77
78
  ├── environment-loader-process.adapter.ts
78
79
  ├── environment-loader.port.ts
79
80
  ├── etag-extractor.middleware.ts
@@ -207,6 +208,7 @@ src/
207
208
  │   ├── directory.ts
208
209
  │   ├── dns.ts
209
210
  │   ├── external-api.ts
211
+ │   ├── file.ts
210
212
  │   ├── jobs.ts
211
213
  │   ├── log-file.ts
212
214
  │   ├── mailer.ts
@@ -0,0 +1,26 @@
1
+ import type { z } from "zod/v4";
2
+ import type { NodeEnvironmentEnum } from "../src/node-env.vo";
3
+ import type { EnvironmentLoaderPort, EnvironmentResultType } from "./environment-loader.port";
4
+
5
+ export class EnvironmentLoaderProcessSafeAdapter<Schema extends z.ZodObject<any>>
6
+ implements EnvironmentLoaderPort<Schema>
7
+ {
8
+ private cached: Readonly<EnvironmentResultType<Schema>> | null = null;
9
+
10
+ constructor(
11
+ private readonly config: { type: NodeEnvironmentEnum; Schema: Schema },
12
+ private env: NodeJS.ProcessEnv,
13
+ ) {}
14
+
15
+ async load() {
16
+ if (this.cached) return this.cached;
17
+
18
+ const result = this.config.Schema.parse(this.env);
19
+
20
+ for (const key of Object.keys(result)) delete process.env[key];
21
+
22
+ this.cached = Object.freeze({ ...result, type: this.config.type });
23
+
24
+ return this.cached;
25
+ }
26
+ }
@@ -1,26 +1,16 @@
1
1
  import type { z } from "zod/v4";
2
2
  import type { NodeEnvironmentEnum } from "../src/node-env.vo";
3
- import type { EnvironmentLoaderPort, EnvironmentResultType } from "./environment-loader.port";
3
+ import type { EnvironmentLoaderPort } from "./environment-loader.port";
4
4
 
5
5
  export class EnvironmentLoaderProcessAdapter<Schema extends z.ZodObject<any>>
6
6
  implements EnvironmentLoaderPort<Schema>
7
7
  {
8
- private cached: Readonly<EnvironmentResultType<Schema>> | null = null;
9
-
10
8
  constructor(
11
9
  private readonly config: { type: NodeEnvironmentEnum; Schema: Schema },
12
10
  private env: NodeJS.ProcessEnv,
13
11
  ) {}
14
12
 
15
13
  async load() {
16
- if (this.cached) return this.cached;
17
-
18
- const result = this.config.Schema.parse(this.env);
19
-
20
- for (const key of Object.keys(result)) delete process.env[key];
21
-
22
- this.cached = Object.freeze({ ...result, type: this.config.type });
23
-
24
- return this.cached;
14
+ return Object.freeze({ ...this.config.Schema.parse(this.env), type: this.config.type });
25
15
  }
26
16
  }
package/src/index.ts CHANGED
@@ -48,6 +48,7 @@ export * from "./encryption-noop.adapter";
48
48
  export * from "./environment-loader.port";
49
49
  export * from "./environment-loader-encrypted.adapter";
50
50
  export * from "./environment-loader-process.adapter";
51
+ export * from "./environment-loader-process-safe.adapter";
51
52
  export * from "./etag-extractor.middleware";
52
53
  export * from "./event.types";
53
54
  export * from "./event-bus-like.types";
@@ -1,28 +1,29 @@
1
- import { constants } from "node:fs";
2
- import fsp from "node:fs/promises";
1
+ import { access, constants, stat } from "node:fs/promises";
3
2
  import * as tools from "@bgord/tools";
4
3
  import type { ClockPort } from "../clock.port";
5
4
  import * as prereqs from "../prerequisites.service";
6
5
 
6
+ export type PrerequisiteDirectoryPermissionsType = { read?: boolean; write?: boolean; execute?: boolean };
7
+
7
8
  export class PrerequisiteDirectory implements prereqs.Prerequisite {
8
9
  readonly kind = "directory";
9
10
  readonly label: prereqs.PrerequisiteLabelType;
10
11
  readonly enabled?: boolean = true;
11
12
 
12
13
  private readonly directory: tools.DirectoryPathAbsoluteType | tools.DirectoryPathRelativeType;
13
- private readonly access?: { write?: boolean; execute?: boolean };
14
+ private readonly permissions: PrerequisiteDirectoryPermissionsType;
14
15
 
15
16
  constructor(
16
17
  config: prereqs.PrerequisiteConfigType & {
17
18
  directory: tools.DirectoryPathAbsoluteType | tools.DirectoryPathRelativeType;
18
- access?: { write?: boolean; execute?: boolean };
19
+ permissions?: PrerequisiteDirectoryPermissionsType;
19
20
  },
20
21
  ) {
21
22
  this.label = config.label;
22
23
  this.enabled = config.enabled === undefined ? true : config.enabled;
23
24
 
24
25
  this.directory = config.directory;
25
- this.access = config.access;
26
+ this.permissions = config.permissions ?? {};
26
27
  }
27
28
 
28
29
  async verify(clock: ClockPort): Promise<prereqs.VerifyOutcome> {
@@ -30,17 +31,40 @@ export class PrerequisiteDirectory implements prereqs.Prerequisite {
30
31
 
31
32
  if (!this.enabled) return prereqs.Verification.undetermined(stopwatch.stop());
32
33
 
33
- const write = this.access?.write ?? false;
34
- const execute = this.access?.execute ?? false;
34
+ try {
35
+ const stats = await stat(this.directory);
35
36
 
36
- const flags = constants.R_OK | (write ? constants.W_OK : 0) | (execute ? constants.X_OK : 0);
37
+ if (!stats.isDirectory()) {
38
+ return prereqs.Verification.failure(stopwatch.stop(), { message: "Not a directory" });
39
+ }
40
+ } catch {
41
+ return prereqs.Verification.failure(stopwatch.stop(), { message: "Directory does not exist" });
42
+ }
37
43
 
38
- try {
39
- await fsp.access(this.directory, flags);
44
+ if (this.permissions.read) {
45
+ try {
46
+ await access(this.directory, constants.R_OK);
47
+ } catch {
48
+ return prereqs.Verification.failure(stopwatch.stop(), { message: "Directory is not readable" });
49
+ }
50
+ }
51
+
52
+ if (this.permissions.write) {
53
+ try {
54
+ await access(this.directory, constants.W_OK);
55
+ } catch {
56
+ return prereqs.Verification.failure(stopwatch.stop(), { message: "Directory is not writable" });
57
+ }
58
+ }
40
59
 
41
- return prereqs.Verification.success(stopwatch.stop());
42
- } catch (error) {
43
- return prereqs.Verification.failure(stopwatch.stop(), error as Error);
60
+ if (this.permissions.execute) {
61
+ try {
62
+ await access(this.directory, constants.X_OK);
63
+ } catch {
64
+ return prereqs.Verification.failure(stopwatch.stop(), { message: "Directory is not executable" });
65
+ }
44
66
  }
67
+
68
+ return prereqs.Verification.success(stopwatch.stop());
45
69
  }
46
70
  }
@@ -0,0 +1,69 @@
1
+ import { access, constants } from "node:fs/promises";
2
+ import * as tools from "@bgord/tools";
3
+ import type { ClockPort } from "../clock.port";
4
+ import * as prereqs from "../prerequisites.service";
5
+
6
+ export type PrerequisiteFilePermissionsType = { read?: boolean; write?: boolean; execute?: boolean };
7
+
8
+ export class PrerequisiteFile implements prereqs.Prerequisite {
9
+ readonly kind = "file";
10
+ readonly label: prereqs.PrerequisiteLabelType;
11
+ readonly enabled?: boolean = true;
12
+
13
+ private readonly file: tools.FilePathAbsolute | tools.FilePathRelative;
14
+ private readonly permissions: PrerequisiteFilePermissionsType;
15
+
16
+ constructor(
17
+ config: prereqs.PrerequisiteConfigType & {
18
+ file: tools.FilePathAbsolute | tools.FilePathRelative;
19
+ permissions?: PrerequisiteFilePermissionsType;
20
+ },
21
+ ) {
22
+ this.label = config.label;
23
+ this.enabled = config.enabled === undefined ? true : config.enabled;
24
+
25
+ this.file = config.file;
26
+ this.permissions = config.permissions ?? {};
27
+ }
28
+
29
+ async verify(clock: ClockPort): Promise<prereqs.VerifyOutcome> {
30
+ const stopwatch = new tools.Stopwatch(clock.now());
31
+
32
+ if (!this.enabled) return prereqs.Verification.undetermined(stopwatch.stop());
33
+
34
+ try {
35
+ const path = this.file.get();
36
+
37
+ const exists = await Bun.file(path).exists();
38
+ if (!exists) return prereqs.Verification.failure(stopwatch.stop(), { message: "File does not exist" });
39
+
40
+ if (this.permissions.read) {
41
+ try {
42
+ await access(path, constants.R_OK);
43
+ } catch {
44
+ return prereqs.Verification.failure(stopwatch.stop(), { message: "File is not readable" });
45
+ }
46
+ }
47
+
48
+ if (this.permissions.write) {
49
+ try {
50
+ await access(path, constants.W_OK);
51
+ } catch {
52
+ return prereqs.Verification.failure(stopwatch.stop(), { message: "File is not writable" });
53
+ }
54
+ }
55
+
56
+ if (this.permissions.execute) {
57
+ try {
58
+ await access(path, constants.X_OK);
59
+ } catch {
60
+ return prereqs.Verification.failure(stopwatch.stop(), { message: "File is not executable" });
61
+ }
62
+ }
63
+
64
+ return prereqs.Verification.success(stopwatch.stop());
65
+ } catch (error) {
66
+ return prereqs.Verification.failure(stopwatch.stop(), error as Error);
67
+ }
68
+ }
69
+ }
@@ -5,6 +5,7 @@ export * from "./dependency-vulnerabilities";
5
5
  export * from "./directory";
6
6
  export * from "./dns";
7
7
  export * from "./external-api";
8
+ export * from "./file";
8
9
  export * from "./jobs";
9
10
  export * from "./log-file";
10
11
  export * from "./mailer";
@@ -2,6 +2,7 @@ import * as tools from "@bgord/tools";
2
2
  import type { ClockPort } from "../clock.port";
3
3
  import type { LoggerPort } from "../logger.port";
4
4
  import * as prereqs from "../prerequisites.service";
5
+ import { PrerequisiteFile } from "./file";
5
6
 
6
7
  type Dependencies = { Logger: LoggerPort };
7
8
 
@@ -27,10 +28,13 @@ export class PrerequisiteLogFile implements prereqs.Prerequisite {
27
28
  const path = this.deps.Logger.getFilePath();
28
29
  if (!path) return prereqs.Verification.undetermined(stopwatch.stop());
29
30
 
30
- const result = await Bun.file(path.get()).exists();
31
+ const file = new PrerequisiteFile({
32
+ label: this.label,
33
+ file: path,
34
+ permissions: { read: true, write: true },
35
+ });
31
36
 
32
- if (result) return prereqs.Verification.success(stopwatch.stop());
33
- return prereqs.Verification.failure(stopwatch.stop(), { message: `Missing file: ${path.get()}` });
37
+ return file.verify(clock);
34
38
  } catch (error) {
35
39
  return prereqs.Verification.failure(stopwatch.stop(), error as Error);
36
40
  }