@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.
Files changed (118) hide show
  1. package/README.md +3 -1
  2. package/dist/{domain/abilities.d.ts → core/auth/abilityCatalog.d.ts} +9 -1
  3. package/dist/core/auth/accessControl.d.ts +1 -1
  4. package/dist/core/auth/membershipContext.d.ts +6 -4
  5. package/dist/core/auth/membershipService.d.ts +5 -6
  6. package/dist/core/contracts/authUserDirectory.d.ts +11 -0
  7. package/dist/core/contracts/membership.d.ts +20 -0
  8. package/dist/core/http/clientIp.d.ts +3 -0
  9. package/dist/core/jobs/dispatchWebhookJob.d.ts +2 -0
  10. package/dist/core/media/imageTransform.d.ts +13 -0
  11. package/dist/core/scheduler/osCron.d.ts +14 -0
  12. package/dist/core/tenant/tenancyConfig.d.ts +5 -0
  13. package/dist/core/tenant/tenantMiddleware.d.ts +1 -1
  14. package/dist/core/terminal/runShell.d.ts +6 -0
  15. package/dist/core/view/assertEtaHtmlSource.d.ts +5 -0
  16. package/dist/core/view/etaViewEngine.d.ts +4 -0
  17. package/dist/entries/audit/exportAuditLogs.js +2 -2
  18. package/dist/entries/audit/siemFormatter.js +2 -2
  19. package/dist/entries/auth/accessControl.js +4 -4
  20. package/dist/entries/auth/membershipMiddleware.js +21 -161
  21. package/dist/entries/auth/membershipScope.js +26 -166
  22. package/dist/entries/auth/membershipService.js +21 -161
  23. package/dist/entries/auth/oauth/providers.js +2 -2
  24. package/dist/entries/auth/password.js +2 -2
  25. package/dist/entries/auth/policy.js +2 -2
  26. package/dist/entries/auth/scimAuthMiddleware.js +22 -4
  27. package/dist/entries/auth/sessionCookie.js +4 -4
  28. package/dist/entries/auth/sessionGuard.js +12 -349
  29. package/dist/entries/auth/tokenHash.js +2 -2
  30. package/dist/entries/config/envSchema.js +2 -2
  31. package/dist/entries/contracts/container.js +2 -2
  32. package/dist/entries/contracts/di.js +2 -2
  33. package/dist/entries/contracts/serviceTokens.js +6 -6
  34. package/dist/entries/crypto/fieldEncryption.js +8 -8
  35. package/dist/entries/crypto/mfaSecret.js +2 -2
  36. package/dist/entries/database/errors.js +2 -2
  37. package/dist/entries/database/migrations.js +8 -8
  38. package/dist/entries/database/model.js +5 -5
  39. package/dist/entries/database/query.js +19 -19
  40. package/dist/entries/database/relationships.js +13 -13
  41. package/dist/entries/database/schema.js +13 -13
  42. package/dist/entries/database/seeders.js +2 -2
  43. package/dist/entries/facades.js +8 -8
  44. package/dist/entries/http/authMiddleware.js +2 -2
  45. package/dist/entries/http/bodySizeLimitMiddleware.js +2 -2
  46. package/dist/entries/http/clientIp.js +19 -0
  47. package/dist/entries/http/cookies.js +2 -2
  48. package/dist/entries/http/csrfMiddleware.js +2 -1
  49. package/dist/entries/http/csrfProtection.js +2 -2
  50. package/dist/entries/http/csrfToken.js +8 -7
  51. package/dist/entries/http/etag.js +8 -8
  52. package/dist/entries/http/flashSession.js +4 -4
  53. package/dist/entries/http/formRequest.js +2 -2
  54. package/dist/entries/http/loginThrottleMiddleware.js +20 -3
  55. package/dist/entries/http/memoryThrottleMiddleware.js +16 -1
  56. package/dist/entries/http/metricsMiddleware.js +2 -2
  57. package/dist/entries/http/pagination.js +4 -4
  58. package/dist/entries/http/parseFormBody.js +2 -2
  59. package/dist/entries/http/parseMultipartUpload.js +2 -2
  60. package/dist/entries/http/resources.js +2 -2
  61. package/dist/entries/http/response.js +214 -395
  62. package/dist/entries/http/scimThrottleMiddleware.js +40 -1
  63. package/dist/entries/http/securedRouteModelBinding.js +2 -2
  64. package/dist/entries/http/throttleMiddleware.js +20 -3
  65. package/dist/entries/http/validation.js +13 -13
  66. package/dist/entries/http/webErrorResponse.js +212 -393
  67. package/dist/entries/jobs/dispatchWebhookJob.js +111 -5
  68. package/dist/entries/lifecycle/gracefulShutdown.js +3 -3
  69. package/dist/entries/logging/logger.js +2 -2
  70. package/dist/entries/logging/requestLoggingMiddleware.js +16 -1
  71. package/dist/entries/mail/mailer.js +6 -6
  72. package/dist/entries/mail/markdownMail.js +3 -3
  73. package/dist/entries/mail/markdownMailable.js +2 -2
  74. package/dist/entries/media/imageTransform.js +40 -0
  75. package/dist/entries/metrics/prometheus.js +2 -2
  76. package/dist/entries/openapi/generator.js +2 -2
  77. package/dist/entries/queue/createAppQueue.js +4 -4
  78. package/dist/entries/queue/publicQueue.js +10 -10
  79. package/dist/entries/queue/queueMetrics.js +2 -2
  80. package/dist/entries/queue/redisQueue.js +7 -7
  81. package/dist/entries/queue.js +3 -3
  82. package/dist/entries/scheduler/osCron.js +27 -0
  83. package/dist/entries/scheduler/schedule.js +2 -2
  84. package/dist/entries/security/oauthState.js +3 -3
  85. package/dist/entries/security/publicReads.js +2 -2
  86. package/dist/entries/security/safeFetch.js +2 -2
  87. package/dist/entries/security/safeUrl.js +5 -5
  88. package/dist/entries/security/scimTenantTokens.js +3 -6
  89. package/dist/entries/security/totp.js +2 -2
  90. package/dist/entries/storage/storage.js +7 -7
  91. package/dist/entries/tenant/tenancyConfig.js +12 -0
  92. package/dist/entries/tenant/tenantDatabaseScope.js +13 -2
  93. package/dist/entries/terminal/runShell.js +18 -0
  94. package/dist/entries/validation/rules.js +12 -12
  95. package/dist/entries/view.js +212 -393
  96. package/dist/framework/public-api.d.ts +4 -1
  97. package/dist/index.js +2591 -2686
  98. package/package.json +27 -2
  99. package/dist/config/database.d.ts +0 -10
  100. package/dist/db/connection/createConnection.d.ts +0 -6
  101. package/dist/db/connection/index.d.ts +0 -11
  102. package/dist/domain/auth.d.ts +0 -4
  103. package/dist/domain/scim.d.ts +0 -9
  104. package/dist/modules/organization/memberRepository.d.ts +0 -14
  105. package/dist/modules/organization/memberTypes.d.ts +0 -9
  106. package/dist/modules/user/apiTokenRepository.d.ts +0 -8
  107. package/dist/modules/user/apiTokenTable.d.ts +0 -3
  108. package/dist/modules/user/authService.d.ts +0 -23
  109. package/dist/modules/user/notificationRepository.d.ts +0 -6
  110. package/dist/modules/user/notificationService.d.ts +0 -25
  111. package/dist/modules/user/notificationTable.d.ts +0 -3
  112. package/dist/modules/user/notificationTypes.d.ts +0 -12
  113. package/dist/modules/user/oauthIdentityRepository.d.ts +0 -9
  114. package/dist/modules/user/provider.d.ts +0 -11
  115. package/dist/modules/user/repository.d.ts +0 -13
  116. package/dist/modules/user/table.d.ts +0 -3
  117. package/dist/modules/user/tokenService.d.ts +0 -29
  118. 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.13 && git push origin v0.5.13`
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
- export { ADMIN_ABILITIES, MEMBER_ABILITIES, PLATFORM_ADMIN_ABILITIES, resolveAbilitiesForRole };
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 "../../modules/organization/memberTypes";
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 OrganizationMemberRepository from "../../modules/organization/memberRepository";
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 const membershipRepository: OrganizationMemberRepository;
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 "../../modules/organization/memberTypes";
1
+ import type { MembershipLookup, OrganizationMemberRole } from "../contracts/membership";
2
2
  import { type AuthUser } from "./authContext";
3
- import { membershipRepository as defaultMembershipRepository } from "./membershipContext";
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("../../modules/organization/memberTypes").OrganizationMemberRecord[]>;
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("../../modules/organization/memberTypes").OrganizationMemberRecord>;
19
- removeMember(organizationId: number, userId: number): Promise<boolean>;
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 };
@@ -0,0 +1,3 @@
1
+ declare function trustForwardedFor(env?: Record<string, string | undefined>): boolean;
2
+ declare function readClientIp(request: Request, env?: Record<string, string | undefined>): string | undefined;
3
+ export { readClientIp, trustForwardedFor };
@@ -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 { TenantContext } from "./tenantContext";
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;
@@ -0,0 +1,6 @@
1
+ declare function runInteractiveShell(command: string[], options?: {
2
+ cwd?: string;
3
+ cols?: number;
4
+ rows?: number;
5
+ }): Promise<number>;
6
+ export { runInteractiveShell };
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Throws when an .eta source still uses Pug shorthand instead of HTML + Eta tags.
3
+ */
4
+ declare function assertEtaHtmlSource(templateName: string, source: string): void;
5
+ export { assertEtaHtmlSource };
@@ -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?;
@@ -262,6 +262,6 @@ async function exportPendingAuditLogs() {
262
262
  });
263
263
  }
264
264
  export {
265
- resolveAuditExportConfig,
266
- exportPendingAuditLogs
265
+ exportPendingAuditLogs,
266
+ resolveAuditExportConfig
267
267
  };
@@ -32,6 +32,6 @@ function formatCefLine(event) {
32
32
  return `CEF:0|WorkHub|API|1.0|${event.action}|${event.subject_type}|5|${extension}`;
33
33
  }
34
34
  export {
35
- formatSiemAuditEvent,
36
- formatCefLine
35
+ formatCefLine,
36
+ formatSiemAuditEvent
37
37
  };
@@ -52,9 +52,9 @@ function resolveUserId(user) {
52
52
  return userId;
53
53
  }
54
54
  export {
55
- resolveUserId,
56
- requireAuthenticatedUser,
57
- isGlobalAdmin,
55
+ ROLE_RANK,
58
56
  hasMinimumOrgRole,
59
- ROLE_RANK
57
+ isGlobalAdmin,
58
+ requireAuthenticatedUser,
59
+ resolveUserId
60
60
  };
@@ -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 membershipRepository = new memberRepository_default;
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/config/database.ts
60
- function readInteger(name, fallback) {
61
- const parsed = Number.parseInt(process.env[name] ?? String(fallback), 10);
62
- return Number.isInteger(parsed) && parsed >= 0 ? parsed : fallback;
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
- get(_target, property) {
161
- const connection = getDb();
162
- const value = connection[property];
163
- return typeof value === "function" ? value.bind(connection) : value;
164
- }
165
- });
166
- var connection_default = db;
167
-
168
- // ../../src/modules/organization/memberRepository.ts
169
- class OrganizationMemberRepository {
170
- constructor() {}
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
- scopedOrganizationIds,
330
- resolveOrganizationScope,
331
- emptyPaginateResult,
332
- assertResourceInCurrentTenant,
333
- assertOrganizationReadable,
189
+ appendOrganizationScope,
334
190
  appendProjectScope,
335
- appendOrganizationScope
191
+ assertOrganizationReadable,
192
+ assertResourceInCurrentTenant,
193
+ emptyPaginateResult,
194
+ resolveOrganizationScope,
195
+ scopedOrganizationIds
336
196
  };