@getstrata/core 0.5.55 → 0.5.57
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 +3 -1
- package/dist/{domain/abilities.d.ts → core/auth/abilityCatalog.d.ts} +9 -1
- package/dist/core/auth/accessControl.d.ts +1 -1
- package/dist/core/auth/membershipContext.d.ts +6 -4
- package/dist/core/auth/membershipService.d.ts +5 -6
- package/dist/core/contracts/authUserDirectory.d.ts +11 -0
- package/dist/core/contracts/membership.d.ts +20 -0
- package/dist/core/http/clientIp.d.ts +3 -0
- package/dist/core/jobs/dispatchWebhookJob.d.ts +2 -0
- package/dist/core/media/imageTransform.d.ts +13 -0
- package/dist/core/scheduler/osCron.d.ts +14 -0
- package/dist/core/tenant/tenancyConfig.d.ts +5 -0
- package/dist/core/tenant/tenantMiddleware.d.ts +1 -1
- package/dist/core/terminal/runShell.d.ts +6 -0
- package/dist/core/view/assertEtaHtmlSource.d.ts +5 -0
- package/dist/core/view/etaViewEngine.d.ts +4 -0
- package/dist/entries/audit/exportAuditLogs.js +2 -2
- package/dist/entries/audit/siemFormatter.js +2 -2
- package/dist/entries/auth/accessControl.js +4 -4
- package/dist/entries/auth/membershipMiddleware.js +21 -161
- package/dist/entries/auth/membershipScope.js +26 -166
- package/dist/entries/auth/membershipService.js +21 -161
- package/dist/entries/auth/oauth/providers.js +2 -2
- package/dist/entries/auth/password.js +2 -2
- package/dist/entries/auth/policy.js +2 -2
- package/dist/entries/auth/scimAuthMiddleware.js +22 -4
- package/dist/entries/auth/sessionCookie.js +4 -4
- package/dist/entries/auth/sessionGuard.js +12 -349
- package/dist/entries/auth/tokenHash.js +2 -2
- package/dist/entries/config/envSchema.js +2 -2
- package/dist/entries/contracts/container.js +2 -2
- package/dist/entries/contracts/di.js +2 -2
- package/dist/entries/contracts/serviceTokens.js +6 -6
- package/dist/entries/crypto/fieldEncryption.js +8 -8
- package/dist/entries/crypto/mfaSecret.js +2 -2
- package/dist/entries/database/errors.js +2 -2
- package/dist/entries/database/migrations.js +8 -8
- package/dist/entries/database/model.js +5 -5
- package/dist/entries/database/query.js +19 -19
- package/dist/entries/database/relationships.js +13 -13
- package/dist/entries/database/schema.js +13 -13
- package/dist/entries/database/seeders.js +2 -2
- package/dist/entries/facades.js +8 -8
- package/dist/entries/http/authMiddleware.js +2 -2
- package/dist/entries/http/bodySizeLimitMiddleware.js +2 -2
- package/dist/entries/http/clientIp.js +19 -0
- package/dist/entries/http/cookies.js +2 -2
- package/dist/entries/http/csrfMiddleware.js +2 -1
- package/dist/entries/http/csrfProtection.js +2 -2
- package/dist/entries/http/csrfToken.js +8 -7
- package/dist/entries/http/etag.js +8 -8
- package/dist/entries/http/flashSession.js +4 -4
- package/dist/entries/http/formRequest.js +2 -2
- package/dist/entries/http/loginThrottleMiddleware.js +20 -3
- package/dist/entries/http/memoryThrottleMiddleware.js +16 -1
- package/dist/entries/http/metricsMiddleware.js +2 -2
- package/dist/entries/http/pagination.js +4 -4
- package/dist/entries/http/parseFormBody.js +2 -2
- package/dist/entries/http/parseMultipartUpload.js +2 -2
- package/dist/entries/http/resources.js +2 -2
- package/dist/entries/http/response.js +214 -395
- package/dist/entries/http/scimThrottleMiddleware.js +40 -1
- package/dist/entries/http/securedRouteModelBinding.js +2 -2
- package/dist/entries/http/throttleMiddleware.js +20 -3
- package/dist/entries/http/validation.js +13 -13
- package/dist/entries/http/webErrorResponse.js +212 -393
- package/dist/entries/jobs/dispatchWebhookJob.js +111 -5
- package/dist/entries/lifecycle/gracefulShutdown.js +3 -3
- package/dist/entries/logging/logger.js +2 -2
- package/dist/entries/logging/requestLoggingMiddleware.js +16 -1
- package/dist/entries/mail/mailer.js +6 -6
- package/dist/entries/mail/markdownMail.js +3 -3
- package/dist/entries/mail/markdownMailable.js +2 -2
- package/dist/entries/media/imageTransform.js +40 -0
- package/dist/entries/metrics/prometheus.js +2 -2
- package/dist/entries/openapi/generator.js +2 -2
- package/dist/entries/queue/createAppQueue.js +4 -4
- package/dist/entries/queue/publicQueue.js +10 -10
- package/dist/entries/queue/queueMetrics.js +2 -2
- package/dist/entries/queue/redisQueue.js +7 -7
- package/dist/entries/queue.js +3 -3
- package/dist/entries/scheduler/osCron.js +27 -0
- package/dist/entries/scheduler/schedule.js +2 -2
- package/dist/entries/security/oauthState.js +3 -3
- package/dist/entries/security/publicReads.js +2 -2
- package/dist/entries/security/safeFetch.js +2 -2
- package/dist/entries/security/safeUrl.js +5 -5
- package/dist/entries/security/scimTenantTokens.js +3 -6
- package/dist/entries/security/totp.js +2 -2
- package/dist/entries/storage/storage.js +7 -7
- package/dist/entries/tenant/tenancyConfig.js +12 -0
- package/dist/entries/tenant/tenantDatabaseScope.js +13 -2
- package/dist/entries/terminal/runShell.js +18 -0
- package/dist/entries/validation/rules.js +12 -12
- package/dist/entries/view.js +212 -393
- package/dist/framework/public-api.d.ts +4 -1
- package/dist/index.js +2591 -2686
- package/package.json +27 -2
- package/dist/config/database.d.ts +0 -10
- package/dist/db/connection/createConnection.d.ts +0 -6
- package/dist/db/connection/index.d.ts +0 -11
- package/dist/domain/auth.d.ts +0 -4
- package/dist/domain/scim.d.ts +0 -9
- package/dist/modules/organization/memberRepository.d.ts +0 -14
- package/dist/modules/organization/memberTypes.d.ts +0 -9
- package/dist/modules/user/apiTokenRepository.d.ts +0 -8
- package/dist/modules/user/apiTokenTable.d.ts +0 -3
- package/dist/modules/user/authService.d.ts +0 -23
- package/dist/modules/user/notificationRepository.d.ts +0 -6
- package/dist/modules/user/notificationService.d.ts +0 -25
- package/dist/modules/user/notificationTable.d.ts +0 -3
- package/dist/modules/user/notificationTypes.d.ts +0 -12
- package/dist/modules/user/oauthIdentityRepository.d.ts +0 -9
- package/dist/modules/user/provider.d.ts +0 -11
- package/dist/modules/user/repository.d.ts +0 -13
- package/dist/modules/user/table.d.ts +0 -3
- package/dist/modules/user/tokenService.d.ts +0 -29
- package/dist/modules/user/types.d.ts +0 -46
package/README.md
CHANGED
|
@@ -26,6 +26,8 @@ import { storage } from "@getstrata/core/facades";
|
|
|
26
26
|
import { EtaViewEngine } from "@getstrata/core/view";
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
+
`.eta` files are **HTML + Eta tags** (`<% %>`, `<%= %>`, `<%~ include() %>`), not Pug. Class/attribute shorthand such as `section.section` or `a href=` fails at render time with the template name.
|
|
30
|
+
|
|
29
31
|
**Dependency:** `eta` is bundled as a direct dependency of `@getstrata/core`. Apps do not need to list it separately. The database driver is your app's choice. WorkHub and getstrata use **Bun's built-in `Bun.sql`** client; bind it with `bindDatabaseConnection()`.
|
|
30
32
|
|
|
31
33
|
`orderBy` accepts explicit `{ column, direction }` objects or column shorthand such as `{ published_at: "desc" }`.
|
|
@@ -80,7 +82,7 @@ import type { Migration } from "@getstrata/core/database/migrations/types";
|
|
|
80
82
|
Package name: **`@getstrata/core`** (npm org [`@getstrata`](https://www.npmjs.com/org/getstrata)).
|
|
81
83
|
|
|
82
84
|
1. Add `NPM_TOKEN` to GitHub repository secrets.
|
|
83
|
-
2. Tag a release: `git tag v0.5.
|
|
85
|
+
2. Tag a release: `git tag v0.5.57 && git push origin v0.5.57`
|
|
84
86
|
3. [Release workflow](../../.github/workflows/release.yml) builds and runs `npm publish --access public`.
|
|
85
87
|
|
|
86
88
|
Previously published as `@eyk-workhub/framework@0.1.0`, deprecated in favor of this package.
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
declare const MEMBER_ABILITIES: readonly ["organizations:read", "projects:read", "projects:create", "tasks:read", "tasks:create", "comments:read", "comments:create", "attachments:read", "attachments:create", "auth:tokens:read", "auth:tokens:write"];
|
|
2
2
|
declare const ADMIN_ABILITIES: readonly ["organizations:read", "projects:read", "projects:create", "tasks:read", "tasks:create", "comments:read", "comments:create", "attachments:read", "attachments:create", "auth:tokens:read", "auth:tokens:write", "organizations:create", "organizations:update", "organizations:delete", "projects:update", "projects:delete", "tasks:update", "tasks:delete", "comments:update", "comments:delete", "attachments:delete", "webhooks:read", "webhooks:write", "audit:read"];
|
|
3
3
|
declare const PLATFORM_ADMIN_ABILITIES: readonly ["*"];
|
|
4
|
+
interface AbilityCatalog {
|
|
5
|
+
member: readonly string[];
|
|
6
|
+
admin: readonly string[];
|
|
7
|
+
resolveForRole(role: string | null | undefined): string[];
|
|
8
|
+
}
|
|
4
9
|
declare function resolveAbilitiesForRole(role: string | null | undefined): string[];
|
|
5
|
-
|
|
10
|
+
declare function configureAbilityCatalog(next: AbilityCatalog): void;
|
|
11
|
+
declare function abilityCatalog(): AbilityCatalog;
|
|
12
|
+
export type { AbilityCatalog };
|
|
13
|
+
export { ADMIN_ABILITIES, abilityCatalog, configureAbilityCatalog, MEMBER_ABILITIES, PLATFORM_ADMIN_ABILITIES, resolveAbilitiesForRole, };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { OrganizationMemberRole } from "
|
|
1
|
+
import type { OrganizationMemberRole } from "../contracts/membership";
|
|
2
2
|
import { type AuthUser } from "./authContext";
|
|
3
3
|
declare const ROLE_RANK: Record<OrganizationMemberRole, number>;
|
|
4
4
|
declare function isGlobalAdmin(user: AuthUser | null): boolean;
|
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
import
|
|
2
|
-
import type { OrganizationMemberRole } from "../../modules/organization/memberTypes";
|
|
1
|
+
import type { MembershipLookup, OrganizationMemberRole } from "../contracts/membership";
|
|
3
2
|
type MembershipContext = {
|
|
4
3
|
organizationIds: number[];
|
|
5
4
|
rolesByOrganizationId: Map<number, OrganizationMemberRole>;
|
|
6
5
|
};
|
|
7
6
|
declare const membershipContext: import("node:async_hooks").AsyncLocalStorage<MembershipContext>;
|
|
8
|
-
declare
|
|
7
|
+
declare let membershipRepository: MembershipLookup;
|
|
8
|
+
declare function configureMembershipLookup(lookup: MembershipLookup): void;
|
|
9
|
+
declare function resolveMembershipLookup(): MembershipLookup;
|
|
10
|
+
declare function resetMembershipLookupForTests(): void;
|
|
9
11
|
declare function runWithMembershipContext<T>(callback: () => T | Promise<T>): Promise<T | Promise<T>>;
|
|
10
12
|
declare function currentOrgRole(organizationId: number): OrganizationMemberRole | null;
|
|
11
13
|
declare function hasOrgMembership(organizationId: number): boolean;
|
|
12
14
|
declare function currentOrganizationIds(): number[];
|
|
13
15
|
declare function hasMinimumOrgRole(organizationId: number, minimum: OrganizationMemberRole): boolean;
|
|
14
16
|
export type { MembershipContext };
|
|
15
|
-
export { currentOrganizationIds, currentOrgRole, hasMinimumOrgRole, hasOrgMembership, membershipContext, membershipRepository, runWithMembershipContext, };
|
|
17
|
+
export { configureMembershipLookup, currentOrganizationIds, currentOrgRole, hasMinimumOrgRole, hasOrgMembership, membershipContext, membershipRepository, resetMembershipLookupForTests, resolveMembershipLookup, runWithMembershipContext, };
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import type { OrganizationMemberRole } from "
|
|
1
|
+
import type { MembershipLookup, OrganizationMemberRole } from "../contracts/membership";
|
|
2
2
|
import { type AuthUser } from "./authContext";
|
|
3
|
-
|
|
4
|
-
type MembershipRepositoryLike = Pick<typeof defaultMembershipRepository, "listForUser" | "findMembership" | "listForOrganization" | "addMember" | "removeMember">;
|
|
3
|
+
type MembershipRepositoryLike = MembershipLookup;
|
|
5
4
|
declare class MembershipService {
|
|
6
5
|
private readonly members;
|
|
7
6
|
constructor(members?: MembershipRepositoryLike);
|
|
@@ -10,13 +9,13 @@ declare class MembershipService {
|
|
|
10
9
|
requireOrgAccess(organizationId: number, minimumRole?: OrganizationMemberRole, user?: AuthUser | null): Promise<OrganizationMemberRole>;
|
|
11
10
|
filterAccessibleOrganizationIds(organizationIds: number[], user?: AuthUser | null): Promise<number[]>;
|
|
12
11
|
addOwnerOnOrganizationCreate(organizationId: number, userId: number): Promise<void>;
|
|
13
|
-
listMembersForOrganization(organizationId: number): Promise<import("
|
|
12
|
+
listMembersForOrganization(organizationId: number): Promise<import("../contracts/membership").MembershipRecord[]>;
|
|
14
13
|
addMember(input: {
|
|
15
14
|
organizationId: number;
|
|
16
15
|
userId: number;
|
|
17
16
|
role?: OrganizationMemberRole;
|
|
18
|
-
}): Promise<import("
|
|
19
|
-
removeMember(organizationId: number, userId: number): Promise<
|
|
17
|
+
}): Promise<import("../contracts/membership").MembershipRecord>;
|
|
18
|
+
removeMember(organizationId: number, userId: number): Promise<unknown>;
|
|
20
19
|
}
|
|
21
20
|
export type { MembershipRepositoryLike };
|
|
22
21
|
export default MembershipService;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { AuthUser } from "../auth/authContext";
|
|
2
|
+
interface AuthUserRecord {
|
|
3
|
+
id: number;
|
|
4
|
+
email?: string | null;
|
|
5
|
+
role: string;
|
|
6
|
+
}
|
|
7
|
+
interface AuthUserDirectory {
|
|
8
|
+
resolveUserFromToken(token: string): Promise<AuthUser | null>;
|
|
9
|
+
findByIdOrThrow(id: number): Promise<AuthUserRecord>;
|
|
10
|
+
}
|
|
11
|
+
export type { AuthUserDirectory, AuthUserRecord };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
type OrganizationMemberRole = "owner" | "admin" | "member";
|
|
2
|
+
interface MembershipRecord {
|
|
3
|
+
id: number;
|
|
4
|
+
organization_id: number;
|
|
5
|
+
user_id: number;
|
|
6
|
+
role: OrganizationMemberRole;
|
|
7
|
+
created_at: Date;
|
|
8
|
+
}
|
|
9
|
+
interface MembershipLookup {
|
|
10
|
+
listForUser(userId: number): Promise<MembershipRecord[]>;
|
|
11
|
+
findMembership(userId: number, organizationId: number): Promise<MembershipRecord | null>;
|
|
12
|
+
listForOrganization(organizationId: number): Promise<MembershipRecord[]>;
|
|
13
|
+
addMember(input: {
|
|
14
|
+
organizationId: number;
|
|
15
|
+
userId: number;
|
|
16
|
+
role?: OrganizationMemberRole;
|
|
17
|
+
}): Promise<MembershipRecord>;
|
|
18
|
+
removeMember(organizationId: number, userId: number): Promise<unknown>;
|
|
19
|
+
}
|
|
20
|
+
export type { MembershipLookup, MembershipRecord, OrganizationMemberRole };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Job } from "../queue";
|
|
2
2
|
interface DispatchWebhookPayload {
|
|
3
3
|
webhookId: number;
|
|
4
|
+
tenantId: number;
|
|
4
5
|
event: string;
|
|
5
6
|
payload: Record<string, unknown>;
|
|
6
7
|
}
|
|
@@ -8,6 +9,7 @@ declare class DispatchWebhookJob extends Job<DispatchWebhookPayload> {
|
|
|
8
9
|
readonly maxAttempts = 3;
|
|
9
10
|
readonly backoffMs = 2000;
|
|
10
11
|
handle(payload: DispatchWebhookPayload): Promise<void>;
|
|
12
|
+
private deliver;
|
|
11
13
|
}
|
|
12
14
|
export { DispatchWebhookJob };
|
|
13
15
|
export default DispatchWebhookJob;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
declare const IMAGE_MIME_TYPES: Set<string>;
|
|
2
|
+
declare function normalizeMimeType(mimeType: string): string;
|
|
3
|
+
declare function isImageMimeType(mimeType: string): boolean;
|
|
4
|
+
declare function parseThumbnailWidth(raw: string | null | undefined, options?: {
|
|
5
|
+
defaultWidth?: number;
|
|
6
|
+
minWidth?: number;
|
|
7
|
+
maxWidth?: number;
|
|
8
|
+
}): number;
|
|
9
|
+
declare function resizeImageContents(contents: Uint8Array, width: number, mimeType: string): Promise<{
|
|
10
|
+
body: Uint8Array;
|
|
11
|
+
contentType: string;
|
|
12
|
+
}>;
|
|
13
|
+
export { IMAGE_MIME_TYPES, isImageMimeType, normalizeMimeType, parseThumbnailWidth, resizeImageContents, };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
declare const DEFAULT_SCHEDULE_RUN_EXPRESSION = "* * * * *";
|
|
2
|
+
declare const OS_CRON_JOB_TITLE = "getstrata-schedule-run";
|
|
3
|
+
interface InProcessCronJob {
|
|
4
|
+
readonly cron: string;
|
|
5
|
+
stop(): InProcessCronJob;
|
|
6
|
+
ref(): InProcessCronJob;
|
|
7
|
+
unref(): InProcessCronJob;
|
|
8
|
+
}
|
|
9
|
+
declare function parseScheduleExpression(expression: string, from?: Date): Date | null;
|
|
10
|
+
declare function registerInProcessScheduleRunner(run: () => void | Promise<void>, expression?: string): InProcessCronJob;
|
|
11
|
+
declare function installOsScheduleRunner(workerScriptPath: string, expression?: string, title?: string): Promise<void>;
|
|
12
|
+
declare function uninstallOsScheduleRunner(title?: string): Promise<void>;
|
|
13
|
+
export type { InProcessCronJob };
|
|
14
|
+
export { DEFAULT_SCHEDULE_RUN_EXPRESSION, installOsScheduleRunner, OS_CRON_JOB_TITLE, parseScheduleExpression, registerInProcessScheduleRunner, uninstallOsScheduleRunner, };
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
type TenancyDriver = "rls" | "none";
|
|
2
|
+
declare function readTenancyDriver(env?: Record<string, string | undefined>): TenancyDriver;
|
|
3
|
+
declare function isTenancyEnabled(env?: Record<string, string | undefined>): boolean;
|
|
4
|
+
export type { TenancyDriver };
|
|
5
|
+
export { isTenancyEnabled, readTenancyDriver };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type TenantContext } from "./tenantContext";
|
|
2
2
|
declare const DEFAULT_TENANT: TenantContext;
|
|
3
3
|
declare function resolveUserTenantId(userId: number): Promise<number>;
|
|
4
4
|
declare function auditChecksum(payload: Record<string, unknown>): string;
|
|
@@ -5,6 +5,10 @@ interface RenderOptions {
|
|
|
5
5
|
layout?: string | false;
|
|
6
6
|
}
|
|
7
7
|
type LayoutDataResolver = () => Promise<Record<string, unknown>>;
|
|
8
|
+
/**
|
|
9
|
+
* Renders `.eta` files as HTML + Eta tags (`<% %>`, `<%= %>`, `<%~ include() %>`).
|
|
10
|
+
* Pug class/attribute shorthand is rejected at render time.
|
|
11
|
+
*/
|
|
8
12
|
declare class EtaViewEngine implements ViewEngine {
|
|
9
13
|
private readonly eta;
|
|
10
14
|
private readonly resolveLayoutData?;
|
|
@@ -1,17 +1,4 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
// ../../src/config/database.ts
|
|
3
|
-
function readInteger(name, fallback) {
|
|
4
|
-
const parsed = Number.parseInt(process.env[name] ?? String(fallback), 10);
|
|
5
|
-
return Number.isInteger(parsed) && parsed >= 0 ? parsed : fallback;
|
|
6
|
-
}
|
|
7
|
-
var databaseConfig = {
|
|
8
|
-
url: process.env.DATABASE_URL ?? "",
|
|
9
|
-
poolMax: readInteger("DB_POOL_MAX", 10),
|
|
10
|
-
idleTimeoutSeconds: readInteger("DB_POOL_IDLE_TIMEOUT", 30),
|
|
11
|
-
maxLifetimeSeconds: readInteger("DB_POOL_MAX_LIFETIME", 3600),
|
|
12
|
-
connectionTimeoutSeconds: readInteger("DB_CONNECTION_TIMEOUT", 10)
|
|
13
|
-
};
|
|
14
|
-
|
|
15
2
|
// ../../src/core/runtime/asyncContextStore.ts
|
|
16
3
|
import { AsyncLocalStorage } from "async_hooks";
|
|
17
4
|
function createAsyncContextStore(key) {
|
|
@@ -26,153 +13,6 @@ function createAsyncContextStore(key) {
|
|
|
26
13
|
return store;
|
|
27
14
|
}
|
|
28
15
|
|
|
29
|
-
// ../../src/core/database/connectionContext.ts
|
|
30
|
-
var activeConnection = createAsyncContextStore("@getstrata/databaseConnectionContext");
|
|
31
|
-
function runWithDatabaseConnection(connection, callback) {
|
|
32
|
-
return activeConnection.run(connection, callback);
|
|
33
|
-
}
|
|
34
|
-
function getActiveDatabaseConnection(fallback) {
|
|
35
|
-
return activeConnection.getStore() ?? fallback;
|
|
36
|
-
}
|
|
37
|
-
function hasActiveDatabaseConnection() {
|
|
38
|
-
return activeConnection.getStore() !== undefined;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// ../../src/core/database/queryProxy.ts
|
|
42
|
-
var POOL_CONNECTION_METHODS = new Set(["begin", "close", "connect"]);
|
|
43
|
-
function createDatabaseQueryProxy(pool) {
|
|
44
|
-
function resolveDatabase() {
|
|
45
|
-
return getActiveDatabaseConnection(pool);
|
|
46
|
-
}
|
|
47
|
-
function resolveDatabaseForProperty(property) {
|
|
48
|
-
if (typeof property === "string" && POOL_CONNECTION_METHODS.has(property)) {
|
|
49
|
-
return pool;
|
|
50
|
-
}
|
|
51
|
-
return resolveDatabase();
|
|
52
|
-
}
|
|
53
|
-
return new Proxy(function database() {}, {
|
|
54
|
-
apply(_target, _thisArg, args) {
|
|
55
|
-
return resolveDatabase()(...args);
|
|
56
|
-
},
|
|
57
|
-
get(_target, property) {
|
|
58
|
-
const connection = resolveDatabaseForProperty(property);
|
|
59
|
-
const value = connection[property];
|
|
60
|
-
return typeof value === "function" ? value.bind(connection) : value;
|
|
61
|
-
}
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// ../../src/core/database/defaultConnection.ts
|
|
66
|
-
var defaultPool = {
|
|
67
|
-
connection: null
|
|
68
|
-
};
|
|
69
|
-
var defaultQuery = {
|
|
70
|
-
connection: null
|
|
71
|
-
};
|
|
72
|
-
function registerDefaultDatabasePool(connection) {
|
|
73
|
-
defaultPool.connection = connection;
|
|
74
|
-
defaultQuery.connection = createDatabaseQueryProxy(connection);
|
|
75
|
-
}
|
|
76
|
-
function getDefaultDatabaseQuery() {
|
|
77
|
-
if (!defaultQuery.connection) {
|
|
78
|
-
throw new Error("Default database query handle is not registered. Call registerDefaultDatabasePool() during app bootstrap.");
|
|
79
|
-
}
|
|
80
|
-
return defaultQuery.connection;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// ../../src/db/connection/createConnection.ts
|
|
84
|
-
var {SQL } = globalThis.Bun;
|
|
85
|
-
function createDatabaseConnection(config) {
|
|
86
|
-
if (!config.url) {
|
|
87
|
-
throw new Error("DATABASE_URL is not configured. Set DATABASE_URL before starting the app or running integration tests.");
|
|
88
|
-
}
|
|
89
|
-
return new SQL({
|
|
90
|
-
url: config.url,
|
|
91
|
-
max: config.poolMax,
|
|
92
|
-
idleTimeout: config.idleTimeoutSeconds,
|
|
93
|
-
maxLifetime: config.maxLifetimeSeconds,
|
|
94
|
-
connectionTimeout: config.connectionTimeoutSeconds
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// ../../src/db/connection/index.ts
|
|
99
|
-
var connectionHolder = {
|
|
100
|
-
connection: null
|
|
101
|
-
};
|
|
102
|
-
function getDatabase() {
|
|
103
|
-
if (!connectionHolder.connection) {
|
|
104
|
-
connectionHolder.connection = createDatabaseConnection(databaseConfig);
|
|
105
|
-
registerDefaultDatabasePool(connectionHolder.connection);
|
|
106
|
-
}
|
|
107
|
-
return connectionHolder.connection;
|
|
108
|
-
}
|
|
109
|
-
function getDb() {
|
|
110
|
-
getDatabase();
|
|
111
|
-
return getDefaultDatabaseQuery();
|
|
112
|
-
}
|
|
113
|
-
var db = new Proxy(function database() {}, {
|
|
114
|
-
apply(_target, _thisArg, args) {
|
|
115
|
-
return getDb()(...args);
|
|
116
|
-
},
|
|
117
|
-
get(_target, property) {
|
|
118
|
-
const connection = getDb();
|
|
119
|
-
const value = connection[property];
|
|
120
|
-
return typeof value === "function" ? value.bind(connection) : value;
|
|
121
|
-
}
|
|
122
|
-
});
|
|
123
|
-
var connection_default = db;
|
|
124
|
-
|
|
125
|
-
// ../../src/modules/organization/memberRepository.ts
|
|
126
|
-
class OrganizationMemberRepository {
|
|
127
|
-
constructor() {}
|
|
128
|
-
async findMembership(userId, organizationId) {
|
|
129
|
-
const rows = await connection_default`
|
|
130
|
-
SELECT id, organization_id, user_id, role, created_at
|
|
131
|
-
FROM organization_member
|
|
132
|
-
WHERE user_id = ${userId} AND organization_id = ${organizationId}
|
|
133
|
-
LIMIT 1
|
|
134
|
-
`;
|
|
135
|
-
return rows[0] ?? null;
|
|
136
|
-
}
|
|
137
|
-
async listForUser(userId) {
|
|
138
|
-
return await connection_default`
|
|
139
|
-
SELECT id, organization_id, user_id, role, created_at
|
|
140
|
-
FROM organization_member
|
|
141
|
-
WHERE user_id = ${userId}
|
|
142
|
-
ORDER BY organization_id
|
|
143
|
-
`;
|
|
144
|
-
}
|
|
145
|
-
async listForOrganization(organizationId) {
|
|
146
|
-
return await connection_default`
|
|
147
|
-
SELECT id, organization_id, user_id, role, created_at
|
|
148
|
-
FROM organization_member
|
|
149
|
-
WHERE organization_id = ${organizationId}
|
|
150
|
-
ORDER BY id
|
|
151
|
-
`;
|
|
152
|
-
}
|
|
153
|
-
async addMember(input) {
|
|
154
|
-
const rows = await connection_default`
|
|
155
|
-
INSERT INTO organization_member (organization_id, user_id, role)
|
|
156
|
-
VALUES (${input.organizationId}, ${input.userId}, ${input.role ?? "member"})
|
|
157
|
-
RETURNING id, organization_id, user_id, role, created_at
|
|
158
|
-
`;
|
|
159
|
-
const row = rows[0];
|
|
160
|
-
if (!row) {
|
|
161
|
-
throw new Error("Organization member insert did not return a row.");
|
|
162
|
-
}
|
|
163
|
-
return row;
|
|
164
|
-
}
|
|
165
|
-
async removeMember(organizationId, userId) {
|
|
166
|
-
const rows = await connection_default`
|
|
167
|
-
DELETE FROM organization_member
|
|
168
|
-
WHERE organization_id = ${organizationId} AND user_id = ${userId}
|
|
169
|
-
RETURNING id
|
|
170
|
-
`;
|
|
171
|
-
return rows.length > 0;
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
var memberRepository_default = OrganizationMemberRepository;
|
|
175
|
-
|
|
176
16
|
// ../../src/core/auth/accessControl.ts
|
|
177
17
|
import { ForbiddenError } from "@getstrata/core/errors/http";
|
|
178
18
|
|
|
@@ -214,7 +54,27 @@ function resolveUserId(user) {
|
|
|
214
54
|
|
|
215
55
|
// ../../src/core/auth/membershipContext.ts
|
|
216
56
|
var membershipContext = createAsyncContextStore("@getstrata/membershipContext");
|
|
217
|
-
var
|
|
57
|
+
var uninitializedMembershipLookup = {
|
|
58
|
+
async listForUser() {
|
|
59
|
+
return [];
|
|
60
|
+
},
|
|
61
|
+
async findMembership() {
|
|
62
|
+
return null;
|
|
63
|
+
},
|
|
64
|
+
async listForOrganization() {
|
|
65
|
+
return [];
|
|
66
|
+
},
|
|
67
|
+
async addMember() {
|
|
68
|
+
throw new Error("configureMembershipLookup() must be called before mutating memberships.");
|
|
69
|
+
},
|
|
70
|
+
async removeMember() {
|
|
71
|
+
throw new Error("configureMembershipLookup() must be called before mutating memberships.");
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
var membershipRepository = uninitializedMembershipLookup;
|
|
75
|
+
function resolveMembershipLookup() {
|
|
76
|
+
return membershipRepository;
|
|
77
|
+
}
|
|
218
78
|
async function runWithMembershipContext(callback) {
|
|
219
79
|
const user = currentAuthUser();
|
|
220
80
|
if (!user || isGlobalAdmin(user)) {
|
|
@@ -56,169 +56,29 @@ function resolveUserId(user) {
|
|
|
56
56
|
return userId;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
// ../../src/
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
var databaseConfig = {
|
|
65
|
-
url: process.env.DATABASE_URL ?? "",
|
|
66
|
-
poolMax: readInteger("DB_POOL_MAX", 10),
|
|
67
|
-
idleTimeoutSeconds: readInteger("DB_POOL_IDLE_TIMEOUT", 30),
|
|
68
|
-
maxLifetimeSeconds: readInteger("DB_POOL_MAX_LIFETIME", 3600),
|
|
69
|
-
connectionTimeoutSeconds: readInteger("DB_CONNECTION_TIMEOUT", 10)
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
// ../../src/core/database/connectionContext.ts
|
|
73
|
-
var activeConnection = createAsyncContextStore("@getstrata/databaseConnectionContext");
|
|
74
|
-
function runWithDatabaseConnection(connection, callback) {
|
|
75
|
-
return activeConnection.run(connection, callback);
|
|
76
|
-
}
|
|
77
|
-
function getActiveDatabaseConnection(fallback) {
|
|
78
|
-
return activeConnection.getStore() ?? fallback;
|
|
79
|
-
}
|
|
80
|
-
function hasActiveDatabaseConnection() {
|
|
81
|
-
return activeConnection.getStore() !== undefined;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// ../../src/core/database/queryProxy.ts
|
|
85
|
-
var POOL_CONNECTION_METHODS = new Set(["begin", "close", "connect"]);
|
|
86
|
-
function createDatabaseQueryProxy(pool) {
|
|
87
|
-
function resolveDatabase() {
|
|
88
|
-
return getActiveDatabaseConnection(pool);
|
|
89
|
-
}
|
|
90
|
-
function resolveDatabaseForProperty(property) {
|
|
91
|
-
if (typeof property === "string" && POOL_CONNECTION_METHODS.has(property)) {
|
|
92
|
-
return pool;
|
|
93
|
-
}
|
|
94
|
-
return resolveDatabase();
|
|
95
|
-
}
|
|
96
|
-
return new Proxy(function database() {}, {
|
|
97
|
-
apply(_target, _thisArg, args) {
|
|
98
|
-
return resolveDatabase()(...args);
|
|
99
|
-
},
|
|
100
|
-
get(_target, property) {
|
|
101
|
-
const connection = resolveDatabaseForProperty(property);
|
|
102
|
-
const value = connection[property];
|
|
103
|
-
return typeof value === "function" ? value.bind(connection) : value;
|
|
104
|
-
}
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// ../../src/core/database/defaultConnection.ts
|
|
109
|
-
var defaultPool = {
|
|
110
|
-
connection: null
|
|
111
|
-
};
|
|
112
|
-
var defaultQuery = {
|
|
113
|
-
connection: null
|
|
114
|
-
};
|
|
115
|
-
function registerDefaultDatabasePool(connection) {
|
|
116
|
-
defaultPool.connection = connection;
|
|
117
|
-
defaultQuery.connection = createDatabaseQueryProxy(connection);
|
|
118
|
-
}
|
|
119
|
-
function getDefaultDatabaseQuery() {
|
|
120
|
-
if (!defaultQuery.connection) {
|
|
121
|
-
throw new Error("Default database query handle is not registered. Call registerDefaultDatabasePool() during app bootstrap.");
|
|
122
|
-
}
|
|
123
|
-
return defaultQuery.connection;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// ../../src/db/connection/createConnection.ts
|
|
127
|
-
var {SQL } = globalThis.Bun;
|
|
128
|
-
function createDatabaseConnection(config) {
|
|
129
|
-
if (!config.url) {
|
|
130
|
-
throw new Error("DATABASE_URL is not configured. Set DATABASE_URL before starting the app or running integration tests.");
|
|
131
|
-
}
|
|
132
|
-
return new SQL({
|
|
133
|
-
url: config.url,
|
|
134
|
-
max: config.poolMax,
|
|
135
|
-
idleTimeout: config.idleTimeoutSeconds,
|
|
136
|
-
maxLifetime: config.maxLifetimeSeconds,
|
|
137
|
-
connectionTimeout: config.connectionTimeoutSeconds
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// ../../src/db/connection/index.ts
|
|
142
|
-
var connectionHolder = {
|
|
143
|
-
connection: null
|
|
144
|
-
};
|
|
145
|
-
function getDatabase() {
|
|
146
|
-
if (!connectionHolder.connection) {
|
|
147
|
-
connectionHolder.connection = createDatabaseConnection(databaseConfig);
|
|
148
|
-
registerDefaultDatabasePool(connectionHolder.connection);
|
|
149
|
-
}
|
|
150
|
-
return connectionHolder.connection;
|
|
151
|
-
}
|
|
152
|
-
function getDb() {
|
|
153
|
-
getDatabase();
|
|
154
|
-
return getDefaultDatabaseQuery();
|
|
155
|
-
}
|
|
156
|
-
var db = new Proxy(function database() {}, {
|
|
157
|
-
apply(_target, _thisArg, args) {
|
|
158
|
-
return getDb()(...args);
|
|
59
|
+
// ../../src/core/auth/membershipContext.ts
|
|
60
|
+
var membershipContext = createAsyncContextStore("@getstrata/membershipContext");
|
|
61
|
+
var uninitializedMembershipLookup = {
|
|
62
|
+
async listForUser() {
|
|
63
|
+
return [];
|
|
159
64
|
},
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
async findMembership(userId, organizationId) {
|
|
172
|
-
const rows = await connection_default`
|
|
173
|
-
SELECT id, organization_id, user_id, role, created_at
|
|
174
|
-
FROM organization_member
|
|
175
|
-
WHERE user_id = ${userId} AND organization_id = ${organizationId}
|
|
176
|
-
LIMIT 1
|
|
177
|
-
`;
|
|
178
|
-
return rows[0] ?? null;
|
|
179
|
-
}
|
|
180
|
-
async listForUser(userId) {
|
|
181
|
-
return await connection_default`
|
|
182
|
-
SELECT id, organization_id, user_id, role, created_at
|
|
183
|
-
FROM organization_member
|
|
184
|
-
WHERE user_id = ${userId}
|
|
185
|
-
ORDER BY organization_id
|
|
186
|
-
`;
|
|
187
|
-
}
|
|
188
|
-
async listForOrganization(organizationId) {
|
|
189
|
-
return await connection_default`
|
|
190
|
-
SELECT id, organization_id, user_id, role, created_at
|
|
191
|
-
FROM organization_member
|
|
192
|
-
WHERE organization_id = ${organizationId}
|
|
193
|
-
ORDER BY id
|
|
194
|
-
`;
|
|
195
|
-
}
|
|
196
|
-
async addMember(input) {
|
|
197
|
-
const rows = await connection_default`
|
|
198
|
-
INSERT INTO organization_member (organization_id, user_id, role)
|
|
199
|
-
VALUES (${input.organizationId}, ${input.userId}, ${input.role ?? "member"})
|
|
200
|
-
RETURNING id, organization_id, user_id, role, created_at
|
|
201
|
-
`;
|
|
202
|
-
const row = rows[0];
|
|
203
|
-
if (!row) {
|
|
204
|
-
throw new Error("Organization member insert did not return a row.");
|
|
205
|
-
}
|
|
206
|
-
return row;
|
|
207
|
-
}
|
|
208
|
-
async removeMember(organizationId, userId) {
|
|
209
|
-
const rows = await connection_default`
|
|
210
|
-
DELETE FROM organization_member
|
|
211
|
-
WHERE organization_id = ${organizationId} AND user_id = ${userId}
|
|
212
|
-
RETURNING id
|
|
213
|
-
`;
|
|
214
|
-
return rows.length > 0;
|
|
65
|
+
async findMembership() {
|
|
66
|
+
return null;
|
|
67
|
+
},
|
|
68
|
+
async listForOrganization() {
|
|
69
|
+
return [];
|
|
70
|
+
},
|
|
71
|
+
async addMember() {
|
|
72
|
+
throw new Error("configureMembershipLookup() must be called before mutating memberships.");
|
|
73
|
+
},
|
|
74
|
+
async removeMember() {
|
|
75
|
+
throw new Error("configureMembershipLookup() must be called before mutating memberships.");
|
|
215
76
|
}
|
|
77
|
+
};
|
|
78
|
+
var membershipRepository = uninitializedMembershipLookup;
|
|
79
|
+
function resolveMembershipLookup() {
|
|
80
|
+
return membershipRepository;
|
|
216
81
|
}
|
|
217
|
-
var memberRepository_default = OrganizationMemberRepository;
|
|
218
|
-
|
|
219
|
-
// ../../src/core/auth/membershipContext.ts
|
|
220
|
-
var membershipContext = createAsyncContextStore("@getstrata/membershipContext");
|
|
221
|
-
var membershipRepository = new memberRepository_default;
|
|
222
82
|
async function runWithMembershipContext(callback) {
|
|
223
83
|
const user = currentAuthUser();
|
|
224
84
|
if (!user || isGlobalAdmin(user)) {
|
|
@@ -326,11 +186,11 @@ function assertOrganizationReadable(organizationId) {
|
|
|
326
186
|
}
|
|
327
187
|
}
|
|
328
188
|
export {
|
|
329
|
-
|
|
330
|
-
resolveOrganizationScope,
|
|
331
|
-
emptyPaginateResult,
|
|
332
|
-
assertResourceInCurrentTenant,
|
|
333
|
-
assertOrganizationReadable,
|
|
189
|
+
appendOrganizationScope,
|
|
334
190
|
appendProjectScope,
|
|
335
|
-
|
|
191
|
+
assertOrganizationReadable,
|
|
192
|
+
assertResourceInCurrentTenant,
|
|
193
|
+
emptyPaginateResult,
|
|
194
|
+
resolveOrganizationScope,
|
|
195
|
+
scopedOrganizationIds
|
|
336
196
|
};
|