@getstrata/core 0.5.14 → 0.5.16

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 (44) hide show
  1. package/dist/bootstrap/contracts.d.ts +2 -0
  2. package/dist/bootstrap/http/securedRouteModelBinding.d.ts +11 -0
  3. package/dist/core/auth/membershipService.d.ts +5 -1
  4. package/dist/core/database/baseRepository.d.ts +6 -1
  5. package/dist/core/database/connectionContext.d.ts +2 -1
  6. package/dist/core/database/defaultConnection.d.ts +6 -0
  7. package/dist/core/database/queryProxy.d.ts +3 -0
  8. package/dist/core/database/repositoryConnection.d.ts +3 -3
  9. package/dist/core/http/securedRouteModelBinding.d.ts +2 -11
  10. package/dist/core/jobs/dispatchWebhookJob.d.ts +0 -1
  11. package/dist/core/security/safeFetch.d.ts +2 -0
  12. package/dist/core/security/safeUrl.d.ts +16 -1
  13. package/dist/core/tenant/tenantDatabaseScope.d.ts +2 -1
  14. package/dist/db/connection/index.d.ts +1 -1
  15. package/dist/entries/auth/accessControl.js +1 -113
  16. package/dist/entries/auth/authContext.js +1 -15
  17. package/dist/entries/auth/guard.js +1 -3044
  18. package/dist/entries/auth/membershipContext.js +1 -276
  19. package/dist/entries/auth/membershipScope.js +1 -390
  20. package/dist/entries/auth/membershipService.js +1 -480
  21. package/dist/entries/auth/policy.js +1 -134
  22. package/dist/entries/cache/createCacheStore.js +376 -0
  23. package/dist/entries/database.js +1 -2398
  24. package/dist/entries/http/csrfToken.js +0 -6
  25. package/dist/entries/http/middleware.js +1 -68
  26. package/dist/entries/http/requestMetaContext.js +1 -18
  27. package/dist/entries/http/webErrorResponse.js +94 -563
  28. package/dist/entries/http/webFormRequest.js +0 -131
  29. package/dist/entries/http.js +1 -4091
  30. package/dist/entries/jobs/dispatchWebhookJob.js +109 -102
  31. package/dist/entries/queue/createAppQueue.js +111 -631
  32. package/dist/entries/queue/jobRunner.js +0 -3
  33. package/dist/entries/queue/publicQueue.js +45 -546
  34. package/dist/entries/queue/queueMetrics.js +111 -631
  35. package/dist/entries/security/safeUrl.js +29 -0
  36. package/dist/entries/security/securityEvents.js +1 -41
  37. package/dist/entries/tenant/tenantContext.js +1 -30
  38. package/dist/entries/tenant/tenantMiddleware.js +1 -312
  39. package/dist/entries/tracing/traceContext.js +1 -15
  40. package/dist/entries/view.js +94 -563
  41. package/dist/framework/public-api.d.ts +30 -6
  42. package/dist/index.js +2529 -2031
  43. package/dist/modules/user/repository.d.ts +1 -0
  44. package/package.json +11 -5
@@ -1,3 +1,4 @@
1
+ import type { StorageManager } from "../core/storage/storage";
1
2
  import type { CacheLike } from "../types/services";
2
3
  type CachedJson = <T>(cacheKey: string, loader: () => Promise<T>, tags?: string[], request?: Request) => Promise<Response>;
3
4
  type AppRouteMap = Record<string, any>;
