@bgord/bun 1.1.6 → 1.2.0
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/dist/checksum.service.d.ts +9 -0
- package/dist/checksum.service.d.ts.map +1 -0
- package/dist/checksum.service.js +20 -0
- package/dist/checksum.service.js.map +1 -0
- package/dist/correlation-storage.service.d.ts +1 -1
- package/dist/correlation-storage.service.d.ts.map +1 -1
- package/dist/correlation-storage.service.js +2 -2
- package/dist/correlation-storage.service.js.map +1 -1
- package/dist/healthcheck.service.d.ts +4 -16
- package/dist/healthcheck.service.d.ts.map +1 -1
- package/dist/healthcheck.service.js +2 -1
- package/dist/healthcheck.service.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/prerequisites/clock-drift.d.ts +2 -2
- package/dist/prerequisites/clock-drift.d.ts.map +1 -1
- package/dist/prerequisites/clock-drift.js +14 -9
- package/dist/prerequisites/clock-drift.js.map +1 -1
- package/dist/prerequisites/mailer.d.ts +3 -0
- package/dist/prerequisites/mailer.d.ts.map +1 -1
- package/dist/prerequisites/mailer.js +4 -1
- package/dist/prerequisites/mailer.js.map +1 -1
- package/dist/prerequisites/outside-connectivity.d.ts +5 -1
- package/dist/prerequisites/outside-connectivity.d.ts.map +1 -1
- package/dist/prerequisites/outside-connectivity.js +4 -1
- package/dist/prerequisites/outside-connectivity.js.map +1 -1
- package/dist/prerequisites.service.d.ts +3 -3
- package/dist/prerequisites.service.d.ts.map +1 -1
- package/dist/prerequisites.service.js +3 -3
- package/dist/prerequisites.service.js.map +1 -1
- package/dist/timekeeper-google.adapter.d.ts +2 -1
- package/dist/timekeeper-google.adapter.d.ts.map +1 -1
- package/dist/timekeeper-google.adapter.js +3 -2
- package/dist/timekeeper-google.adapter.js.map +1 -1
- package/dist/timekeeper.port.d.ts +1 -1
- package/dist/timekeeper.port.d.ts.map +1 -1
- package/dist/timeout.service.d.ts +11 -0
- package/dist/timeout.service.d.ts.map +1 -0
- package/dist/timeout.service.js +46 -0
- package/dist/timeout.service.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/readme.md +2 -0
- package/src/checksum.service.ts +23 -0
- package/src/correlation-storage.service.ts +2 -2
- package/src/healthcheck.service.ts +4 -2
- package/src/index.ts +2 -0
- package/src/prerequisites/clock-drift.ts +16 -10
- package/src/prerequisites/mailer.ts +5 -2
- package/src/prerequisites/outside-connectivity.ts +9 -2
- package/src/prerequisites.service.ts +6 -6
- package/src/timekeeper-google.adapter.ts +4 -2
- package/src/timekeeper.port.ts +1 -1
- package/src/timeout.service.ts +71 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bgord/bun",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
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.
|
|
41
|
+
"@bgord/tools": "1.1.6",
|
|
42
42
|
"@hono/ua-blocker": "0.1.15",
|
|
43
43
|
"better-auth": "1.3.34",
|
|
44
44
|
"croner": "9.1.0",
|
package/readme.md
CHANGED
|
@@ -41,6 +41,7 @@ src/
|
|
|
41
41
|
├── certificate-inspector-noop.adapter.ts
|
|
42
42
|
├── certificate-inspector-tls.adapter.ts
|
|
43
43
|
├── certificate-inspector.port.ts
|
|
44
|
+
├── checksum.service.ts
|
|
44
45
|
├── client-from-hono.adapter.ts
|
|
45
46
|
├── client.vo.ts
|
|
46
47
|
├── clock-fixed.adapter.ts
|
|
@@ -232,6 +233,7 @@ src/
|
|
|
232
233
|
├── timekeeper-google.adapter.ts
|
|
233
234
|
├── timekeeper-noop.adapter.ts
|
|
234
235
|
├── timekeeper.port.ts
|
|
236
|
+
├── timeout.service.ts
|
|
235
237
|
├── to-event-map.types.ts
|
|
236
238
|
├── translations.service.ts
|
|
237
239
|
├── uptime.service.ts
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { FileHashResult } from "./file-hash.port";
|
|
2
|
+
|
|
3
|
+
export enum ChecksumStrategy {
|
|
4
|
+
etag = "etag",
|
|
5
|
+
complex = "complex",
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export class Checksum {
|
|
9
|
+
static compare(first: FileHashResult, second: FileHashResult, strategy: ChecksumStrategy): boolean {
|
|
10
|
+
switch (strategy) {
|
|
11
|
+
case ChecksumStrategy.etag:
|
|
12
|
+
return first.etag === second.etag;
|
|
13
|
+
case ChecksumStrategy.complex:
|
|
14
|
+
return (
|
|
15
|
+
first.etag === second.etag &&
|
|
16
|
+
first.size === second.size &&
|
|
17
|
+
first.lastModified &&
|
|
18
|
+
second.lastModified &&
|
|
19
|
+
first.mime.isSatisfiedBy(second.mime)
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -15,8 +15,8 @@ export class CorrelationStorage {
|
|
|
15
15
|
] ??=
|
|
16
16
|
new AsyncLocalStorage<CorrelationContext>());
|
|
17
17
|
|
|
18
|
-
static run<T>(correlationId: CorrelationIdType,
|
|
19
|
-
return CorrelationStorage.als.run({ correlationId },
|
|
18
|
+
static run<T>(correlationId: CorrelationIdType, action: () => T | Promise<T>): T | Promise<T> {
|
|
19
|
+
return CorrelationStorage.als.run({ correlationId }, action);
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
static get(): CorrelationIdType {
|
|
@@ -14,7 +14,7 @@ type HealthcheckResultType = {
|
|
|
14
14
|
ok: prereqs.PrerequisiteStatusEnum;
|
|
15
15
|
version: string;
|
|
16
16
|
details: { label: prereqs.PrerequisiteLabelType; outcome: prereqs.VerifyOutcome }[];
|
|
17
|
-
uptime: UptimeResultType;
|
|
17
|
+
uptime: Omit<UptimeResultType, "duration"> & { durationMs: tools.DurationMsType };
|
|
18
18
|
memory: { bytes: tools.Size["bytes"]; formatted: ReturnType<tools.Size["format"]> };
|
|
19
19
|
durationMs: tools.Duration["ms"];
|
|
20
20
|
};
|
|
@@ -40,11 +40,13 @@ export class Healthcheck {
|
|
|
40
40
|
|
|
41
41
|
const code = ok === prereqs.PrerequisiteStatusEnum.success ? 200 : 424;
|
|
42
42
|
|
|
43
|
+
const uptime = Uptime.get(deps.Clock);
|
|
44
|
+
|
|
43
45
|
const result: HealthcheckResultType = {
|
|
44
46
|
ok,
|
|
45
47
|
details,
|
|
46
48
|
version: buildInfo.BUILD_VERSION ?? "unknown",
|
|
47
|
-
uptime:
|
|
49
|
+
uptime: { durationMs: uptime.duration.ms, formatted: uptime.formatted },
|
|
48
50
|
memory: {
|
|
49
51
|
bytes: MemoryConsumption.get().toBytes(),
|
|
50
52
|
formatted: MemoryConsumption.get().format(tools.Size.unit.MB),
|
package/src/index.ts
CHANGED
|
@@ -14,6 +14,7 @@ export * from "./cache-response.middleware";
|
|
|
14
14
|
export * from "./certificate-inspector.port";
|
|
15
15
|
export * from "./certificate-inspector-noop.adapter";
|
|
16
16
|
export * from "./certificate-inspector-tls.adapter";
|
|
17
|
+
export * from "./checksum.service";
|
|
17
18
|
export * from "./client.vo";
|
|
18
19
|
export * from "./client-from-hono.adapter";
|
|
19
20
|
export * from "./clock.port";
|
|
@@ -155,6 +156,7 @@ export * from "./time-zone-offset.middleware";
|
|
|
155
156
|
export * from "./timekeeper.port";
|
|
156
157
|
export * from "./timekeeper-google.adapter";
|
|
157
158
|
export * from "./timekeeper-noop.adapter";
|
|
159
|
+
export * from "./timeout.service";
|
|
158
160
|
export * from "./to-event-map.types";
|
|
159
161
|
export * from "./translations.service";
|
|
160
162
|
export * from "./uptime.service";
|
|
@@ -2,6 +2,7 @@ import * as tools from "@bgord/tools";
|
|
|
2
2
|
import type { ClockPort } from "../clock.port";
|
|
3
3
|
import * as prereqs from "../prerequisites.service";
|
|
4
4
|
import type { TimekeeperPort } from "../timekeeper.port";
|
|
5
|
+
import { Timeout } from "../timeout.service";
|
|
5
6
|
|
|
6
7
|
export class PrerequisiteClockDrift implements prereqs.Prerequisite {
|
|
7
8
|
readonly kind = "clock-drift";
|
|
@@ -10,13 +11,13 @@ export class PrerequisiteClockDrift implements prereqs.Prerequisite {
|
|
|
10
11
|
|
|
11
12
|
readonly skew: tools.Duration;
|
|
12
13
|
readonly timekeeper: TimekeeperPort;
|
|
13
|
-
readonly
|
|
14
|
+
readonly timeout: tools.Duration;
|
|
14
15
|
|
|
15
16
|
constructor(
|
|
16
17
|
config: prereqs.PrerequisiteConfigType & {
|
|
17
18
|
skew: tools.Duration;
|
|
18
19
|
timekeeper: TimekeeperPort;
|
|
19
|
-
|
|
20
|
+
timeout?: tools.Duration;
|
|
20
21
|
},
|
|
21
22
|
) {
|
|
22
23
|
this.label = config.label;
|
|
@@ -24,7 +25,7 @@ export class PrerequisiteClockDrift implements prereqs.Prerequisite {
|
|
|
24
25
|
|
|
25
26
|
this.skew = config.skew;
|
|
26
27
|
this.timekeeper = config.timekeeper;
|
|
27
|
-
this.
|
|
28
|
+
this.timeout = config.timeout ?? tools.Duration.Seconds(2);
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
async verify(clock: ClockPort): Promise<prereqs.VerifyOutcome> {
|
|
@@ -32,14 +33,19 @@ export class PrerequisiteClockDrift implements prereqs.Prerequisite {
|
|
|
32
33
|
|
|
33
34
|
if (!this.enabled) return prereqs.Verification.undetermined(stopwatch.stop());
|
|
34
35
|
|
|
35
|
-
|
|
36
|
+
try {
|
|
37
|
+
const timestamp = await Timeout.cancellable(
|
|
38
|
+
(signal: AbortSignal) => this.timekeeper.get(signal),
|
|
39
|
+
this.timeout,
|
|
40
|
+
);
|
|
41
|
+
if (!timestamp) return prereqs.Verification.undetermined(stopwatch.stop());
|
|
36
42
|
|
|
37
|
-
|
|
38
|
-
if (!timestamp) return prereqs.Verification.undetermined(stopwatch.stop());
|
|
43
|
+
const duration = clock.now().difference(timestamp).toAbsolute();
|
|
39
44
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
45
|
+
if (duration.isShorterThan(this.skew)) return prereqs.Verification.success(stopwatch.stop());
|
|
46
|
+
return prereqs.Verification.failure(stopwatch.stop(), { message: `Difference: ${duration.seconds}s` });
|
|
47
|
+
} catch (error) {
|
|
48
|
+
return prereqs.Verification.undetermined(stopwatch.stop());
|
|
49
|
+
}
|
|
44
50
|
}
|
|
45
51
|
}
|
|
@@ -2,6 +2,7 @@ import * as tools from "@bgord/tools";
|
|
|
2
2
|
import type { ClockPort } from "../clock.port";
|
|
3
3
|
import type { MailerPort } from "../mailer.port";
|
|
4
4
|
import * as prereqs from "../prerequisites.service";
|
|
5
|
+
import { Timeout } from "../timeout.service";
|
|
5
6
|
|
|
6
7
|
export class PrerequisiteMailer implements prereqs.Prerequisite {
|
|
7
8
|
readonly kind = "mailer";
|
|
@@ -9,12 +10,14 @@ export class PrerequisiteMailer implements prereqs.Prerequisite {
|
|
|
9
10
|
readonly enabled?: boolean = true;
|
|
10
11
|
|
|
11
12
|
private readonly mailer: MailerPort;
|
|
13
|
+
readonly timeout: tools.Duration;
|
|
12
14
|
|
|
13
|
-
constructor(config: prereqs.PrerequisiteConfigType & { mailer: MailerPort }) {
|
|
15
|
+
constructor(config: prereqs.PrerequisiteConfigType & { mailer: MailerPort; timeout?: tools.Duration }) {
|
|
14
16
|
this.label = config.label;
|
|
15
17
|
this.enabled = config.enabled === undefined ? true : config.enabled;
|
|
16
18
|
|
|
17
19
|
this.mailer = config.mailer;
|
|
20
|
+
this.timeout = config.timeout ?? tools.Duration.Seconds(2);
|
|
18
21
|
}
|
|
19
22
|
|
|
20
23
|
async verify(clock: ClockPort): Promise<prereqs.VerifyOutcome> {
|
|
@@ -23,7 +26,7 @@ export class PrerequisiteMailer implements prereqs.Prerequisite {
|
|
|
23
26
|
if (!this.enabled) return prereqs.Verification.undetermined(stopwatch.stop());
|
|
24
27
|
|
|
25
28
|
try {
|
|
26
|
-
await this.mailer.verify();
|
|
29
|
+
await Timeout.run(this.mailer.verify(), this.timeout);
|
|
27
30
|
return prereqs.Verification.success(stopwatch.stop());
|
|
28
31
|
} catch (error) {
|
|
29
32
|
return prereqs.Verification.failure(stopwatch.stop(), error as Error);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as tools from "@bgord/tools";
|
|
2
2
|
import type { ClockPort } from "../clock.port";
|
|
3
3
|
import * as prereqs from "../prerequisites.service";
|
|
4
|
+
import { Timeout } from "../timeout.service";
|
|
4
5
|
|
|
5
6
|
export class PrerequisiteOutsideConnectivity implements prereqs.Prerequisite {
|
|
6
7
|
readonly kind = "outside-connectivity";
|
|
@@ -8,10 +9,13 @@ export class PrerequisiteOutsideConnectivity implements prereqs.Prerequisite {
|
|
|
8
9
|
readonly enabled?: boolean = true;
|
|
9
10
|
|
|
10
11
|
private readonly url = "https://google.com";
|
|
12
|
+
readonly timeout: tools.Duration;
|
|
11
13
|
|
|
12
|
-
constructor(config: prereqs.PrerequisiteConfigType) {
|
|
14
|
+
constructor(config: prereqs.PrerequisiteConfigType & { timeout?: tools.Duration }) {
|
|
13
15
|
this.label = config.label;
|
|
14
16
|
this.enabled = config.enabled === undefined ? true : config.enabled;
|
|
17
|
+
|
|
18
|
+
this.timeout = config.timeout ?? tools.Duration.Seconds(2);
|
|
15
19
|
}
|
|
16
20
|
|
|
17
21
|
async verify(clock: ClockPort): Promise<prereqs.VerifyOutcome> {
|
|
@@ -20,7 +24,10 @@ export class PrerequisiteOutsideConnectivity implements prereqs.Prerequisite {
|
|
|
20
24
|
try {
|
|
21
25
|
if (!this.enabled) return prereqs.Verification.undetermined(stopwatch.stop());
|
|
22
26
|
|
|
23
|
-
const response = await
|
|
27
|
+
const response = await Timeout.cancellable(
|
|
28
|
+
(signal: AbortSignal) => fetch(this.url, { method: "HEAD", signal }),
|
|
29
|
+
this.timeout,
|
|
30
|
+
);
|
|
24
31
|
|
|
25
32
|
if (response.ok) return prereqs.Verification.success(stopwatch.stop());
|
|
26
33
|
return prereqs.Verification.failure(stopwatch.stop(), { message: `HTTP ${response.status}` });
|
|
@@ -11,15 +11,15 @@ export enum PrerequisiteStatusEnum {
|
|
|
11
11
|
undetermined = "undetermined",
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
export type VerifySuccess = { status: PrerequisiteStatusEnum.success;
|
|
14
|
+
export type VerifySuccess = { status: PrerequisiteStatusEnum.success; durationMs: tools.DurationMsType };
|
|
15
15
|
export type VerifyFailure = {
|
|
16
16
|
status: PrerequisiteStatusEnum.failure;
|
|
17
|
-
|
|
17
|
+
durationMs: tools.DurationMsType;
|
|
18
18
|
error?: ErrorInfo;
|
|
19
19
|
};
|
|
20
20
|
export type VerifyUndetermined = {
|
|
21
21
|
status: PrerequisiteStatusEnum.undetermined;
|
|
22
|
-
|
|
22
|
+
durationMs: tools.DurationMsType;
|
|
23
23
|
};
|
|
24
24
|
export type VerifyOutcome = VerifySuccess | VerifyFailure | VerifyUndetermined;
|
|
25
25
|
|
|
@@ -39,17 +39,17 @@ export type PrerequisiteResult = {
|
|
|
39
39
|
|
|
40
40
|
export class Verification {
|
|
41
41
|
static success(duration: tools.Duration): VerifySuccess {
|
|
42
|
-
return { status: PrerequisiteStatusEnum.success,
|
|
42
|
+
return { status: PrerequisiteStatusEnum.success, durationMs: duration.ms };
|
|
43
43
|
}
|
|
44
44
|
static failure(duration: tools.Duration, meta?: Error | ErrorInfo): VerifyFailure {
|
|
45
45
|
return {
|
|
46
46
|
status: PrerequisiteStatusEnum.failure,
|
|
47
|
-
|
|
47
|
+
durationMs: duration.ms,
|
|
48
48
|
error: meta instanceof Error ? formatError(meta) : meta,
|
|
49
49
|
};
|
|
50
50
|
}
|
|
51
51
|
static undetermined(duration: tools.Duration): VerifyUndetermined {
|
|
52
|
-
return { status: PrerequisiteStatusEnum.undetermined,
|
|
52
|
+
return { status: PrerequisiteStatusEnum.undetermined, durationMs: duration.ms };
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
|
|
@@ -2,9 +2,11 @@ import * as tools from "@bgord/tools";
|
|
|
2
2
|
import type { TimekeeperPort } from "./timekeeper.port";
|
|
3
3
|
|
|
4
4
|
export class TimekeeperGoogleAdapter implements TimekeeperPort {
|
|
5
|
-
|
|
5
|
+
static URL = "https://www.google.com/generate_204";
|
|
6
|
+
|
|
7
|
+
async get(signal?: AbortSignal) {
|
|
6
8
|
try {
|
|
7
|
-
const response = await fetch(
|
|
9
|
+
const response = await fetch(TimekeeperGoogleAdapter.URL, { signal });
|
|
8
10
|
if (!response.ok) return null;
|
|
9
11
|
|
|
10
12
|
const date = response.headers.get("Date");
|
package/src/timekeeper.port.ts
CHANGED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type * as tools from "@bgord/tools";
|
|
2
|
+
import type { LoggerPort } from "../src/logger.port";
|
|
3
|
+
|
|
4
|
+
export const TimeoutError = { Exceeded: "timeout.exceeded" };
|
|
5
|
+
|
|
6
|
+
export class Timeout {
|
|
7
|
+
static async run<T>(action: Promise<T>, timeout: tools.Duration): Promise<T> {
|
|
8
|
+
return new Promise<T>((resolve, reject) => {
|
|
9
|
+
const reason = new Error(TimeoutError.Exceeded);
|
|
10
|
+
|
|
11
|
+
const canceller = setTimeout(() => reject(reason), timeout.ms);
|
|
12
|
+
|
|
13
|
+
action.then(
|
|
14
|
+
(value) => {
|
|
15
|
+
clearTimeout(canceller);
|
|
16
|
+
resolve(value);
|
|
17
|
+
},
|
|
18
|
+
(error) => {
|
|
19
|
+
clearTimeout(canceller);
|
|
20
|
+
reject(error);
|
|
21
|
+
},
|
|
22
|
+
);
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
static async monitor<T>(action: Promise<T>, timeout: tools.Duration, logger: LoggerPort): Promise<T> {
|
|
27
|
+
const monitor = setTimeout(
|
|
28
|
+
() =>
|
|
29
|
+
logger.warn({
|
|
30
|
+
message: "Timeout",
|
|
31
|
+
component: "infra",
|
|
32
|
+
operation: "timeout_monitor",
|
|
33
|
+
metadata: { timeoutMs: timeout.ms },
|
|
34
|
+
}),
|
|
35
|
+
timeout.ms,
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
return action.finally(() => clearTimeout(monitor));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
static async cancellable<T>(
|
|
42
|
+
action: (signal: AbortSignal) => Promise<T>,
|
|
43
|
+
timeout: tools.Duration,
|
|
44
|
+
): Promise<T> {
|
|
45
|
+
return new Promise<T>((resolve, reject) => {
|
|
46
|
+
const controller = new AbortController();
|
|
47
|
+
|
|
48
|
+
const reason = new Error(TimeoutError.Exceeded);
|
|
49
|
+
|
|
50
|
+
const canceller = setTimeout(() => {
|
|
51
|
+
controller.abort(reason);
|
|
52
|
+
reject(reason);
|
|
53
|
+
}, timeout.ms);
|
|
54
|
+
|
|
55
|
+
// Promise.resolve.then used to prevent the initial action(controller.signal) call
|
|
56
|
+
// from throwing before the resulting work-promise is run by promise.then.
|
|
57
|
+
const promise: Promise<T> = Promise.resolve().then(() => action(controller.signal));
|
|
58
|
+
|
|
59
|
+
promise.then(
|
|
60
|
+
(value) => {
|
|
61
|
+
clearTimeout(canceller);
|
|
62
|
+
resolve(value);
|
|
63
|
+
},
|
|
64
|
+
(error) => {
|
|
65
|
+
clearTimeout(canceller);
|
|
66
|
+
reject(error);
|
|
67
|
+
},
|
|
68
|
+
);
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|