@getstrata/core 0.5.66 → 0.5.68
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 +10 -0
- package/README.md +1 -1
- package/dist/core/http/corsMiddleware.d.ts +8 -1
- package/dist/core/openapi/generator.d.ts +2 -2
- package/dist/{config/queue.d.ts → core/queue/queueConfig.d.ts} +2 -1
- package/dist/core/runtime/appKeyPrefix.d.ts +5 -1
- package/dist/entries/audit/exportAuditLogs.js +50 -39
- package/dist/entries/audit/siemFormatter.js +20 -0
- package/dist/entries/auth/oauth/providers.js +20 -0
- package/dist/entries/auth/oauth/samlProvider.js +20 -0
- package/dist/entries/cache/createCacheStore.js +20 -0
- package/dist/entries/facades.js +20 -0
- package/dist/entries/http/corsMiddleware.js +20 -18
- package/dist/entries/http/loginThrottleMiddleware.js +20 -0
- package/dist/entries/http/memoryThrottleMiddleware.js +20 -0
- package/dist/entries/http/parseMultipartUpload.js +1 -1
- package/dist/entries/http/requireAbilityMiddleware.js +4 -1
- package/dist/entries/http/response.js +4 -1
- package/dist/entries/http/scimThrottleMiddleware.js +20 -0
- package/dist/entries/http/securityHeadersMiddleware.js +48 -9
- package/dist/entries/http/throttleMiddleware.js +20 -0
- package/dist/entries/http/webErrorResponse.js +18 -15
- package/dist/entries/jobs/exportAuditLogsJob.js +50 -39
- package/dist/entries/mail/mailer.js +20 -0
- package/dist/entries/openapi/generator.js +58 -19
- package/dist/entries/queue/createAppQueue.js +40 -18
- package/dist/entries/queue/jobRunner.js +23 -21
- package/dist/entries/queue/publicQueue.js +40 -18
- package/dist/entries/queue/queueMetrics.js +43 -21
- package/dist/entries/queue/redisQueue.js +40 -18
- package/dist/entries/runtime/appKeyPrefix.js +24 -0
- package/dist/entries/runtime/frontendMode.js +23 -0
- package/dist/entries/security/publicReads.js +1 -22
- package/dist/entries/security/safeFetch.js +48 -9
- package/dist/entries/tracing/tracingMiddleware.js +20 -0
- package/dist/index.js +45 -57
- package/package.json +7 -2
- package/dist/bootstrap/config.d.ts +0 -15
- package/dist/config/app.d.ts +0 -10
- package/dist/config/cors.d.ts +0 -9
- package/dist/config/features.d.ts +0 -18
- /package/dist/{config → core/http}/uploads.d.ts +0 -0
- /package/dist/{config/frontend.d.ts → core/runtime/frontendMode.d.ts} +0 -0
|
@@ -2,21 +2,6 @@
|
|
|
2
2
|
// ../../src/core/http/webErrorResponse.ts
|
|
3
3
|
import { HttpError as HttpError2, UnauthorizedError, ValidationError } from "@getstrata/core/errors/http";
|
|
4
4
|
|
|
5
|
-
// ../../src/config/frontend.ts
|
|
6
|
-
function readFrontendMode() {
|
|
7
|
-
const mode = (process.env.FRONTEND_MODE ?? "api").trim();
|
|
8
|
-
if (mode === "server-htmx") {
|
|
9
|
-
return "server-htmx";
|
|
10
|
-
}
|
|
11
|
-
if (mode === "spa-react") {
|
|
12
|
-
return "spa-react";
|
|
13
|
-
}
|
|
14
|
-
return "api";
|
|
15
|
-
}
|
|
16
|
-
function isViewsEnabled() {
|
|
17
|
-
return readFrontendMode() === "server-htmx";
|
|
18
|
-
}
|
|
19
|
-
|
|
20
5
|
// ../../src/core/database/errors.ts
|
|
21
6
|
import {
|
|
22
7
|
BadRequestError,
|
|
@@ -80,6 +65,24 @@ async function withDatabaseErrorHandling(operation) {
|
|
|
80
65
|
}
|
|
81
66
|
}
|
|
82
67
|
|
|
68
|
+
// ../../src/core/runtime/frontendMode.ts
|
|
69
|
+
function readFrontendMode() {
|
|
70
|
+
const mode = (process.env.FRONTEND_MODE ?? "api").trim();
|
|
71
|
+
if (mode === "server-htmx") {
|
|
72
|
+
return "server-htmx";
|
|
73
|
+
}
|
|
74
|
+
if (mode === "spa-react") {
|
|
75
|
+
return "spa-react";
|
|
76
|
+
}
|
|
77
|
+
return "api";
|
|
78
|
+
}
|
|
79
|
+
function isViewsEnabled() {
|
|
80
|
+
return readFrontendMode() === "server-htmx";
|
|
81
|
+
}
|
|
82
|
+
function isSpaEnabled() {
|
|
83
|
+
return readFrontendMode() === "spa-react";
|
|
84
|
+
}
|
|
85
|
+
|
|
83
86
|
// ../../src/core/view/webErrorView.ts
|
|
84
87
|
import { currentRequestMeta } from "@getstrata/core/http/requestMetaContext";
|
|
85
88
|
|
|
@@ -2,14 +2,53 @@
|
|
|
2
2
|
// ../../src/core/audit/exportAuditLogs.ts
|
|
3
3
|
import { repositoryConnection as db2 } from "@getstrata/core/database/repositoryConnection";
|
|
4
4
|
|
|
5
|
-
// ../../src/
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
5
|
+
// ../../src/core/runtime/appKeyPrefix.ts
|
|
6
|
+
function appKeyPrefix() {
|
|
7
|
+
return process.env.APP_KEY_PREFIX?.trim() || "workhub";
|
|
8
|
+
}
|
|
9
|
+
function namespacedRedisKey(kind) {
|
|
10
|
+
return `${appKeyPrefix()}:${kind}`;
|
|
11
|
+
}
|
|
12
|
+
function smtpEhloHost() {
|
|
13
|
+
const raw = process.env.MAIL_EHLO?.trim() || `${appKeyPrefix()}.local`;
|
|
14
|
+
const safe = raw.replace(/[^a-zA-Z0-9.-]/g, "");
|
|
15
|
+
return safe || "strata.local";
|
|
16
|
+
}
|
|
17
|
+
function siemEventType() {
|
|
18
|
+
return process.env.SIEM_EVENT_TYPE?.trim() || `${appKeyPrefix()}.audit`;
|
|
19
|
+
}
|
|
20
|
+
function appUserAgent() {
|
|
21
|
+
return process.env.APP_USER_AGENT?.trim() || appKeyPrefix();
|
|
22
|
+
}
|
|
23
|
+
function otelServiceName() {
|
|
24
|
+
return process.env.OTEL_SERVICE_NAME?.trim() || `${appKeyPrefix()}-api`;
|
|
25
|
+
}
|
|
26
|
+
function webhookSignatureHeader() {
|
|
27
|
+
return process.env.WEBHOOK_SIGNATURE_HEADER?.trim() || `x-${appKeyPrefix()}-signature`;
|
|
28
|
+
}
|
|
29
|
+
function appDisplayName() {
|
|
30
|
+
return process.env.APP_NAME?.trim() || "WorkHub";
|
|
31
|
+
}
|
|
32
|
+
function appEnv() {
|
|
33
|
+
return process.env.APP_ENV?.trim() || "local";
|
|
34
|
+
}
|
|
35
|
+
function appUrl() {
|
|
36
|
+
return (process.env.APP_URL?.trim() || "http://localhost:3000").replace(/\/$/, "");
|
|
37
|
+
}
|
|
38
|
+
function apiPrefix() {
|
|
39
|
+
const raw = process.env.API_PREFIX?.trim() || "/api/v1";
|
|
40
|
+
const withSlash = raw.startsWith("/") ? raw : `/${raw}`;
|
|
41
|
+
const trimmed = withSlash.replace(/\/+$/, "");
|
|
42
|
+
return trimmed || "/api/v1";
|
|
43
|
+
}
|
|
44
|
+
function sdkClientClassName() {
|
|
45
|
+
const override = process.env.APP_SDK_CLASS?.trim();
|
|
46
|
+
if (override && /^[A-Za-z_][A-Za-z0-9_]*$/.test(override)) {
|
|
47
|
+
return override;
|
|
48
|
+
}
|
|
49
|
+
const fromName = appDisplayName().replace(/[^A-Za-z0-9]/g, "");
|
|
50
|
+
return fromName ? `${fromName}Client` : "AppClient";
|
|
51
|
+
}
|
|
13
52
|
|
|
14
53
|
// ../../src/core/security/safeUrl.ts
|
|
15
54
|
import { lookup as dnsLookupImpl } from "dns/promises";
|
|
@@ -113,7 +152,7 @@ var DEFAULT_FETCH_TIMEOUT_MS = 1e4;
|
|
|
113
152
|
async function safeFetch(input, init = {}, options = {}) {
|
|
114
153
|
const timeoutMs = options.timeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS;
|
|
115
154
|
const maxRedirects = options.maxRedirects ?? 0;
|
|
116
|
-
const resolveDns = options.resolveDns ??
|
|
155
|
+
const resolveDns = options.resolveDns ?? appEnv() === "production";
|
|
117
156
|
const urlOptions = { allowHttp: options.allowHttp, resolveDns };
|
|
118
157
|
const controller = new AbortController;
|
|
119
158
|
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
@@ -153,34 +192,6 @@ async function runWithMigrationBypass(callback) {
|
|
|
153
192
|
}
|
|
154
193
|
}
|
|
155
194
|
|
|
156
|
-
// ../../src/core/runtime/appKeyPrefix.ts
|
|
157
|
-
function appKeyPrefix() {
|
|
158
|
-
return process.env.APP_KEY_PREFIX?.trim() || "workhub";
|
|
159
|
-
}
|
|
160
|
-
function namespacedRedisKey(kind) {
|
|
161
|
-
return `${appKeyPrefix()}:${kind}`;
|
|
162
|
-
}
|
|
163
|
-
function smtpEhloHost() {
|
|
164
|
-
const raw = process.env.MAIL_EHLO?.trim() || `${appKeyPrefix()}.local`;
|
|
165
|
-
const safe = raw.replace(/[^a-zA-Z0-9.-]/g, "");
|
|
166
|
-
return safe || "strata.local";
|
|
167
|
-
}
|
|
168
|
-
function siemEventType() {
|
|
169
|
-
return process.env.SIEM_EVENT_TYPE?.trim() || `${appKeyPrefix()}.audit`;
|
|
170
|
-
}
|
|
171
|
-
function appUserAgent() {
|
|
172
|
-
return process.env.APP_USER_AGENT?.trim() || appKeyPrefix();
|
|
173
|
-
}
|
|
174
|
-
function otelServiceName() {
|
|
175
|
-
return process.env.OTEL_SERVICE_NAME?.trim() || `${appKeyPrefix()}-api`;
|
|
176
|
-
}
|
|
177
|
-
function webhookSignatureHeader() {
|
|
178
|
-
return process.env.WEBHOOK_SIGNATURE_HEADER?.trim() || `x-${appKeyPrefix()}-signature`;
|
|
179
|
-
}
|
|
180
|
-
function appDisplayName() {
|
|
181
|
-
return process.env.APP_NAME?.trim() || "WorkHub";
|
|
182
|
-
}
|
|
183
|
-
|
|
184
195
|
// ../../src/core/audit/siemFormatter.ts
|
|
185
196
|
function formatSiemAuditEvent(input) {
|
|
186
197
|
return {
|
|
@@ -220,7 +231,7 @@ function resolveAuditExportConfig() {
|
|
|
220
231
|
if (!endpoint) {
|
|
221
232
|
return null;
|
|
222
233
|
}
|
|
223
|
-
assertSafeOutboundUrl(endpoint, { allowHttp:
|
|
234
|
+
assertSafeOutboundUrl(endpoint, { allowHttp: appEnv() !== "production" });
|
|
224
235
|
const batchSize = Number(process.env.SIEM_EXPORT_BATCH_SIZE ?? "100");
|
|
225
236
|
return {
|
|
226
237
|
endpoint,
|
|
@@ -278,7 +289,7 @@ async function exportPendingAuditLogs() {
|
|
|
278
289
|
...process.env.SIEM_EXPORT_TOKEN ? { authorization: `Bearer ${process.env.SIEM_EXPORT_TOKEN}` } : {}
|
|
279
290
|
},
|
|
280
291
|
body
|
|
281
|
-
}, { allowHttp:
|
|
292
|
+
}, { allowHttp: appEnv() !== "production" });
|
|
282
293
|
if (!response.ok) {
|
|
283
294
|
throw new Error(`SIEM export failed with status ${response.status}.`);
|
|
284
295
|
}
|
|
@@ -26,6 +26,26 @@ function webhookSignatureHeader() {
|
|
|
26
26
|
function appDisplayName() {
|
|
27
27
|
return process.env.APP_NAME?.trim() || "WorkHub";
|
|
28
28
|
}
|
|
29
|
+
function appEnv() {
|
|
30
|
+
return process.env.APP_ENV?.trim() || "local";
|
|
31
|
+
}
|
|
32
|
+
function appUrl() {
|
|
33
|
+
return (process.env.APP_URL?.trim() || "http://localhost:3000").replace(/\/$/, "");
|
|
34
|
+
}
|
|
35
|
+
function apiPrefix() {
|
|
36
|
+
const raw = process.env.API_PREFIX?.trim() || "/api/v1";
|
|
37
|
+
const withSlash = raw.startsWith("/") ? raw : `/${raw}`;
|
|
38
|
+
const trimmed = withSlash.replace(/\/+$/, "");
|
|
39
|
+
return trimmed || "/api/v1";
|
|
40
|
+
}
|
|
41
|
+
function sdkClientClassName() {
|
|
42
|
+
const override = process.env.APP_SDK_CLASS?.trim();
|
|
43
|
+
if (override && /^[A-Za-z_][A-Za-z0-9_]*$/.test(override)) {
|
|
44
|
+
return override;
|
|
45
|
+
}
|
|
46
|
+
const fromName = appDisplayName().replace(/[^A-Za-z0-9]/g, "");
|
|
47
|
+
return fromName ? `${fromName}Client` : "AppClient";
|
|
48
|
+
}
|
|
29
49
|
|
|
30
50
|
// ../../src/core/mail/mailer.ts
|
|
31
51
|
function resolveSmtpConfig() {
|
|
@@ -1,12 +1,51 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
// ../../src/
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
2
|
+
// ../../src/core/runtime/appKeyPrefix.ts
|
|
3
|
+
function appKeyPrefix() {
|
|
4
|
+
return process.env.APP_KEY_PREFIX?.trim() || "workhub";
|
|
5
|
+
}
|
|
6
|
+
function namespacedRedisKey(kind) {
|
|
7
|
+
return `${appKeyPrefix()}:${kind}`;
|
|
8
|
+
}
|
|
9
|
+
function smtpEhloHost() {
|
|
10
|
+
const raw = process.env.MAIL_EHLO?.trim() || `${appKeyPrefix()}.local`;
|
|
11
|
+
const safe = raw.replace(/[^a-zA-Z0-9.-]/g, "");
|
|
12
|
+
return safe || "strata.local";
|
|
13
|
+
}
|
|
14
|
+
function siemEventType() {
|
|
15
|
+
return process.env.SIEM_EVENT_TYPE?.trim() || `${appKeyPrefix()}.audit`;
|
|
16
|
+
}
|
|
17
|
+
function appUserAgent() {
|
|
18
|
+
return process.env.APP_USER_AGENT?.trim() || appKeyPrefix();
|
|
19
|
+
}
|
|
20
|
+
function otelServiceName() {
|
|
21
|
+
return process.env.OTEL_SERVICE_NAME?.trim() || `${appKeyPrefix()}-api`;
|
|
22
|
+
}
|
|
23
|
+
function webhookSignatureHeader() {
|
|
24
|
+
return process.env.WEBHOOK_SIGNATURE_HEADER?.trim() || `x-${appKeyPrefix()}-signature`;
|
|
25
|
+
}
|
|
26
|
+
function appDisplayName() {
|
|
27
|
+
return process.env.APP_NAME?.trim() || "WorkHub";
|
|
28
|
+
}
|
|
29
|
+
function appEnv() {
|
|
30
|
+
return process.env.APP_ENV?.trim() || "local";
|
|
31
|
+
}
|
|
32
|
+
function appUrl() {
|
|
33
|
+
return (process.env.APP_URL?.trim() || "http://localhost:3000").replace(/\/$/, "");
|
|
34
|
+
}
|
|
35
|
+
function apiPrefix() {
|
|
36
|
+
const raw = process.env.API_PREFIX?.trim() || "/api/v1";
|
|
37
|
+
const withSlash = raw.startsWith("/") ? raw : `/${raw}`;
|
|
38
|
+
const trimmed = withSlash.replace(/\/+$/, "");
|
|
39
|
+
return trimmed || "/api/v1";
|
|
40
|
+
}
|
|
41
|
+
function sdkClientClassName() {
|
|
42
|
+
const override = process.env.APP_SDK_CLASS?.trim();
|
|
43
|
+
if (override && /^[A-Za-z_][A-Za-z0-9_]*$/.test(override)) {
|
|
44
|
+
return override;
|
|
45
|
+
}
|
|
46
|
+
const fromName = appDisplayName().replace(/[^A-Za-z0-9]/g, "");
|
|
47
|
+
return fromName ? `${fromName}Client` : "AppClient";
|
|
48
|
+
}
|
|
10
49
|
|
|
11
50
|
// ../../src/core/openapi/generator.ts
|
|
12
51
|
var PUBLIC_ROUTE_DESCRIPTIONS = {
|
|
@@ -84,12 +123,12 @@ function generateOpenApiSpec(routes) {
|
|
|
84
123
|
return {
|
|
85
124
|
openapi: "3.1.0",
|
|
86
125
|
info: {
|
|
87
|
-
title:
|
|
126
|
+
title: `${appDisplayName()} API`,
|
|
88
127
|
version: "1.0.0"
|
|
89
128
|
},
|
|
90
129
|
servers: [
|
|
91
|
-
{ url: `${
|
|
92
|
-
{ url:
|
|
130
|
+
{ url: `${appUrl()}${apiPrefix()}`, description: `${appDisplayName()} API` },
|
|
131
|
+
{ url: appUrl(), description: "Root (health, metrics, SCIM)" }
|
|
93
132
|
],
|
|
94
133
|
paths,
|
|
95
134
|
components: {
|
|
@@ -142,17 +181,17 @@ function renderOpenApiDocument(spec) {
|
|
|
142
181
|
return `${JSON.stringify(spec, null, 2)}
|
|
143
182
|
`;
|
|
144
183
|
}
|
|
145
|
-
function toMethodName(method, path,
|
|
146
|
-
const relativePath = path.startsWith(
|
|
184
|
+
function toMethodName(method, path, apiPrefix2) {
|
|
185
|
+
const relativePath = path.startsWith(apiPrefix2) ? path.slice(apiPrefix2.length) || "/" : path;
|
|
147
186
|
const segments = relativePath.replace(/\{|\}/g, "").split("/").filter(Boolean).flatMap((segment) => segment.split("-")).map((segment) => segment.replace(/[^a-zA-Z0-9]/g, "")).filter(Boolean).map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1));
|
|
148
187
|
return `${method.toLowerCase()}${segments.join("")}`;
|
|
149
188
|
}
|
|
150
|
-
function toRequestPath(path,
|
|
151
|
-
return path.startsWith(
|
|
189
|
+
function toRequestPath(path, apiPrefix2) {
|
|
190
|
+
return path.startsWith(apiPrefix2) ? path.slice(apiPrefix2.length) || "/" : path;
|
|
152
191
|
}
|
|
153
|
-
function renderTypeScriptSdk(spec,
|
|
192
|
+
function renderTypeScriptSdk(spec, prefix = apiPrefix()) {
|
|
154
193
|
const lines = [
|
|
155
|
-
|
|
194
|
+
`export class ${sdkClientClassName()} {`,
|
|
156
195
|
` constructor(private readonly baseUrl = "${spec.servers[0]?.url ?? ""}") {}`,
|
|
157
196
|
"",
|
|
158
197
|
" private async request(path: string, init: RequestInit = {}): Promise<Response> {",
|
|
@@ -161,9 +200,9 @@ function renderTypeScriptSdk(spec, apiPrefix = "/api/v1") {
|
|
|
161
200
|
""
|
|
162
201
|
];
|
|
163
202
|
for (const [path, methods] of Object.entries(spec.paths)) {
|
|
164
|
-
const requestPath = toRequestPath(path,
|
|
203
|
+
const requestPath = toRequestPath(path, prefix);
|
|
165
204
|
for (const method of Object.keys(methods)) {
|
|
166
|
-
const functionName = toMethodName(method, path,
|
|
205
|
+
const functionName = toMethodName(method, path, prefix);
|
|
167
206
|
lines.push(` async ${functionName}(init: RequestInit = {}): Promise<Response> {`, ` return await this.request("${requestPath}", { ...init, method: "${method.toUpperCase()}" });`, " }", "");
|
|
168
207
|
}
|
|
169
208
|
}
|
|
@@ -100,25 +100,27 @@ function readSharedJobRegistry() {
|
|
|
100
100
|
}
|
|
101
101
|
var jobRegistry = readSharedJobRegistry();
|
|
102
102
|
|
|
103
|
-
// ../../src/
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
} from "@getstrata/core/contracts/serviceTokens";
|
|
115
|
-
var DEFAULT_QUEUE_DRIVER = "sync";
|
|
116
|
-
|
|
117
|
-
// ../../src/config/queue.ts
|
|
103
|
+
// ../../src/core/queue/queueConfig.ts
|
|
104
|
+
function resolveQueueConfig() {
|
|
105
|
+
const driver = process.env.QUEUE_DRIVER;
|
|
106
|
+
const maxAttempts = Number(process.env.QUEUE_MAX_ATTEMPTS ?? "3");
|
|
107
|
+
const backoffMs = Number(process.env.QUEUE_BACKOFF_MS ?? "1000");
|
|
108
|
+
return {
|
|
109
|
+
driver: driver === "redis" || driver === "async" || driver === "sync" ? driver : "sync",
|
|
110
|
+
maxAttempts: Number.isFinite(maxAttempts) && maxAttempts > 0 ? maxAttempts : 3,
|
|
111
|
+
backoffMs: Number.isFinite(backoffMs) && backoffMs >= 0 ? backoffMs : 1000
|
|
112
|
+
};
|
|
113
|
+
}
|
|
118
114
|
var queueConfig = {
|
|
119
|
-
driver
|
|
120
|
-
|
|
121
|
-
|
|
115
|
+
get driver() {
|
|
116
|
+
return resolveQueueConfig().driver;
|
|
117
|
+
},
|
|
118
|
+
get maxAttempts() {
|
|
119
|
+
return resolveQueueConfig().maxAttempts;
|
|
120
|
+
},
|
|
121
|
+
get backoffMs() {
|
|
122
|
+
return resolveQueueConfig().backoffMs;
|
|
123
|
+
}
|
|
122
124
|
};
|
|
123
125
|
|
|
124
126
|
// ../../src/core/queue/jobRunner.ts
|
|
@@ -181,6 +183,26 @@ function webhookSignatureHeader() {
|
|
|
181
183
|
function appDisplayName() {
|
|
182
184
|
return process.env.APP_NAME?.trim() || "WorkHub";
|
|
183
185
|
}
|
|
186
|
+
function appEnv() {
|
|
187
|
+
return process.env.APP_ENV?.trim() || "local";
|
|
188
|
+
}
|
|
189
|
+
function appUrl() {
|
|
190
|
+
return (process.env.APP_URL?.trim() || "http://localhost:3000").replace(/\/$/, "");
|
|
191
|
+
}
|
|
192
|
+
function apiPrefix() {
|
|
193
|
+
const raw = process.env.API_PREFIX?.trim() || "/api/v1";
|
|
194
|
+
const withSlash = raw.startsWith("/") ? raw : `/${raw}`;
|
|
195
|
+
const trimmed = withSlash.replace(/\/+$/, "");
|
|
196
|
+
return trimmed || "/api/v1";
|
|
197
|
+
}
|
|
198
|
+
function sdkClientClassName() {
|
|
199
|
+
const override = process.env.APP_SDK_CLASS?.trim();
|
|
200
|
+
if (override && /^[A-Za-z_][A-Za-z0-9_]*$/.test(override)) {
|
|
201
|
+
return override;
|
|
202
|
+
}
|
|
203
|
+
const fromName = appDisplayName().replace(/[^A-Za-z0-9]/g, "");
|
|
204
|
+
return fromName ? `${fromName}Client` : "AppClient";
|
|
205
|
+
}
|
|
184
206
|
|
|
185
207
|
// ../../src/core/queue/redisQueue.ts
|
|
186
208
|
function queueListKey() {
|
|
@@ -1,25 +1,4 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
// ../../src/bootstrap/config.ts
|
|
3
|
-
import {
|
|
4
|
-
CORE_ABILITY_CHECKER_TOKEN,
|
|
5
|
-
CORE_AUTH_TOKEN,
|
|
6
|
-
CORE_AUTH_USER_DIRECTORY_TOKEN,
|
|
7
|
-
CORE_CACHE_TOKEN,
|
|
8
|
-
CORE_CONFIG_TOKEN,
|
|
9
|
-
CORE_EVENT_BUS_TOKEN,
|
|
10
|
-
CORE_POLICY_GATE_TOKEN,
|
|
11
|
-
CORE_QUEUE_TOKEN,
|
|
12
|
-
CORE_TOKEN_SERVICE_TOKEN
|
|
13
|
-
} from "@getstrata/core/contracts/serviceTokens";
|
|
14
|
-
var DEFAULT_QUEUE_DRIVER = "sync";
|
|
15
|
-
|
|
16
|
-
// ../../src/config/queue.ts
|
|
17
|
-
var queueConfig = {
|
|
18
|
-
driver: process.env.QUEUE_DRIVER === "redis" || process.env.QUEUE_DRIVER === "async" || process.env.QUEUE_DRIVER === "sync" ? process.env.QUEUE_DRIVER : DEFAULT_QUEUE_DRIVER,
|
|
19
|
-
maxAttempts: Number(process.env.QUEUE_MAX_ATTEMPTS ?? "3"),
|
|
20
|
-
backoffMs: Number(process.env.QUEUE_BACKOFF_MS ?? "1000")
|
|
21
|
-
};
|
|
22
|
-
|
|
23
2
|
// ../../src/core/queue/jobRegistry.ts
|
|
24
3
|
class JobRegistry {
|
|
25
4
|
factories = new Map;
|
|
@@ -57,6 +36,29 @@ function readSharedJobRegistry() {
|
|
|
57
36
|
}
|
|
58
37
|
var jobRegistry = readSharedJobRegistry();
|
|
59
38
|
|
|
39
|
+
// ../../src/core/queue/queueConfig.ts
|
|
40
|
+
function resolveQueueConfig() {
|
|
41
|
+
const driver = process.env.QUEUE_DRIVER;
|
|
42
|
+
const maxAttempts = Number(process.env.QUEUE_MAX_ATTEMPTS ?? "3");
|
|
43
|
+
const backoffMs = Number(process.env.QUEUE_BACKOFF_MS ?? "1000");
|
|
44
|
+
return {
|
|
45
|
+
driver: driver === "redis" || driver === "async" || driver === "sync" ? driver : "sync",
|
|
46
|
+
maxAttempts: Number.isFinite(maxAttempts) && maxAttempts > 0 ? maxAttempts : 3,
|
|
47
|
+
backoffMs: Number.isFinite(backoffMs) && backoffMs >= 0 ? backoffMs : 1000
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
var queueConfig = {
|
|
51
|
+
get driver() {
|
|
52
|
+
return resolveQueueConfig().driver;
|
|
53
|
+
},
|
|
54
|
+
get maxAttempts() {
|
|
55
|
+
return resolveQueueConfig().maxAttempts;
|
|
56
|
+
},
|
|
57
|
+
get backoffMs() {
|
|
58
|
+
return resolveQueueConfig().backoffMs;
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
60
62
|
// ../../src/core/queue/jobRunner.ts
|
|
61
63
|
async function runQueueJob(envelope, failedJobs) {
|
|
62
64
|
const job = jobRegistry.create(envelope.name);
|
|
@@ -100,25 +100,27 @@ function readSharedJobRegistry() {
|
|
|
100
100
|
}
|
|
101
101
|
var jobRegistry = readSharedJobRegistry();
|
|
102
102
|
|
|
103
|
-
// ../../src/
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
} from "@getstrata/core/contracts/serviceTokens";
|
|
115
|
-
var DEFAULT_QUEUE_DRIVER = "sync";
|
|
116
|
-
|
|
117
|
-
// ../../src/config/queue.ts
|
|
103
|
+
// ../../src/core/queue/queueConfig.ts
|
|
104
|
+
function resolveQueueConfig() {
|
|
105
|
+
const driver = process.env.QUEUE_DRIVER;
|
|
106
|
+
const maxAttempts = Number(process.env.QUEUE_MAX_ATTEMPTS ?? "3");
|
|
107
|
+
const backoffMs = Number(process.env.QUEUE_BACKOFF_MS ?? "1000");
|
|
108
|
+
return {
|
|
109
|
+
driver: driver === "redis" || driver === "async" || driver === "sync" ? driver : "sync",
|
|
110
|
+
maxAttempts: Number.isFinite(maxAttempts) && maxAttempts > 0 ? maxAttempts : 3,
|
|
111
|
+
backoffMs: Number.isFinite(backoffMs) && backoffMs >= 0 ? backoffMs : 1000
|
|
112
|
+
};
|
|
113
|
+
}
|
|
118
114
|
var queueConfig = {
|
|
119
|
-
driver
|
|
120
|
-
|
|
121
|
-
|
|
115
|
+
get driver() {
|
|
116
|
+
return resolveQueueConfig().driver;
|
|
117
|
+
},
|
|
118
|
+
get maxAttempts() {
|
|
119
|
+
return resolveQueueConfig().maxAttempts;
|
|
120
|
+
},
|
|
121
|
+
get backoffMs() {
|
|
122
|
+
return resolveQueueConfig().backoffMs;
|
|
123
|
+
}
|
|
122
124
|
};
|
|
123
125
|
|
|
124
126
|
// ../../src/core/queue/jobRunner.ts
|
|
@@ -181,6 +183,26 @@ function webhookSignatureHeader() {
|
|
|
181
183
|
function appDisplayName() {
|
|
182
184
|
return process.env.APP_NAME?.trim() || "WorkHub";
|
|
183
185
|
}
|
|
186
|
+
function appEnv() {
|
|
187
|
+
return process.env.APP_ENV?.trim() || "local";
|
|
188
|
+
}
|
|
189
|
+
function appUrl() {
|
|
190
|
+
return (process.env.APP_URL?.trim() || "http://localhost:3000").replace(/\/$/, "");
|
|
191
|
+
}
|
|
192
|
+
function apiPrefix() {
|
|
193
|
+
const raw = process.env.API_PREFIX?.trim() || "/api/v1";
|
|
194
|
+
const withSlash = raw.startsWith("/") ? raw : `/${raw}`;
|
|
195
|
+
const trimmed = withSlash.replace(/\/+$/, "");
|
|
196
|
+
return trimmed || "/api/v1";
|
|
197
|
+
}
|
|
198
|
+
function sdkClientClassName() {
|
|
199
|
+
const override = process.env.APP_SDK_CLASS?.trim();
|
|
200
|
+
if (override && /^[A-Za-z_][A-Za-z0-9_]*$/.test(override)) {
|
|
201
|
+
return override;
|
|
202
|
+
}
|
|
203
|
+
const fromName = appDisplayName().replace(/[^A-Za-z0-9]/g, "");
|
|
204
|
+
return fromName ? `${fromName}Client` : "AppClient";
|
|
205
|
+
}
|
|
184
206
|
|
|
185
207
|
// ../../src/core/queue/redisQueue.ts
|
|
186
208
|
function queueListKey() {
|
|
@@ -2,27 +2,6 @@
|
|
|
2
2
|
// ../../src/core/queue/queueMetrics.ts
|
|
3
3
|
var {RedisClient: RedisClient2 } = globalThis.Bun;
|
|
4
4
|
|
|
5
|
-
// ../../src/bootstrap/config.ts
|
|
6
|
-
import {
|
|
7
|
-
CORE_ABILITY_CHECKER_TOKEN,
|
|
8
|
-
CORE_AUTH_TOKEN,
|
|
9
|
-
CORE_AUTH_USER_DIRECTORY_TOKEN,
|
|
10
|
-
CORE_CACHE_TOKEN,
|
|
11
|
-
CORE_CONFIG_TOKEN,
|
|
12
|
-
CORE_EVENT_BUS_TOKEN,
|
|
13
|
-
CORE_POLICY_GATE_TOKEN,
|
|
14
|
-
CORE_QUEUE_TOKEN,
|
|
15
|
-
CORE_TOKEN_SERVICE_TOKEN
|
|
16
|
-
} from "@getstrata/core/contracts/serviceTokens";
|
|
17
|
-
var DEFAULT_QUEUE_DRIVER = "sync";
|
|
18
|
-
|
|
19
|
-
// ../../src/config/queue.ts
|
|
20
|
-
var queueConfig = {
|
|
21
|
-
driver: process.env.QUEUE_DRIVER === "redis" || process.env.QUEUE_DRIVER === "async" || process.env.QUEUE_DRIVER === "sync" ? process.env.QUEUE_DRIVER : DEFAULT_QUEUE_DRIVER,
|
|
22
|
-
maxAttempts: Number(process.env.QUEUE_MAX_ATTEMPTS ?? "3"),
|
|
23
|
-
backoffMs: Number(process.env.QUEUE_BACKOFF_MS ?? "1000")
|
|
24
|
-
};
|
|
25
|
-
|
|
26
5
|
// ../../src/core/queue/failedJobRepository.ts
|
|
27
6
|
import { BaseRepository } from "@getstrata/core/database/baseRepository";
|
|
28
7
|
|
|
@@ -124,6 +103,29 @@ function readSharedJobRegistry() {
|
|
|
124
103
|
}
|
|
125
104
|
var jobRegistry = readSharedJobRegistry();
|
|
126
105
|
|
|
106
|
+
// ../../src/core/queue/queueConfig.ts
|
|
107
|
+
function resolveQueueConfig() {
|
|
108
|
+
const driver = process.env.QUEUE_DRIVER;
|
|
109
|
+
const maxAttempts = Number(process.env.QUEUE_MAX_ATTEMPTS ?? "3");
|
|
110
|
+
const backoffMs = Number(process.env.QUEUE_BACKOFF_MS ?? "1000");
|
|
111
|
+
return {
|
|
112
|
+
driver: driver === "redis" || driver === "async" || driver === "sync" ? driver : "sync",
|
|
113
|
+
maxAttempts: Number.isFinite(maxAttempts) && maxAttempts > 0 ? maxAttempts : 3,
|
|
114
|
+
backoffMs: Number.isFinite(backoffMs) && backoffMs >= 0 ? backoffMs : 1000
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
var queueConfig = {
|
|
118
|
+
get driver() {
|
|
119
|
+
return resolveQueueConfig().driver;
|
|
120
|
+
},
|
|
121
|
+
get maxAttempts() {
|
|
122
|
+
return resolveQueueConfig().maxAttempts;
|
|
123
|
+
},
|
|
124
|
+
get backoffMs() {
|
|
125
|
+
return resolveQueueConfig().backoffMs;
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
|
|
127
129
|
// ../../src/core/queue/jobRunner.ts
|
|
128
130
|
async function runQueueJob(envelope, failedJobs) {
|
|
129
131
|
const job = jobRegistry.create(envelope.name);
|
|
@@ -184,6 +186,26 @@ function webhookSignatureHeader() {
|
|
|
184
186
|
function appDisplayName() {
|
|
185
187
|
return process.env.APP_NAME?.trim() || "WorkHub";
|
|
186
188
|
}
|
|
189
|
+
function appEnv() {
|
|
190
|
+
return process.env.APP_ENV?.trim() || "local";
|
|
191
|
+
}
|
|
192
|
+
function appUrl() {
|
|
193
|
+
return (process.env.APP_URL?.trim() || "http://localhost:3000").replace(/\/$/, "");
|
|
194
|
+
}
|
|
195
|
+
function apiPrefix() {
|
|
196
|
+
const raw = process.env.API_PREFIX?.trim() || "/api/v1";
|
|
197
|
+
const withSlash = raw.startsWith("/") ? raw : `/${raw}`;
|
|
198
|
+
const trimmed = withSlash.replace(/\/+$/, "");
|
|
199
|
+
return trimmed || "/api/v1";
|
|
200
|
+
}
|
|
201
|
+
function sdkClientClassName() {
|
|
202
|
+
const override = process.env.APP_SDK_CLASS?.trim();
|
|
203
|
+
if (override && /^[A-Za-z_][A-Za-z0-9_]*$/.test(override)) {
|
|
204
|
+
return override;
|
|
205
|
+
}
|
|
206
|
+
const fromName = appDisplayName().replace(/[^A-Za-z0-9]/g, "");
|
|
207
|
+
return fromName ? `${fromName}Client` : "AppClient";
|
|
208
|
+
}
|
|
187
209
|
|
|
188
210
|
// ../../src/core/queue/redisQueue.ts
|
|
189
211
|
function queueListKey() {
|
|
@@ -29,6 +29,26 @@ function webhookSignatureHeader() {
|
|
|
29
29
|
function appDisplayName() {
|
|
30
30
|
return process.env.APP_NAME?.trim() || "WorkHub";
|
|
31
31
|
}
|
|
32
|
+
function appEnv() {
|
|
33
|
+
return process.env.APP_ENV?.trim() || "local";
|
|
34
|
+
}
|
|
35
|
+
function appUrl() {
|
|
36
|
+
return (process.env.APP_URL?.trim() || "http://localhost:3000").replace(/\/$/, "");
|
|
37
|
+
}
|
|
38
|
+
function apiPrefix() {
|
|
39
|
+
const raw = process.env.API_PREFIX?.trim() || "/api/v1";
|
|
40
|
+
const withSlash = raw.startsWith("/") ? raw : `/${raw}`;
|
|
41
|
+
const trimmed = withSlash.replace(/\/+$/, "");
|
|
42
|
+
return trimmed || "/api/v1";
|
|
43
|
+
}
|
|
44
|
+
function sdkClientClassName() {
|
|
45
|
+
const override = process.env.APP_SDK_CLASS?.trim();
|
|
46
|
+
if (override && /^[A-Za-z_][A-Za-z0-9_]*$/.test(override)) {
|
|
47
|
+
return override;
|
|
48
|
+
}
|
|
49
|
+
const fromName = appDisplayName().replace(/[^A-Za-z0-9]/g, "");
|
|
50
|
+
return fromName ? `${fromName}Client` : "AppClient";
|
|
51
|
+
}
|
|
32
52
|
|
|
33
53
|
// ../../src/core/queue/jobRegistry.ts
|
|
34
54
|
class JobRegistry {
|
|
@@ -67,25 +87,27 @@ function readSharedJobRegistry() {
|
|
|
67
87
|
}
|
|
68
88
|
var jobRegistry = readSharedJobRegistry();
|
|
69
89
|
|
|
70
|
-
// ../../src/
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
} from "@getstrata/core/contracts/serviceTokens";
|
|
82
|
-
var DEFAULT_QUEUE_DRIVER = "sync";
|
|
83
|
-
|
|
84
|
-
// ../../src/config/queue.ts
|
|
90
|
+
// ../../src/core/queue/queueConfig.ts
|
|
91
|
+
function resolveQueueConfig() {
|
|
92
|
+
const driver = process.env.QUEUE_DRIVER;
|
|
93
|
+
const maxAttempts = Number(process.env.QUEUE_MAX_ATTEMPTS ?? "3");
|
|
94
|
+
const backoffMs = Number(process.env.QUEUE_BACKOFF_MS ?? "1000");
|
|
95
|
+
return {
|
|
96
|
+
driver: driver === "redis" || driver === "async" || driver === "sync" ? driver : "sync",
|
|
97
|
+
maxAttempts: Number.isFinite(maxAttempts) && maxAttempts > 0 ? maxAttempts : 3,
|
|
98
|
+
backoffMs: Number.isFinite(backoffMs) && backoffMs >= 0 ? backoffMs : 1000
|
|
99
|
+
};
|
|
100
|
+
}
|
|
85
101
|
var queueConfig = {
|
|
86
|
-
driver
|
|
87
|
-
|
|
88
|
-
|
|
102
|
+
get driver() {
|
|
103
|
+
return resolveQueueConfig().driver;
|
|
104
|
+
},
|
|
105
|
+
get maxAttempts() {
|
|
106
|
+
return resolveQueueConfig().maxAttempts;
|
|
107
|
+
},
|
|
108
|
+
get backoffMs() {
|
|
109
|
+
return resolveQueueConfig().backoffMs;
|
|
110
|
+
}
|
|
89
111
|
};
|
|
90
112
|
|
|
91
113
|
// ../../src/core/queue/jobRunner.ts
|