@@ -23,6 +24,7 @@ declare class ConfigStore {
23
24
  interface AppDependencies {
24
25
  container: ServiceContainer;
25
26
  cache: CacheLike;
27
+ storage: StorageManager;
26
28
  }
27
29
  type MutableAppDependencies = Partial<Omit<AppDependencies, "container">> & Pick<AppDependencies, "container">;
28
30
  interface ProviderContext {
@@ -0,0 +1,11 @@
1
+ import type { Policy } from "../../core/auth/policy";
2
+ import type { RouteRequest } from "../../core/http/route";
3
+ interface RouteModelAuthorization {
4
+ resource: string;
5
+ action: keyof Policy;
6
+ requireIfMatch?: boolean;
7
+ }
8
+ declare function securedBindRouteModel<TParams extends Record<string, string>, TModel, TParam extends keyof TParams & string>(param: TParam, resolver: (id: number, request: RouteRequest<TParams>) => Promise<TModel>, authorization: RouteModelAuthorization, handler: (request: RouteRequest<TParams>, model: TModel) => Response | Promise<Response>): (request: RouteRequest<TParams>) => Promise<Response>;
9
+ declare function securedBindRouteModelByKey<TParams extends Record<string, string>, TModel, TParam extends keyof TParams & string>(param: TParam, resolver: (key: string, request: RouteRequest<TParams>) => Promise<TModel>, authorization: RouteModelAuthorization, handler: (request: RouteRequest<TParams>, model: TModel) => Response | Promise<Response>): (request: RouteRequest<TParams>) => Promise<Response>;
10
+ export type { RouteModelAuthorization };
11
+ export { securedBindRouteModel, securedBindRouteModelByKey };
@@ -1,7 +1,10 @@
1
1
  import type { OrganizationMemberRole } from "../../modules/organization/memberTypes";
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
5
  declare class MembershipService {
4
- constructor();
6
+ private readonly members;
7
+ constructor(members?: MembershipRepositoryLike);
5
8
  listOrganizationIdsForUser(userId: number): Promise<number[]>;
6
9
  getOrgRole(userId: number, organizationId: number): Promise<OrganizationMemberRole | null>;
7
10
  requireOrgAccess(organizationId: number, minimumRole?: OrganizationMemberRole, user?: AuthUser | null): Promise<OrganizationMemberRole>;
@@ -16,5 +19,6 @@ declare class MembershipService {
16
19
  removeMember(organizationId: number, userId: number): Promise<boolean>;
17
20
  }
18
21
  declare function resolveMembershipService(): MembershipService;
22
+ export type { MembershipRepositoryLike };
19
23
  export default MembershipService;
20
24
  export { resolveMembershipService };
@@ -9,6 +9,11 @@ type ExtendedQueryOptions<TEntity extends object> = QueryOptions<TEntity> & {
9
9
  };
10
10
  interface DatabaseConnection {
11
11
  unsafe<T>(query: string, params?: readonly unknown[]): Promise<T[]>;
12
+ begin?<T>(callback: (transaction: DatabaseConnection) => Promise<T>): Promise<T>;
13
+ close?(): Promise<void>;
14
+ }
15
+ interface SqlDatabaseConnection extends DatabaseConnection {
16
+ (strings: TemplateStringsArray, ...values: unknown[]): Promise<unknown[]>;
12
17
  }
13
18
  type ErrorFactory<TValue> = (value: TValue) => Error;
14
19
  declare class BaseRepository<TEntity extends object, PrimaryKey extends keyof TEntity & string> {
@@ -59,4 +64,4 @@ declare class BaseRepository<TEntity extends object, PrimaryKey extends keyof TE
59
64
  loadMorphToForChildren<TChild extends object, TParent extends object, MorphTypeKey extends keyof TChild & string, MorphIdKey extends keyof TChild & string, OwnerKey extends keyof TParent & string>(children: readonly TChild[], relation: MorphToRelation<TChild, MorphTypeKey, MorphIdKey>, repositoriesByType: ReadonlyMap<string, BaseRepository<TParent, OwnerKey>>, options?: Omit<QueryOptions<TParent>, "where">): Promise<Map<TChild[MorphIdKey], TParent>>;
60
65
  }
61
66
  export default BaseRepository;
62
- export type { DatabaseConnection };
67
+ export type { DatabaseConnection, SqlDatabaseConnection };
@@ -3,5 +3,6 @@ type ActiveDatabaseHandle = {
3
3
  };
4
4
  declare function runWithDatabaseConnection<T>(connection: ActiveDatabaseHandle, callback: () => T | Promise<T>): T | Promise<T>;
5
5
  declare function getActiveDatabaseConnection<T extends ActiveDatabaseHandle>(fallback: T): T;
6
+ declare function hasActiveDatabaseConnection(): boolean;
6
7
  export type { ActiveDatabaseHandle };
7
- export { getActiveDatabaseConnection, runWithDatabaseConnection };
8
+ export { getActiveDatabaseConnection, hasActiveDatabaseConnection, runWithDatabaseConnection };
@@ -0,0 +1,6 @@
1
+ import type { SqlDatabaseConnection } from "./baseRepository";
2
+ declare function registerDefaultDatabasePool(connection: SqlDatabaseConnection): void;
3
+ declare function getDefaultDatabasePool(): SqlDatabaseConnection;
4
+ declare function getDefaultDatabaseQuery(): SqlDatabaseConnection;
5
+ declare function resetDefaultDatabasePoolForTests(): void;
6
+ export { getDefaultDatabasePool, getDefaultDatabaseQuery, registerDefaultDatabasePool, resetDefaultDatabasePoolForTests, };
@@ -0,0 +1,3 @@
1
+ import type { SqlDatabaseConnection } from "./baseRepository";
2
+ declare function createDatabaseQueryProxy(pool: SqlDatabaseConnection): SqlDatabaseConnection;
3
+ export { createDatabaseQueryProxy };
@@ -1,4 +1,4 @@
1
- import type { DatabaseConnection } from "./baseRepository";
2
- declare function resolveRepositoryConnection(): DatabaseConnection;
3
- declare const repositoryConnection: DatabaseConnection;
1
+ import type { SqlDatabaseConnection } from "./baseRepository";
2
+ declare function resolveRepositoryConnection(): SqlDatabaseConnection;
3
+ declare const repositoryConnection: SqlDatabaseConnection;
4
4
  export { repositoryConnection, resolveRepositoryConnection };
@@ -1,11 +1,2 @@
1
- import type { Policy } from "../auth/policy";
2
- import type { RouteRequest } from "./route";
3
- interface RouteModelAuthorization {
4
- resource: string;
5
- action: keyof Policy;
6
- requireIfMatch?: boolean;
7
- }
8
- declare function securedBindRouteModel<TParams extends Record<string, string>, TModel, TParam extends keyof TParams & string>(param: TParam, resolver: (id: number, request: RouteRequest<TParams>) => Promise<TModel>, authorization: RouteModelAuthorization, handler: (request: RouteRequest<TParams>, model: TModel) => Response | Promise<Response>): (request: RouteRequest<TParams>) => Promise<Response>;
9
- declare function securedBindRouteModelByKey<TParams extends Record<string, string>, TModel, TParam extends keyof TParams & string>(param: TParam, resolver: (key: string, request: RouteRequest<TParams>) => Promise<TModel>, authorization: RouteModelAuthorization, handler: (request: RouteRequest<TParams>, model: TModel) => Response | Promise<Response>): (request: RouteRequest<TParams>) => Promise<Response>;
10
- export type { RouteModelAuthorization };
11
- export { securedBindRouteModel, securedBindRouteModelByKey };
1
+ export type { RouteModelAuthorization } from "../../bootstrap/http/securedRouteModelBinding.ts";
2
+ export { securedBindRouteModel, securedBindRouteModelByKey, } from "../../bootstrap/http/securedRouteModelBinding.ts";
@@ -5,7 +5,6 @@ interface DispatchWebhookPayload {
5
5
  payload: Record<string, unknown>;
6
6
  }
7
7
  declare class DispatchWebhookJob extends Job<DispatchWebhookPayload> {
8
- constructor();
9
8
  readonly maxAttempts = 3;
10
9
  readonly backoffMs = 2000;
11
10
  handle(payload: DispatchWebhookPayload): Promise<void>;
@@ -2,6 +2,8 @@ declare const DEFAULT_FETCH_TIMEOUT_MS = 10000;
2
2
  interface SafeFetchOptions {
3
3
  timeoutMs?: number;
4
4
  maxRedirects?: number;
5
+ allowHttp?: boolean;
6
+ resolveDns?: boolean;
5
7
  }
6
8
  declare function safeFetch(input: string, init?: RequestInit, options?: SafeFetchOptions): Promise<Response>;
7
9
  export type { SafeFetchOptions };
@@ -1,5 +1,20 @@
1
+ type DnsLookupResult = {
2
+ address: string;
3
+ family: number;
4
+ };
5
+ type DnsLookup = (hostname: string, options: {
6
+ all: true;
7
+ verbatim: true;
8
+ }) => Promise<DnsLookupResult[]>;
1
9
  declare function isBlockedHostname(hostname: string): boolean;
2
10
  declare function assertSafeOutboundUrl(rawUrl: string, options?: {
3
11
  allowHttp?: boolean;
4
12
  }): URL;
5
- export { assertSafeOutboundUrl, isBlockedHostname };
13
+ declare function isBlockedIpAddress(address: string): boolean;
14
+ declare function assertSafeOutboundUrlResolved(rawUrl: string, options?: {
15
+ allowHttp?: boolean;
16
+ resolveDns?: boolean;
17
+ }): Promise<URL>;
18
+ declare function setDnsLookupForTests(lookupFn: DnsLookup): void;
19
+ declare function resetDnsLookupForTests(): void;
20
+ export { assertSafeOutboundUrl, assertSafeOutboundUrlResolved, isBlockedHostname, isBlockedIpAddress, resetDnsLookupForTests, setDnsLookupForTests, };
@@ -4,4 +4,5 @@ type TransactionHandle = {
4
4
  };
5
5
  declare function applyTenantContextToTransaction(transaction: TransactionHandle, tenantId: number): Promise<void>;
6
6
  declare function runWithTenantDatabase<T>(tenant: TenantContext, callback: () => T | Promise<T>): Promise<T>;
7
- export { applyTenantContextToTransaction, runWithTenantDatabase };
7
+ declare function isInsideTenantDatabaseScope(tenantId?: number | undefined): boolean;
8
+ export { applyTenantContextToTransaction, isInsideTenantDatabaseScope, runWithTenantDatabase };
@@ -1,4 +1,4 @@
1
- import { type DatabaseConnection } from "./createConnection";
1
+ import type { DatabaseConnection } from "./createConnection";
2
2
  declare function getDatabase(): DatabaseConnection;
3
3
  declare function pingDatabase(connection?: DatabaseConnection): Promise<boolean>;
4
4
  declare function ensureDatabaseConnection(): Promise<DatabaseConnection>;
@@ -1,113 +1 @@
1
- // @bun
2
- // ../../src/core/errors/http.ts
3
- class HttpError extends Error {
4
- status;
5
- details;
6
- constructor(status, message, details) {
7
- super(message);
8
- this.name = new.target.name;
9
- this.status = status;
10
- this.details = details;
11
- }
12
- }
13
-
14
- class BadRequestError extends HttpError {
15
- constructor(message = "Bad Request", details) {
16
- super(400, message, details);
17
- }
18
- }
19
-
20
- class NotFoundError extends HttpError {
21
- constructor(message = "Not Found", details) {
22
- super(404, message, details);
23
- }
24
- }
25
-
26
- class ConflictError extends HttpError {
27
- constructor(message = "Conflict", details) {
28
- super(409, message, details);
29
- }
30
- }
31
-
32
- class UnprocessableEntityError extends HttpError {
33
- constructor(message = "Unprocessable Entity", details) {
34
- super(422, message, details);
35
- }
36
- }
37
-
38
- class ValidationError extends HttpError {
39
- constructor(message = "Validation failed", details) {
40
- super(422, message, details);
41
- }
42
- }
43
-
44
- class ForbiddenError extends HttpError {
45
- constructor(message = "Forbidden", details) {
46
- super(403, message, details);
47
- }
48
- }
49
-
50
- class UnauthorizedError extends HttpError {
51
- constructor(message = "Unauthorized", details) {
52
- super(401, message, details);
53
- }
54
- }
55
-
56
- class PayloadTooLargeError extends HttpError {
57
- constructor(message = "Payload Too Large", details) {
58
- super(413, message, details);
59
- }
60
- }
61
-
62
- class PreconditionFailedError extends HttpError {
63
- constructor(message = "Precondition Failed", details) {
64
- super(412, message, details);
65
- }
66
- }
67
-
68
- // ../../src/core/auth/authContext.ts
69
- import { AsyncLocalStorage } from "async_hooks";
70
- var authContext = new AsyncLocalStorage;
71
- function runWithAuthUser(user, callback) {
72
- return authContext.run(user, callback);
73
- }
74
- function currentAuthUser() {
75
- return authContext.getStore() ?? null;
76
- }
77
-
78
- // ../../src/core/auth/accessControl.ts
79
- var ROLE_RANK = {
80
- member: 1,
81
- admin: 2,
82
- owner: 3
83
- };
84
- function isGlobalAdmin(user) {
85
- return user?.role === "admin";
86
- }
87
- function hasMinimumOrgRole(role, minimum) {
88
- if (!role) {
89
- return false;
90
- }
91
- return ROLE_RANK[role] >= ROLE_RANK[minimum];
92
- }
93
- function requireAuthenticatedUser() {
94
- const user = currentAuthUser();
95
- if (!user) {
96
- throw new ForbiddenError("Authentication required.");
97
- }
98
- return user;
99
- }
100
- function resolveUserId(user) {
101
- const userId = typeof user.id === "number" ? user.id : Number(user.id);
102
- if (!Number.isInteger(userId) || userId <= 0) {
103
- throw new ForbiddenError("Invalid authenticated user.");
104
- }
105
- return userId;
106
- }
107
- export {
108
- resolveUserId,
109
- requireAuthenticatedUser,
110
- isGlobalAdmin,
111
- hasMinimumOrgRole,
112
- ROLE_RANK
113
- };
1
+ export * from "../../index.js";
@@ -1,15 +1 @@
1
- // @bun
2
- // ../../src/core/auth/authContext.ts
3
- import { AsyncLocalStorage } from "async_hooks";
4
- var authContext = new AsyncLocalStorage;
5
- function runWithAuthUser(user, callback) {
6
- return authContext.run(user, callback);
7
- }
8
- function currentAuthUser() {
9
- return authContext.getStore() ?? null;
10
- }
11
- export {
12
- runWithAuthUser,
13
- currentAuthUser,
14
- authContext
15
- };
1
+ export * from "../../index.js";