@getstrata/bootstrap 2.2.4 → 2.2.5
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/CHANGELOG.md +4 -0
- package/dist/bootstrap/health.d.ts +5 -1
- package/dist/entries/health.js +36 -6
- package/dist/index.js +36 -6
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import type { AppDependencies } from "./contracts";
|
|
2
2
|
interface CreateHealthRoutesOptions {
|
|
3
3
|
pingOnHealth?: boolean;
|
|
4
|
+
/** Optional non-mutating schema check; applies to dependency-checked /health only. */
|
|
5
|
+
schemaCheck?: () => boolean | Promise<boolean>;
|
|
6
|
+
/** Text mode emits only ok/degraded for container readiness probes. */
|
|
7
|
+
healthFormat?: "json" | "text";
|
|
4
8
|
extra?: Record<string, unknown> | (() => Record<string, unknown> | Promise<Record<string, unknown>>);
|
|
5
9
|
}
|
|
6
10
|
declare function checkDatabase(): Promise<boolean>;
|
|
7
11
|
declare function pingDatabase(): Promise<boolean>;
|
|
8
12
|
declare function checkRedis(redisUrl: string): Promise<boolean>;
|
|
9
13
|
declare function createHealthRoutes(dependencies: AppDependencies, options?: CreateHealthRoutesOptions): {
|
|
10
|
-
"/health": () => Promise<Response>;
|
|
14
|
+
"/health": (request?: Request) => Promise<Response>;
|
|
11
15
|
"/ready": () => Promise<Response>;
|
|
12
16
|
};
|
|
13
17
|
export type { CreateHealthRoutesOptions };
|
package/dist/entries/health.js
CHANGED
|
@@ -75,12 +75,19 @@ async function pingDatabase() {
|
|
|
75
75
|
return await checkDatabase();
|
|
76
76
|
}
|
|
77
77
|
async function checkRedis(redisUrl) {
|
|
78
|
+
let client;
|
|
78
79
|
try {
|
|
79
|
-
|
|
80
|
+
client = new RedisClient(redisUrl, {
|
|
81
|
+
connectionTimeout: 1000,
|
|
82
|
+
idleTimeout: 1000,
|
|
83
|
+
autoReconnect: false
|
|
84
|
+
});
|
|
80
85
|
const response = await client.ping();
|
|
81
86
|
return response === "PONG";
|
|
82
87
|
} catch {
|
|
83
88
|
return false;
|
|
89
|
+
} finally {
|
|
90
|
+
client?.close();
|
|
84
91
|
}
|
|
85
92
|
}
|
|
86
93
|
async function resolveExtraFields(extra) {
|
|
@@ -104,17 +111,40 @@ async function collectDependencyChecks(dependencies) {
|
|
|
104
111
|
}
|
|
105
112
|
function createHealthRoutes(dependencies, options = {}) {
|
|
106
113
|
return {
|
|
107
|
-
"/health": async () => {
|
|
108
|
-
|
|
114
|
+
"/health": async (request) => {
|
|
115
|
+
if (request && request.method !== "GET" && request.method !== "HEAD") {
|
|
116
|
+
return new Response(null, { status: 405, headers: { allow: "GET, HEAD" } });
|
|
117
|
+
}
|
|
118
|
+
const headers = { "cache-control": "no-store" };
|
|
119
|
+
const extra = options.healthFormat === "text" ? {} : await resolveExtraFields(options.extra);
|
|
109
120
|
if (!options.pingOnHealth) {
|
|
110
|
-
return
|
|
121
|
+
return options.healthFormat === "text" ? new Response("ok", {
|
|
122
|
+
headers: { ...headers, "content-type": "text/plain; charset=utf-8" }
|
|
123
|
+
}) : jsonResponse({ status: "ok", ...extra }, { headers });
|
|
124
|
+
}
|
|
125
|
+
const { checks, ready: dependenciesReady } = await collectDependencyChecks(dependencies);
|
|
126
|
+
let schemaReady = true;
|
|
127
|
+
if (options.schemaCheck) {
|
|
128
|
+
try {
|
|
129
|
+
schemaReady = await options.schemaCheck();
|
|
130
|
+
} catch {
|
|
131
|
+
schemaReady = false;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
if (options.schemaCheck)
|
|
135
|
+
checks.schema = schemaReady ? "ok" : "error";
|
|
136
|
+
const ready = dependenciesReady && schemaReady;
|
|
137
|
+
if (options.healthFormat === "text") {
|
|
138
|
+
return new Response(ready ? "ok" : "degraded", {
|
|
139
|
+
status: ready ? 200 : 503,
|
|
140
|
+
headers: { ...headers, "content-type": "text/plain; charset=utf-8" }
|
|
141
|
+
});
|
|
111
142
|
}
|
|
112
|
-
const { checks, ready } = await collectDependencyChecks(dependencies);
|
|
113
143
|
return jsonResponse({
|
|
114
144
|
status: ready ? "ok" : "error",
|
|
115
145
|
checks,
|
|
116
146
|
...extra
|
|
117
|
-
}, { status: ready ? 200 : 503 });
|
|
147
|
+
}, { status: ready ? 200 : 503, headers });
|
|
118
148
|
},
|
|
119
149
|
"/ready": async () => {
|
|
120
150
|
const extra = await resolveExtraFields(options.extra);
|
package/dist/index.js
CHANGED
|
@@ -1559,12 +1559,19 @@ async function pingDatabase() {
|
|
|
1559
1559
|
return await checkDatabase();
|
|
1560
1560
|
}
|
|
1561
1561
|
async function checkRedis(redisUrl) {
|
|
1562
|
+
let client;
|
|
1562
1563
|
try {
|
|
1563
|
-
|
|
1564
|
+
client = new RedisClient(redisUrl, {
|
|
1565
|
+
connectionTimeout: 1000,
|
|
1566
|
+
idleTimeout: 1000,
|
|
1567
|
+
autoReconnect: false
|
|
1568
|
+
});
|
|
1564
1569
|
const response = await client.ping();
|
|
1565
1570
|
return response === "PONG";
|
|
1566
1571
|
} catch {
|
|
1567
1572
|
return false;
|
|
1573
|
+
} finally {
|
|
1574
|
+
client?.close();
|
|
1568
1575
|
}
|
|
1569
1576
|
}
|
|
1570
1577
|
async function resolveExtraFields(extra) {
|
|
@@ -1588,17 +1595,40 @@ async function collectDependencyChecks(dependencies) {
|
|
|
1588
1595
|
}
|
|
1589
1596
|
function createHealthRoutes(dependencies, options = {}) {
|
|
1590
1597
|
return {
|
|
1591
|
-
"/health": async () => {
|
|
1592
|
-
|
|
1598
|
+
"/health": async (request) => {
|
|
1599
|
+
if (request && request.method !== "GET" && request.method !== "HEAD") {
|
|
1600
|
+
return new Response(null, { status: 405, headers: { allow: "GET, HEAD" } });
|
|
1601
|
+
}
|
|
1602
|
+
const headers = { "cache-control": "no-store" };
|
|
1603
|
+
const extra = options.healthFormat === "text" ? {} : await resolveExtraFields(options.extra);
|
|
1593
1604
|
if (!options.pingOnHealth) {
|
|
1594
|
-
return
|
|
1605
|
+
return options.healthFormat === "text" ? new Response("ok", {
|
|
1606
|
+
headers: { ...headers, "content-type": "text/plain; charset=utf-8" }
|
|
1607
|
+
}) : jsonResponse({ status: "ok", ...extra }, { headers });
|
|
1608
|
+
}
|
|
1609
|
+
const { checks, ready: dependenciesReady } = await collectDependencyChecks(dependencies);
|
|
1610
|
+
let schemaReady = true;
|
|
1611
|
+
if (options.schemaCheck) {
|
|
1612
|
+
try {
|
|
1613
|
+
schemaReady = await options.schemaCheck();
|
|
1614
|
+
} catch {
|
|
1615
|
+
schemaReady = false;
|
|
1616
|
+
}
|
|
1617
|
+
}
|
|
1618
|
+
if (options.schemaCheck)
|
|
1619
|
+
checks.schema = schemaReady ? "ok" : "error";
|
|
1620
|
+
const ready = dependenciesReady && schemaReady;
|
|
1621
|
+
if (options.healthFormat === "text") {
|
|
1622
|
+
return new Response(ready ? "ok" : "degraded", {
|
|
1623
|
+
status: ready ? 200 : 503,
|
|
1624
|
+
headers: { ...headers, "content-type": "text/plain; charset=utf-8" }
|
|
1625
|
+
});
|
|
1595
1626
|
}
|
|
1596
|
-
const { checks, ready } = await collectDependencyChecks(dependencies);
|
|
1597
1627
|
return jsonResponse({
|
|
1598
1628
|
status: ready ? "ok" : "error",
|
|
1599
1629
|
checks,
|
|
1600
1630
|
...extra
|
|
1601
|
-
}, { status: ready ? 200 : 503 });
|
|
1631
|
+
}, { status: ready ? 200 : 503, headers });
|
|
1602
1632
|
},
|
|
1603
1633
|
"/ready": async () => {
|
|
1604
1634
|
const extra = await resolveExtraFields(options.extra);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@getstrata/bootstrap",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.5",
|
|
4
4
|
"description": "Strata application bootstrap: HttpKernel, DI, web session helpers",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -190,7 +190,7 @@
|
|
|
190
190
|
"access": "public"
|
|
191
191
|
},
|
|
192
192
|
"peerDependencies": {
|
|
193
|
-
"@getstrata/core": "^2.2.
|
|
193
|
+
"@getstrata/core": "^2.2.5",
|
|
194
194
|
"typescript": "^5.9.0 || ^6.0.0 || ^7.0.0"
|
|
195
195
|
},
|
|
196
196
|
"engines": {
|