@getstrata/core 0.5.48 → 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 +25 -0
- 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 -96
- package/dist/entries/auth/sessionGuard.js +138 -2732
- 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 -110
- package/dist/entries/http/pagination.js +8 -113
- 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 -111
- package/dist/entries/http/securedRouteModelBinding.js +15 -206
- package/dist/entries/http/throttleMiddleware.js +2 -47
- package/dist/entries/http/webErrorResponse.js +222 -2732
- package/dist/entries/http/webFormRequest.js +8 -112
- package/dist/entries/jobs/dispatchWebhookJob.js +5 -174
- package/dist/entries/logging/requestLoggingMiddleware.js +2 -25
- package/dist/entries/queue/createAppQueue.js +13 -2332
- package/dist/entries/queue/failedJobRepository.js +4 -2324
- package/dist/entries/queue/jobRunner.js +9 -8
- package/dist/entries/queue/publicQueue.js +13 -2332
- package/dist/entries/queue/queueMetrics.js +13 -2332
- package/dist/entries/queue/redisQueue.js +9 -8
- 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 -61
- package/dist/entries/validation/rules.js +1 -66
- package/dist/entries/view.js +159 -2735
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -44,6 +44,31 @@ Exports for admin dashboards and queue recovery:
|
|
|
44
44
|
```bash
|
|
45
45
|
bun run build:framework
|
|
46
46
|
bun run verify:framework # build + public API tests
|
|
47
|
+
bun run verify:shared-subpaths # after build: confirm singleton shims
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Subpath imports
|
|
51
|
+
|
|
52
|
+
`@getstrata/core` publishes **139+ subpaths** (for example `@getstrata/core/http/authMiddleware`,
|
|
53
|
+
`@getstrata/core/database/migrations`). Prefer subpaths over the root import in apps, bootstrap, and tests.
|
|
54
|
+
|
|
55
|
+
Some subpaths **re-export the main bundle** so singleton state stays shared (database pool binding,
|
|
56
|
+
`AsyncLocalStorage` auth/tenant context, global registries, `HttpError` / `Notification` classes for
|
|
57
|
+
`instanceof`). The canonical list lives in `scripts/core-shared-subpaths.ts` and is verified by
|
|
58
|
+
`scripts/verify-core-shared-subpaths.ts` after each framework build.
|
|
59
|
+
|
|
60
|
+
When adding a subpath that owns process-wide state or base classes used with `instanceof`, append it to
|
|
61
|
+
`CORE_SHARED_SUBPATHS`, run `bun scripts/sync-package-subpaths.ts`, and rebuild. Non-shared subpath
|
|
62
|
+
bundles are built with generated `--external @getstrata/core/*` flags so framework source can import
|
|
63
|
+
shared modules via package self-imports (`scripts/codemod-core-self-imports.ts`).
|
|
64
|
+
`scripts/verify-bundled-subpaths.ts` ensures entries like `http/webFormRequest` do not inline
|
|
65
|
+
`ValidationError`.
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
import { createAuthMiddleware } from "@getstrata/core/http/authMiddleware";
|
|
69
|
+
import { bindDatabaseConnection } from "@getstrata/core/database/bindConnection";
|
|
70
|
+
import { ValidationError } from "@getstrata/core/errors/http";
|
|
71
|
+
import type { Migration } from "@getstrata/core/database/migrations/types";
|
|
47
72
|
```
|
|
48
73
|
|
|
49
74
|
## Publish to npm
|
|
@@ -2,7 +2,7 @@ declare function cache(): import("../../types/services").CacheLike;
|
|
|
2
2
|
declare function auth(): import("../auth/guard").AuthManager;
|
|
3
3
|
declare function policyGate(): import("../auth/policy").PolicyGate;
|
|
4
4
|
declare function queue(): import("../queue").Queue;
|
|
5
|
-
declare function events(): import("
|
|
5
|
+
declare function events(): import("@getstrata/core/events").EventBus;
|
|
6
6
|
declare function config<T>(key: string): T | undefined;
|
|
7
7
|
declare function log(): import("../logging/logger").Logger;
|
|
8
8
|
declare function mail(): import("../mail/mailer").Mailer;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { type AuthUser, authContext } from "
|
|
2
|
-
import type { AuthManager } from "
|
|
1
|
+
import { type AuthUser, authContext } from "@getstrata/core/auth/authContext";
|
|
2
|
+
import type { AuthManager } from "@getstrata/core/auth/guard";
|
|
3
3
|
declare function createAuthMiddleware(auth: AuthManager): (request: Request, next: () => Promise<Response>) => Promise<Response>;
|
|
4
4
|
export type { AuthUser };
|
|
5
5
|
export { authContext, createAuthMiddleware };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { AuthManager } from "
|
|
2
|
-
import type { Policy, PolicyGate } from "
|
|
1
|
+
import type { AuthManager } from "@getstrata/core/auth/guard";
|
|
2
|
+
import type { Policy, PolicyGate } from "@getstrata/core/auth/policy";
|
|
3
3
|
import type { Middleware } from "./middleware";
|
|
4
4
|
declare function createAuthorizeMiddleware(gate: PolicyGate, auth: AuthManager, resource: string, action: keyof Policy): Middleware;
|
|
5
5
|
export { createAuthorizeMiddleware };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { BadRequestError, ConflictError, ForbiddenError, HttpError, NotFoundError, PayloadTooLargeError, PreconditionFailedError, UnauthorizedError, UnprocessableEntityError, ValidationError, } from "
|
|
1
|
+
export { BadRequestError, ConflictError, ForbiddenError, HttpError, NotFoundError, PayloadTooLargeError, PreconditionFailedError, UnauthorizedError, UnprocessableEntityError, ValidationError, } from "@getstrata/core/errors/http";
|
|
2
2
|
export type { PaginatedResult, PaginationMeta } from "../pagination";
|
|
3
3
|
export { createAuthMiddleware } from "./authMiddleware";
|
|
4
4
|
export { createAuthorizeMiddleware } from "./authorizeMiddleware";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AuthManager } from "
|
|
1
|
+
import type { AuthManager } from "@getstrata/core/auth/guard";
|
|
2
2
|
import type { Middleware } from "./middleware";
|
|
3
3
|
declare function createRequireAuthMiddleware(auth: AuthManager): Middleware;
|
|
4
4
|
export { createRequireAuthMiddleware };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AuthManager } from "
|
|
1
|
+
import type { AuthManager } from "@getstrata/core/auth/guard";
|
|
2
2
|
import type { Middleware } from "./middleware";
|
|
3
3
|
declare function createRequireWebAuthMiddleware(auth: AuthManager): Middleware;
|
|
4
4
|
export { createRequireWebAuthMiddleware };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { FailedJobRecord } from "./types";
|
|
2
|
-
declare const failedJobTable: import("
|
|
2
|
+
declare const failedJobTable: import("@getstrata/core/database").TableDefinition<FailedJobRecord, "id">;
|
|
3
3
|
export { failedJobTable };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import type { AuthManager } from "@getstrata/core/auth/guard";
|
|
2
|
+
import type { PolicyGate } from "@getstrata/core/auth/policy";
|
|
3
|
+
import type { EventBus } from "@getstrata/core/events";
|
|
1
4
|
import type { CacheLike } from "../../types/services";
|
|
2
|
-
import type { AuthManager } from "../auth/guard";
|
|
3
|
-
import type { PolicyGate } from "../auth/policy";
|
|
4
5
|
import type { AppContext } from "../contracts/di";
|
|
5
|
-
import type { EventBus } from "../events";
|
|
6
6
|
import { type Logger } from "../logging/logger";
|
|
7
7
|
import type { Queue } from "../queue";
|
|
8
8
|
declare function setActiveApplicationContext(context: AppContext): void;
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
// @bun
|
|
2
|
+
// ../../src/core/audit/exportAuditLogs.ts
|
|
3
|
+
import { repositoryConnection as db2 } from "@getstrata/core/database/repositoryConnection";
|
|
4
|
+
|
|
2
5
|
// ../../src/config/app.ts
|
|
3
6
|
var appConfig = {
|
|
4
7
|
name: "WorkHub",
|
|
@@ -8,179 +11,9 @@ var appConfig = {
|
|
|
8
11
|
apiPrefix: process.env.API_PREFIX ?? "/api/v1"
|
|
9
12
|
};
|
|
10
13
|
|
|
11
|
-
// ../../src/core/database/boundConnection.ts
|
|
12
|
-
var boundConnectionHolder = {
|
|
13
|
-
connection: null
|
|
14
|
-
};
|
|
15
|
-
function bindDatabaseConnection(connection) {
|
|
16
|
-
boundConnectionHolder.connection = connection;
|
|
17
|
-
}
|
|
18
|
-
function getBoundDatabaseConnection() {
|
|
19
|
-
return boundConnectionHolder.connection;
|
|
20
|
-
}
|
|
21
|
-
function resetBoundDatabaseConnection() {
|
|
22
|
-
boundConnectionHolder.connection = null;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// ../../src/core/runtime/asyncContextStore.ts
|
|
26
|
-
import { AsyncLocalStorage } from "async_hooks";
|
|
27
|
-
function createAsyncContextStore(key) {
|
|
28
|
-
const symbol = Symbol.for(key);
|
|
29
|
-
const globalRecord = globalThis;
|
|
30
|
-
const existing = globalRecord[symbol];
|
|
31
|
-
if (existing) {
|
|
32
|
-
return existing;
|
|
33
|
-
}
|
|
34
|
-
const store = new AsyncLocalStorage;
|
|
35
|
-
globalRecord[symbol] = store;
|
|
36
|
-
return store;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// ../../src/core/database/connectionContext.ts
|
|
40
|
-
var activeConnection = createAsyncContextStore("@getstrata/databaseConnectionContext");
|
|
41
|
-
function runWithDatabaseConnection(connection, callback) {
|
|
42
|
-
return activeConnection.run(connection, callback);
|
|
43
|
-
}
|
|
44
|
-
function getActiveDatabaseConnection(fallback) {
|
|
45
|
-
return activeConnection.getStore() ?? fallback;
|
|
46
|
-
}
|
|
47
|
-
function hasActiveDatabaseConnection() {
|
|
48
|
-
return activeConnection.getStore() !== undefined;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// ../../src/core/database/queryProxy.ts
|
|
52
|
-
var POOL_CONNECTION_METHODS = new Set(["begin", "close", "connect"]);
|
|
53
|
-
function createDatabaseQueryProxy(pool) {
|
|
54
|
-
function resolveDatabase() {
|
|
55
|
-
return getActiveDatabaseConnection(pool);
|
|
56
|
-
}
|
|
57
|
-
function resolveDatabaseForProperty(property) {
|
|
58
|
-
if (typeof property === "string" && POOL_CONNECTION_METHODS.has(property)) {
|
|
59
|
-
return pool;
|
|
60
|
-
}
|
|
61
|
-
return resolveDatabase();
|
|
62
|
-
}
|
|
63
|
-
return new Proxy(function database() {}, {
|
|
64
|
-
apply(_target, _thisArg, args) {
|
|
65
|
-
return resolveDatabase()(...args);
|
|
66
|
-
},
|
|
67
|
-
get(_target, property) {
|
|
68
|
-
const connection = resolveDatabaseForProperty(property);
|
|
69
|
-
const value = connection[property];
|
|
70
|
-
return typeof value === "function" ? value.bind(connection) : value;
|
|
71
|
-
}
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// ../../src/core/database/defaultConnection.ts
|
|
76
|
-
var defaultPool = {
|
|
77
|
-
connection: null
|
|
78
|
-
};
|
|
79
|
-
var defaultQuery = {
|
|
80
|
-
connection: null
|
|
81
|
-
};
|
|
82
|
-
function registerDefaultDatabasePool(connection) {
|
|
83
|
-
defaultPool.connection = connection;
|
|
84
|
-
defaultQuery.connection = createDatabaseQueryProxy(connection);
|
|
85
|
-
}
|
|
86
|
-
function getDefaultDatabasePool() {
|
|
87
|
-
if (!defaultPool.connection) {
|
|
88
|
-
throw new Error("Default database pool is not registered. Call registerDefaultDatabasePool() during app bootstrap.");
|
|
89
|
-
}
|
|
90
|
-
return defaultPool.connection;
|
|
91
|
-
}
|
|
92
|
-
function getDefaultDatabaseQuery() {
|
|
93
|
-
if (!defaultQuery.connection) {
|
|
94
|
-
throw new Error("Default database query handle is not registered. Call registerDefaultDatabasePool() during app bootstrap.");
|
|
95
|
-
}
|
|
96
|
-
return defaultQuery.connection;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// ../../src/core/database/repositoryConnection.ts
|
|
100
|
-
function resolveRepositoryConnection() {
|
|
101
|
-
return getBoundDatabaseConnection() ?? getDefaultDatabaseQuery();
|
|
102
|
-
}
|
|
103
|
-
var repositoryConnection = new Proxy(function repositoryConnection2() {}, {
|
|
104
|
-
apply(_target, _thisArg, args) {
|
|
105
|
-
return resolveRepositoryConnection()(...args);
|
|
106
|
-
},
|
|
107
|
-
get(_target, property) {
|
|
108
|
-
const connection = resolveRepositoryConnection();
|
|
109
|
-
const value = connection[property];
|
|
110
|
-
return typeof value === "function" ? value.bind(connection) : value;
|
|
111
|
-
}
|
|
112
|
-
});
|
|
113
|
-
|
|
114
14
|
// ../../src/core/security/safeUrl.ts
|
|
115
15
|
import { lookup as dnsLookupImpl } from "dns/promises";
|
|
116
|
-
|
|
117
|
-
// ../../src/core/errors/http.ts
|
|
118
|
-
class HttpError extends Error {
|
|
119
|
-
status;
|
|
120
|
-
details;
|
|
121
|
-
constructor(status, message, details) {
|
|
122
|
-
super(message);
|
|
123
|
-
this.name = new.target.name;
|
|
124
|
-
this.status = status;
|
|
125
|
-
this.details = details;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
class BadRequestError extends HttpError {
|
|
130
|
-
constructor(message = "Bad Request", details) {
|
|
131
|
-
super(400, message, details);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
class NotFoundError extends HttpError {
|
|
136
|
-
constructor(message = "Not Found", details) {
|
|
137
|
-
super(404, message, details);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
class ConflictError extends HttpError {
|
|
142
|
-
constructor(message = "Conflict", details) {
|
|
143
|
-
super(409, message, details);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
class UnprocessableEntityError extends HttpError {
|
|
148
|
-
constructor(message = "Unprocessable Entity", details) {
|
|
149
|
-
super(422, message, details);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
class ValidationError extends HttpError {
|
|
154
|
-
constructor(message = "Validation failed", details) {
|
|
155
|
-
super(422, message, details);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
class ForbiddenError extends HttpError {
|
|
160
|
-
constructor(message = "Forbidden", details) {
|
|
161
|
-
super(403, message, details);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
class UnauthorizedError extends HttpError {
|
|
166
|
-
constructor(message = "Unauthorized", details) {
|
|
167
|
-
super(401, message, details);
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
class PayloadTooLargeError extends HttpError {
|
|
172
|
-
constructor(message = "Payload Too Large", details) {
|
|
173
|
-
super(413, message, details);
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
class PreconditionFailedError extends HttpError {
|
|
178
|
-
constructor(message = "Precondition Failed", details) {
|
|
179
|
-
super(412, message, details);
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// ../../src/core/security/safeUrl.ts
|
|
16
|
+
import { BadRequestError } from "@getstrata/core/errors/http";
|
|
184
17
|
var dnsLookup = dnsLookupImpl;
|
|
185
18
|
var BLOCKED_HOSTNAMES = new Set([
|
|
186
19
|
"localhost",
|
|
@@ -310,12 +143,13 @@ async function safeFetch(input, init = {}, options = {}) {
|
|
|
310
143
|
}
|
|
311
144
|
|
|
312
145
|
// ../../src/core/tenant/databaseTenantContext.ts
|
|
146
|
+
import { repositoryConnection as db } from "@getstrata/core/database/repositoryConnection";
|
|
313
147
|
async function runWithMigrationBypass(callback) {
|
|
314
|
-
await
|
|
148
|
+
await db`SELECT set_config('app.bypass_rls', 'true', false)`;
|
|
315
149
|
try {
|
|
316
150
|
return await callback();
|
|
317
151
|
} finally {
|
|
318
|
-
await
|
|
152
|
+
await db`SELECT set_config('app.bypass_rls', 'false', false)`;
|
|
319
153
|
}
|
|
320
154
|
}
|
|
321
155
|
|
|
@@ -372,7 +206,7 @@ async function exportPendingAuditLogs() {
|
|
|
372
206
|
return 0;
|
|
373
207
|
}
|
|
374
208
|
return await runWithMigrationBypass(async () => {
|
|
375
|
-
const rows = await
|
|
209
|
+
const rows = await db2`
|
|
376
210
|
SELECT
|
|
377
211
|
id,
|
|
378
212
|
user_id,
|
|
@@ -422,7 +256,7 @@ async function exportPendingAuditLogs() {
|
|
|
422
256
|
}
|
|
423
257
|
const ids = rows.map((row) => row.id);
|
|
424
258
|
for (const id of ids) {
|
|
425
|
-
await
|
|
259
|
+
await db2`UPDATE audit_log SET exported_at = NOW() WHERE id = ${id}`;
|
|
426
260
|
}
|
|
427
261
|
return rows.length;
|
|
428
262
|
});
|
|
@@ -73,12 +73,6 @@ function registerDefaultDatabasePool(connection) {
|
|
|
73
73
|
defaultPool.connection = connection;
|
|
74
74
|
defaultQuery.connection = createDatabaseQueryProxy(connection);
|
|
75
75
|
}
|
|
76
|
-
function getDefaultDatabasePool() {
|
|
77
|
-
if (!defaultPool.connection) {
|
|
78
|
-
throw new Error("Default database pool is not registered. Call registerDefaultDatabasePool() during app bootstrap.");
|
|
79
|
-
}
|
|
80
|
-
return defaultPool.connection;
|
|
81
|
-
}
|
|
82
76
|
function getDefaultDatabaseQuery() {
|
|
83
77
|
if (!defaultQuery.connection) {
|
|
84
78
|
throw new Error("Default database query handle is not registered. Call registerDefaultDatabasePool() during app bootstrap.");
|
|
@@ -179,77 +173,11 @@ class OrganizationMemberRepository {
|
|
|
179
173
|
}
|
|
180
174
|
var memberRepository_default = OrganizationMemberRepository;
|
|
181
175
|
|
|
182
|
-
// ../../src/core/
|
|
183
|
-
|
|
184
|
-
status;
|
|
185
|
-
details;
|
|
186
|
-
constructor(status, message, details) {
|
|
187
|
-
super(message);
|
|
188
|
-
this.name = new.target.name;
|
|
189
|
-
this.status = status;
|
|
190
|
-
this.details = details;
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
class BadRequestError extends HttpError {
|
|
195
|
-
constructor(message = "Bad Request", details) {
|
|
196
|
-
super(400, message, details);
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
class NotFoundError extends HttpError {
|
|
201
|
-
constructor(message = "Not Found", details) {
|
|
202
|
-
super(404, message, details);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
class ConflictError extends HttpError {
|
|
207
|
-
constructor(message = "Conflict", details) {
|
|
208
|
-
super(409, message, details);
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
class UnprocessableEntityError extends HttpError {
|
|
213
|
-
constructor(message = "Unprocessable Entity", details) {
|
|
214
|
-
super(422, message, details);
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
class ValidationError extends HttpError {
|
|
219
|
-
constructor(message = "Validation failed", details) {
|
|
220
|
-
super(422, message, details);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
class ForbiddenError extends HttpError {
|
|
225
|
-
constructor(message = "Forbidden", details) {
|
|
226
|
-
super(403, message, details);
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
class UnauthorizedError extends HttpError {
|
|
231
|
-
constructor(message = "Unauthorized", details) {
|
|
232
|
-
super(401, message, details);
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
class PayloadTooLargeError extends HttpError {
|
|
237
|
-
constructor(message = "Payload Too Large", details) {
|
|
238
|
-
super(413, message, details);
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
class PreconditionFailedError extends HttpError {
|
|
243
|
-
constructor(message = "Precondition Failed", details) {
|
|
244
|
-
super(412, message, details);
|
|
245
|
-
}
|
|
246
|
-
}
|
|
176
|
+
// ../../src/core/auth/accessControl.ts
|
|
177
|
+
import { ForbiddenError } from "@getstrata/core/errors/http";
|
|
247
178
|
|
|
248
179
|
// ../../src/core/auth/authContext.ts
|
|
249
180
|
var authContext = createAsyncContextStore("@getstrata/authContext");
|
|
250
|
-
function runWithAuthUser(user, callback) {
|
|
251
|
-
return authContext.run(user, callback);
|
|
252
|
-
}
|
|
253
181
|
function currentAuthUser() {
|
|
254
182
|
return authContext.getStore() ?? null;
|
|
255
183
|
}
|
|
@@ -1,22 +1,8 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
// ../../src/core/
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
};
|
|
6
|
-
function bindDatabaseConnection(connection) {
|
|
7
|
-
boundConnectionHolder.connection = connection;
|
|
8
|
-
}
|
|
9
|
-
function getBoundDatabaseConnection() {
|
|
10
|
-
return boundConnectionHolder.connection;
|
|
11
|
-
}
|
|
12
|
-
function resetBoundDatabaseConnection() {
|
|
13
|
-
boundConnectionHolder.connection = null;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
// ../../src/core/database/bindConnection.ts
|
|
17
|
-
function bindDatabaseConnection2(connection) {
|
|
18
|
-
bindDatabaseConnection(connection);
|
|
19
|
-
}
|
|
2
|
+
// ../../src/core/auth/scimAuthMiddleware.ts
|
|
3
|
+
import { bindDatabaseConnection } from "@getstrata/core/database/bindConnection";
|
|
4
|
+
import { resetBoundDatabaseConnection } from "@getstrata/core/database/boundConnection";
|
|
5
|
+
import { getDefaultDatabasePool as getDefaultDatabasePool2 } from "@getstrata/core/database/defaultConnection";
|
|
20
6
|
|
|
21
7
|
// ../../src/core/runtime/asyncContextStore.ts
|
|
22
8
|
import { AsyncLocalStorage } from "async_hooks";
|
|
@@ -44,54 +30,6 @@ function hasActiveDatabaseConnection() {
|
|
|
44
30
|
return activeConnection.getStore() !== undefined;
|
|
45
31
|
}
|
|
46
32
|
|
|
47
|
-
// ../../src/core/database/queryProxy.ts
|
|
48
|
-
var POOL_CONNECTION_METHODS = new Set(["begin", "close", "connect"]);
|
|
49
|
-
function createDatabaseQueryProxy(pool) {
|
|
50
|
-
function resolveDatabase() {
|
|
51
|
-
return getActiveDatabaseConnection(pool);
|
|
52
|
-
}
|
|
53
|
-
function resolveDatabaseForProperty(property) {
|
|
54
|
-
if (typeof property === "string" && POOL_CONNECTION_METHODS.has(property)) {
|
|
55
|
-
return pool;
|
|
56
|
-
}
|
|
57
|
-
return resolveDatabase();
|
|
58
|
-
}
|
|
59
|
-
return new Proxy(function database() {}, {
|
|
60
|
-
apply(_target, _thisArg, args) {
|
|
61
|
-
return resolveDatabase()(...args);
|
|
62
|
-
},
|
|
63
|
-
get(_target, property) {
|
|
64
|
-
const connection = resolveDatabaseForProperty(property);
|
|
65
|
-
const value = connection[property];
|
|
66
|
-
return typeof value === "function" ? value.bind(connection) : value;
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// ../../src/core/database/defaultConnection.ts
|
|
72
|
-
var defaultPool = {
|
|
73
|
-
connection: null
|
|
74
|
-
};
|
|
75
|
-
var defaultQuery = {
|
|
76
|
-
connection: null
|
|
77
|
-
};
|
|
78
|
-
function registerDefaultDatabasePool(connection) {
|
|
79
|
-
defaultPool.connection = connection;
|
|
80
|
-
defaultQuery.connection = createDatabaseQueryProxy(connection);
|
|
81
|
-
}
|
|
82
|
-
function getDefaultDatabasePool() {
|
|
83
|
-
if (!defaultPool.connection) {
|
|
84
|
-
throw new Error("Default database pool is not registered. Call registerDefaultDatabasePool() during app bootstrap.");
|
|
85
|
-
}
|
|
86
|
-
return defaultPool.connection;
|
|
87
|
-
}
|
|
88
|
-
function getDefaultDatabaseQuery() {
|
|
89
|
-
if (!defaultQuery.connection) {
|
|
90
|
-
throw new Error("Default database query handle is not registered. Call registerDefaultDatabasePool() during app bootstrap.");
|
|
91
|
-
}
|
|
92
|
-
return defaultQuery.connection;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
33
|
// ../../src/domain/scim.ts
|
|
96
34
|
var TEST_SCIM_BEARER_TOKEN = "workhub-scim-test-token";
|
|
97
35
|
|
|
@@ -139,24 +77,10 @@ function resolveScimTenantFromToken(token) {
|
|
|
139
77
|
return null;
|
|
140
78
|
}
|
|
141
79
|
|
|
142
|
-
// ../../src/core/database/repositoryConnection.ts
|
|
143
|
-
function resolveRepositoryConnection() {
|
|
144
|
-
return getBoundDatabaseConnection() ?? getDefaultDatabaseQuery();
|
|
145
|
-
}
|
|
146
|
-
var repositoryConnection = new Proxy(function repositoryConnection2() {}, {
|
|
147
|
-
apply(_target, _thisArg, args) {
|
|
148
|
-
return resolveRepositoryConnection()(...args);
|
|
149
|
-
},
|
|
150
|
-
get(_target, property) {
|
|
151
|
-
const connection = resolveRepositoryConnection();
|
|
152
|
-
const value = connection[property];
|
|
153
|
-
return typeof value === "function" ? value.bind(connection) : value;
|
|
154
|
-
}
|
|
155
|
-
});
|
|
156
|
-
|
|
157
80
|
// ../../src/core/tenant/resolveTenant.ts
|
|
81
|
+
import { repositoryConnection as db } from "@getstrata/core/database/repositoryConnection";
|
|
158
82
|
async function resolveTenant(tenantId) {
|
|
159
|
-
const rows = await
|
|
83
|
+
const rows = await db`
|
|
160
84
|
SELECT id, slug, plan, region
|
|
161
85
|
FROM tenant
|
|
162
86
|
WHERE id = ${tenantId}
|
|
@@ -166,6 +90,9 @@ async function resolveTenant(tenantId) {
|
|
|
166
90
|
return row ? { id: row.id, slug: row.slug, plan: row.plan, region: row.region ?? "eu" } : null;
|
|
167
91
|
}
|
|
168
92
|
|
|
93
|
+
// ../../src/core/tenant/tenantDatabaseScope.ts
|
|
94
|
+
import { getDefaultDatabasePool } from "@getstrata/core/database/defaultConnection";
|
|
95
|
+
|
|
169
96
|
// ../../src/core/tenant/tenantContext.ts
|
|
170
97
|
var tenantContext = createAsyncContextStore("@getstrata/tenantContext");
|
|
171
98
|
function runWithTenant(tenant, callback) {
|
|
@@ -174,19 +101,6 @@ function runWithTenant(tenant, callback) {
|
|
|
174
101
|
function currentTenant() {
|
|
175
102
|
return tenantContext.getStore() ?? null;
|
|
176
103
|
}
|
|
177
|
-
function currentTenantId() {
|
|
178
|
-
return currentTenant()?.id ?? 1;
|
|
179
|
-
}
|
|
180
|
-
function rateLimitMultiplierForPlan(plan) {
|
|
181
|
-
switch (plan) {
|
|
182
|
-
case "enterprise":
|
|
183
|
-
return 4;
|
|
184
|
-
case "pro":
|
|
185
|
-
return 2;
|
|
186
|
-
default:
|
|
187
|
-
return 1;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
104
|
|
|
191
105
|
// ../../src/core/tenant/tenantDatabaseScope.ts
|
|
192
106
|
async function applyTenantContextToTransaction(transaction, tenantId) {
|
|
@@ -227,7 +141,7 @@ function createScimAuthMiddleware() {
|
|
|
227
141
|
return jsonScimError("SCIM tenant not found.", 401);
|
|
228
142
|
}
|
|
229
143
|
return await runWithTenantDatabase(tenant, async () => {
|
|
230
|
-
|
|
144
|
+
bindDatabaseConnection(getActiveDatabaseConnection(getDefaultDatabasePool2()));
|
|
231
145
|
try {
|
|
232
146
|
return await next();
|
|
233
147
|
} finally {
|