@getstrata/core 0.5.49 → 0.5.50
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/README.md +4 -2
- package/dist/core/auth/membershipContextMiddleware.d.ts +1 -1
- package/dist/core/auth/scimAuthMiddleware.d.ts +1 -1
- package/dist/core/database/errors.d.ts +1 -1
- package/dist/core/facades/index.d.ts +1 -1
- package/dist/core/http/authMiddleware.d.ts +2 -2
- package/dist/core/http/authorizeMiddleware.d.ts +2 -2
- package/dist/core/http/index.d.ts +1 -1
- package/dist/core/http/requireAuthMiddleware.d.ts +1 -1
- package/dist/core/http/requireWebAuthMiddleware.d.ts +1 -1
- package/dist/core/http/securedRouteModelBinding.d.ts +1 -1
- package/dist/core/logging/requestLoggingMiddleware.d.ts +1 -1
- package/dist/core/queue/failedJobRepository.d.ts +1 -1
- package/dist/core/queue/failedJobTable.d.ts +1 -1
- package/dist/core/runtime/applicationRegistry.d.ts +3 -3
- package/dist/core/tracing/tracingMiddleware.d.ts +1 -1
- package/dist/entries/audit/exportAuditLogs.js +9 -175
- package/dist/entries/auth/membershipMiddleware.js +2 -74
- package/dist/entries/auth/scimAuthMiddleware.js +10 -93
- package/dist/entries/auth/sessionGuard.js +0 -6
- package/dist/entries/database/errors.js +6 -66
- package/dist/entries/database/model.js +2 -65
- package/dist/entries/http/authMiddleware.js +1 -23
- package/dist/entries/http/authorizeMiddleware.js +2 -89
- package/dist/entries/http/bodySizeLimitMiddleware.js +1 -66
- package/dist/entries/http/conditionalResponse.js +3 -66
- package/dist/entries/http/csrfMiddleware.js +2 -65
- package/dist/entries/http/etag.js +3 -66
- package/dist/entries/http/formRequest.js +5 -107
- package/dist/entries/http/pagination.js +8 -110
- package/dist/entries/http/parseFormBody.js +1 -66
- package/dist/entries/http/parseMultipartUpload.js +3 -66
- package/dist/entries/http/requireAbilityMiddleware.js +2 -89
- package/dist/entries/http/requireAuthMiddleware.js +1 -66
- package/dist/entries/http/requireGlobalAdminMiddleware.js +4 -128
- package/dist/entries/http/requireWebAuthMiddleware.js +2 -65
- package/dist/entries/http/routeModelBinding.js +3 -108
- package/dist/entries/http/securedRouteModelBinding.js +15 -203
- package/dist/entries/http/throttleMiddleware.js +2 -44
- package/dist/entries/http/webErrorResponse.js +25 -93
- package/dist/entries/http/webFormRequest.js +8 -109
- package/dist/entries/jobs/dispatchWebhookJob.js +5 -174
- package/dist/entries/logging/requestLoggingMiddleware.js +2 -25
- package/dist/entries/queue/createAppQueue.js +4 -2324
- package/dist/entries/queue/failedJobRepository.js +4 -2324
- package/dist/entries/queue/publicQueue.js +4 -2324
- package/dist/entries/queue/queueMetrics.js +4 -2324
- package/dist/entries/security/safeFetch.js +1 -68
- package/dist/entries/security/safeUrl.js +1 -68
- package/dist/entries/security/stripeWebhook.js +1 -68
- package/dist/entries/tenant/databaseTenantContext.js +3 -105
- package/dist/entries/tenant/tenantDatabaseScope.js +3 -58
- package/dist/entries/validation/rules.js +1 -66
- package/dist/entries/view.js +5 -16
- package/package.json +2 -2
|
@@ -1,69 +1,6 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
// ../../src/core/
|
|
3
|
-
|
|
4
|
-
status;
|
|
5
|
-
details;
|
|
6
|
-
constructor(status, message, details) {
|
|
7
|
-
super(message);
|
|
8
|
-
this.name = new.target.name;
|
|
9
|
-
this.status = status;
|
|
10
|
-
this.details = details;
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
class BadRequestError extends HttpError {
|
|
15
|
-
constructor(message = "Bad Request", details) {
|
|
16
|
-
super(400, message, details);
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
class NotFoundError extends HttpError {
|
|
21
|
-
constructor(message = "Not Found", details) {
|
|
22
|
-
super(404, message, details);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
class ConflictError extends HttpError {
|
|
27
|
-
constructor(message = "Conflict", details) {
|
|
28
|
-
super(409, message, details);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
class UnprocessableEntityError extends HttpError {
|
|
33
|
-
constructor(message = "Unprocessable Entity", details) {
|
|
34
|
-
super(422, message, details);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
class ValidationError extends HttpError {
|
|
39
|
-
constructor(message = "Validation failed", details) {
|
|
40
|
-
super(422, message, details);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
class ForbiddenError extends HttpError {
|
|
45
|
-
constructor(message = "Forbidden", details) {
|
|
46
|
-
super(403, message, details);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
class UnauthorizedError extends HttpError {
|
|
51
|
-
constructor(message = "Unauthorized", details) {
|
|
52
|
-
super(401, message, details);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
class PayloadTooLargeError extends HttpError {
|
|
57
|
-
constructor(message = "Payload Too Large", details) {
|
|
58
|
-
super(413, message, details);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
class PreconditionFailedError extends HttpError {
|
|
63
|
-
constructor(message = "Precondition Failed", details) {
|
|
64
|
-
super(412, message, details);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
2
|
+
// ../../src/core/http/webFormRequest.ts
|
|
3
|
+
import { ForbiddenError, ValidationError } from "@getstrata/core/errors/http";
|
|
67
4
|
|
|
68
5
|
// ../../src/core/http/contentNegotiation.ts
|
|
69
6
|
function requestPrefersJson(request) {
|
|
@@ -89,6 +26,7 @@ function requestPrefersJson(request) {
|
|
|
89
26
|
}
|
|
90
27
|
|
|
91
28
|
// ../../src/core/http/parseFormBody.ts
|
|
29
|
+
import { BadRequestError } from "@getstrata/core/errors/http";
|
|
92
30
|
async function parseFormBody(request) {
|
|
93
31
|
const contentType = request.headers.get("content-type")?.toLowerCase() ?? "";
|
|
94
32
|
if (!contentType.includes("application/x-www-form-urlencoded") && !contentType.includes("multipart/form-data")) {
|
|
@@ -106,49 +44,10 @@ function formDataToRecord(formData) {
|
|
|
106
44
|
return values;
|
|
107
45
|
}
|
|
108
46
|
|
|
109
|
-
// ../../src/core/runtime/asyncContextStore.ts
|
|
110
|
-
import { AsyncLocalStorage } from "async_hooks";
|
|
111
|
-
function createAsyncContextStore(key) {
|
|
112
|
-
const symbol = Symbol.for(key);
|
|
113
|
-
const globalRecord = globalThis;
|
|
114
|
-
const existing = globalRecord[symbol];
|
|
115
|
-
if (existing) {
|
|
116
|
-
return existing;
|
|
117
|
-
}
|
|
118
|
-
const store = new AsyncLocalStorage;
|
|
119
|
-
globalRecord[symbol] = store;
|
|
120
|
-
return store;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// ../../src/core/auth/authContext.ts
|
|
124
|
-
var authContext = createAsyncContextStore("@getstrata/authContext");
|
|
125
|
-
function runWithAuthUser(user, callback) {
|
|
126
|
-
return authContext.run(user, callback);
|
|
127
|
-
}
|
|
128
|
-
function currentAuthUser() {
|
|
129
|
-
return authContext.getStore() ?? null;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// ../../src/core/tenant/tenantContext.ts
|
|
133
|
-
var tenantContext = createAsyncContextStore("@getstrata/tenantContext");
|
|
134
|
-
function runWithTenant(tenant, callback) {
|
|
135
|
-
return tenantContext.run(tenant, callback);
|
|
136
|
-
}
|
|
137
|
-
function currentTenant() {
|
|
138
|
-
return tenantContext.getStore() ?? null;
|
|
139
|
-
}
|
|
140
|
-
function rateLimitMultiplierForPlan(plan) {
|
|
141
|
-
switch (plan) {
|
|
142
|
-
case "enterprise":
|
|
143
|
-
return 4;
|
|
144
|
-
case "pro":
|
|
145
|
-
return 2;
|
|
146
|
-
default:
|
|
147
|
-
return 1;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
47
|
// ../../src/core/http/validation.ts
|
|
48
|
+
import { currentAuthUser } from "@getstrata/core/auth/authContext";
|
|
49
|
+
import { BadRequestError as BadRequestError2 } from "@getstrata/core/errors/http";
|
|
50
|
+
import { currentTenantId } from "@getstrata/core/tenant/tenantContext";
|
|
152
51
|
function getQueryParams(request) {
|
|
153
52
|
if (!request) {
|
|
154
53
|
return new URLSearchParams;
|
|
@@ -160,14 +59,14 @@ async function parseJsonBody(request, validator) {
|
|
|
160
59
|
try {
|
|
161
60
|
payload = await request.json();
|
|
162
61
|
} catch {
|
|
163
|
-
throw new
|
|
62
|
+
throw new BadRequestError2("Request body must be valid JSON.");
|
|
164
63
|
}
|
|
165
64
|
return validator(payload);
|
|
166
65
|
}
|
|
167
66
|
function parsePositiveIntParam(value, name = "id") {
|
|
168
67
|
const parsed = Number.parseInt(value, 10);
|
|
169
68
|
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
170
|
-
throw new
|
|
69
|
+
throw new BadRequestError2(`Invalid ${name}. Expected a positive integer.`);
|
|
171
70
|
}
|
|
172
71
|
return parsed;
|
|
173
72
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// ../../src/core/jobs/dispatchWebhookJob.ts
|
|
3
3
|
import { createHmac } from "crypto";
|
|
4
|
+
import { repositoryConnection as db } from "@getstrata/core/database/repositoryConnection";
|
|
4
5
|
|
|
5
6
|
// ../../src/config/app.ts
|
|
6
7
|
var appConfig = {
|
|
@@ -11,109 +12,6 @@ var appConfig = {
|
|
|
11
12
|
apiPrefix: process.env.API_PREFIX ?? "/api/v1"
|
|
12
13
|
};
|
|
13
14
|
|
|
14
|
-
// ../../src/core/database/boundConnection.ts
|
|
15
|
-
var boundConnectionHolder = {
|
|
16
|
-
connection: null
|
|
17
|
-
};
|
|
18
|
-
function bindDatabaseConnection(connection) {
|
|
19
|
-
boundConnectionHolder.connection = connection;
|
|
20
|
-
}
|
|
21
|
-
function getBoundDatabaseConnection() {
|
|
22
|
-
return boundConnectionHolder.connection;
|
|
23
|
-
}
|
|
24
|
-
function resetBoundDatabaseConnection() {
|
|
25
|
-
boundConnectionHolder.connection = null;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// ../../src/core/runtime/asyncContextStore.ts
|
|
29
|
-
import { AsyncLocalStorage } from "async_hooks";
|
|
30
|
-
function createAsyncContextStore(key) {
|
|
31
|
-
const symbol = Symbol.for(key);
|
|
32
|
-
const globalRecord = globalThis;
|
|
33
|
-
const existing = globalRecord[symbol];
|
|
34
|
-
if (existing) {
|
|
35
|
-
return existing;
|
|
36
|
-
}
|
|
37
|
-
const store = new AsyncLocalStorage;
|
|
38
|
-
globalRecord[symbol] = store;
|
|
39
|
-
return store;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// ../../src/core/database/connectionContext.ts
|
|
43
|
-
var activeConnection = createAsyncContextStore("@getstrata/databaseConnectionContext");
|
|
44
|
-
function runWithDatabaseConnection(connection, callback) {
|
|
45
|
-
return activeConnection.run(connection, callback);
|
|
46
|
-
}
|
|
47
|
-
function getActiveDatabaseConnection(fallback) {
|
|
48
|
-
return activeConnection.getStore() ?? fallback;
|
|
49
|
-
}
|
|
50
|
-
function hasActiveDatabaseConnection() {
|
|
51
|
-
return activeConnection.getStore() !== undefined;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// ../../src/core/database/queryProxy.ts
|
|
55
|
-
var POOL_CONNECTION_METHODS = new Set(["begin", "close", "connect"]);
|
|
56
|
-
function createDatabaseQueryProxy(pool) {
|
|
57
|
-
function resolveDatabase() {
|
|
58
|
-
return getActiveDatabaseConnection(pool);
|
|
59
|
-
}
|
|
60
|
-
function resolveDatabaseForProperty(property) {
|
|
61
|
-
if (typeof property === "string" && POOL_CONNECTION_METHODS.has(property)) {
|
|
62
|
-
return pool;
|
|
63
|
-
}
|
|
64
|
-
return resolveDatabase();
|
|
65
|
-
}
|
|
66
|
-
return new Proxy(function database() {}, {
|
|
67
|
-
apply(_target, _thisArg, args) {
|
|
68
|
-
return resolveDatabase()(...args);
|
|
69
|
-
},
|
|
70
|
-
get(_target, property) {
|
|
71
|
-
const connection = resolveDatabaseForProperty(property);
|
|
72
|
-
const value = connection[property];
|
|
73
|
-
return typeof value === "function" ? value.bind(connection) : value;
|
|
74
|
-
}
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// ../../src/core/database/defaultConnection.ts
|
|
79
|
-
var defaultPool = {
|
|
80
|
-
connection: null
|
|
81
|
-
};
|
|
82
|
-
var defaultQuery = {
|
|
83
|
-
connection: null
|
|
84
|
-
};
|
|
85
|
-
function registerDefaultDatabasePool(connection) {
|
|
86
|
-
defaultPool.connection = connection;
|
|
87
|
-
defaultQuery.connection = createDatabaseQueryProxy(connection);
|
|
88
|
-
}
|
|
89
|
-
function getDefaultDatabasePool() {
|
|
90
|
-
if (!defaultPool.connection) {
|
|
91
|
-
throw new Error("Default database pool is not registered. Call registerDefaultDatabasePool() during app bootstrap.");
|
|
92
|
-
}
|
|
93
|
-
return defaultPool.connection;
|
|
94
|
-
}
|
|
95
|
-
function getDefaultDatabaseQuery() {
|
|
96
|
-
if (!defaultQuery.connection) {
|
|
97
|
-
throw new Error("Default database query handle is not registered. Call registerDefaultDatabasePool() during app bootstrap.");
|
|
98
|
-
}
|
|
99
|
-
return defaultQuery.connection;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// ../../src/core/database/repositoryConnection.ts
|
|
103
|
-
function resolveRepositoryConnection() {
|
|
104
|
-
return getBoundDatabaseConnection() ?? getDefaultDatabaseQuery();
|
|
105
|
-
}
|
|
106
|
-
var repositoryConnection = new Proxy(function repositoryConnection2() {}, {
|
|
107
|
-
apply(_target, _thisArg, args) {
|
|
108
|
-
return resolveRepositoryConnection()(...args);
|
|
109
|
-
},
|
|
110
|
-
get(_target, property) {
|
|
111
|
-
const connection = resolveRepositoryConnection();
|
|
112
|
-
const value = connection[property];
|
|
113
|
-
return typeof value === "function" ? value.bind(connection) : value;
|
|
114
|
-
}
|
|
115
|
-
});
|
|
116
|
-
|
|
117
15
|
// ../../src/core/queue/index.ts
|
|
118
16
|
class Job {
|
|
119
17
|
maxAttempts;
|
|
@@ -142,74 +40,7 @@ function createQueue(driver) {
|
|
|
142
40
|
|
|
143
41
|
// ../../src/core/security/safeUrl.ts
|
|
144
42
|
import { lookup as dnsLookupImpl } from "dns/promises";
|
|
145
|
-
|
|
146
|
-
// ../../src/core/errors/http.ts
|
|
147
|
-
class HttpError extends Error {
|
|
148
|
-
status;
|
|
149
|
-
details;
|
|
150
|
-
constructor(status, message, details) {
|
|
151
|
-
super(message);
|
|
152
|
-
this.name = new.target.name;
|
|
153
|
-
this.status = status;
|
|
154
|
-
this.details = details;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
class BadRequestError extends HttpError {
|
|
159
|
-
constructor(message = "Bad Request", details) {
|
|
160
|
-
super(400, message, details);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
class NotFoundError extends HttpError {
|
|
165
|
-
constructor(message = "Not Found", details) {
|
|
166
|
-
super(404, message, details);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
class ConflictError extends HttpError {
|
|
171
|
-
constructor(message = "Conflict", details) {
|
|
172
|
-
super(409, message, details);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
class UnprocessableEntityError extends HttpError {
|
|
177
|
-
constructor(message = "Unprocessable Entity", details) {
|
|
178
|
-
super(422, message, details);
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
class ValidationError extends HttpError {
|
|
183
|
-
constructor(message = "Validation failed", details) {
|
|
184
|
-
super(422, message, details);
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
class ForbiddenError extends HttpError {
|
|
189
|
-
constructor(message = "Forbidden", details) {
|
|
190
|
-
super(403, message, details);
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
class UnauthorizedError extends HttpError {
|
|
195
|
-
constructor(message = "Unauthorized", details) {
|
|
196
|
-
super(401, message, details);
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
class PayloadTooLargeError extends HttpError {
|
|
201
|
-
constructor(message = "Payload Too Large", details) {
|
|
202
|
-
super(413, message, details);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
class PreconditionFailedError extends HttpError {
|
|
207
|
-
constructor(message = "Precondition Failed", details) {
|
|
208
|
-
super(412, message, details);
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
// ../../src/core/security/safeUrl.ts
|
|
43
|
+
import { BadRequestError } from "@getstrata/core/errors/http";
|
|
213
44
|
var dnsLookup = dnsLookupImpl;
|
|
214
45
|
var BLOCKED_HOSTNAMES = new Set([
|
|
215
46
|
"localhost",
|
|
@@ -343,7 +174,7 @@ class DispatchWebhookJob extends Job {
|
|
|
343
174
|
maxAttempts = 3;
|
|
344
175
|
backoffMs = 2000;
|
|
345
176
|
async handle(payload) {
|
|
346
|
-
const rows = await
|
|
177
|
+
const rows = await db`
|
|
347
178
|
SELECT id, url, secret
|
|
348
179
|
FROM webhook
|
|
349
180
|
WHERE id = ${payload.webhookId} AND active = TRUE
|
|
@@ -373,7 +204,7 @@ class DispatchWebhookJob extends Job {
|
|
|
373
204
|
}
|
|
374
205
|
} catch (error) {
|
|
375
206
|
errorMessage = error instanceof Error ? error.message : String(error);
|
|
376
|
-
await
|
|
207
|
+
await db`
|
|
377
208
|
INSERT INTO webhook_delivery (webhook_id, event, payload, response_status, error)
|
|
378
209
|
VALUES (
|
|
379
210
|
${webhook.id},
|
|
@@ -385,7 +216,7 @@ class DispatchWebhookJob extends Job {
|
|
|
385
216
|
`;
|
|
386
217
|
throw error instanceof Error ? error : new Error(errorMessage);
|
|
387
218
|
}
|
|
388
|
-
await
|
|
219
|
+
await db`
|
|
389
220
|
INSERT INTO webhook_delivery (webhook_id, event, payload, response_status)
|
|
390
221
|
VALUES (
|
|
391
222
|
${webhook.id},
|
|
@@ -1,29 +1,6 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
// ../../src/core/
|
|
3
|
-
import {
|
|
4
|
-
function createAsyncContextStore(key) {
|
|
5
|
-
const symbol = Symbol.for(key);
|
|
6
|
-
const globalRecord = globalThis;
|
|
7
|
-
const existing = globalRecord[symbol];
|
|
8
|
-
if (existing) {
|
|
9
|
-
return existing;
|
|
10
|
-
}
|
|
11
|
-
const store = new AsyncLocalStorage;
|
|
12
|
-
globalRecord[symbol] = store;
|
|
13
|
-
return store;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
// ../../src/core/http/requestMetaContext.ts
|
|
17
|
-
var requestMetaContext = createAsyncContextStore("@getstrata/requestMetaContext");
|
|
18
|
-
function runWithRequestMeta(meta, callback) {
|
|
19
|
-
return requestMetaContext.run(meta, callback);
|
|
20
|
-
}
|
|
21
|
-
function currentRequestMeta() {
|
|
22
|
-
return requestMetaContext.getStore() ?? {
|
|
23
|
-
ipAddress: null,
|
|
24
|
-
userAgent: null
|
|
25
|
-
};
|
|
26
|
-
}
|
|
2
|
+
// ../../src/core/logging/requestLoggingMiddleware.ts
|
|
3
|
+
import { runWithRequestMeta } from "@getstrata/core/http/requestMetaContext";
|
|
27
4
|
|
|
28
5
|
// ../../src/core/logging/logger.ts
|
|
29
6
|
class Logger {
|