@getstrata/core 0.5.101 → 0.7.4

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 (61) hide show
  1. package/CHANGELOG.md +67 -32
  2. package/README.md +16 -35
  3. package/dist/core/auth/abilityCatalog.d.ts +2 -2
  4. package/dist/core/auth/basicAuthGuard.d.ts +9 -0
  5. package/dist/core/auth/guard.d.ts +6 -0
  6. package/dist/core/auth/jwt.d.ts +19 -0
  7. package/dist/core/auth/jwtGuard.d.ts +14 -0
  8. package/dist/core/auth/tokenAbilityChecker.d.ts +5 -0
  9. package/dist/core/cache/tags.d.ts +6 -0
  10. package/dist/core/contracts/authUserDirectory.d.ts +4 -0
  11. package/dist/core/database/dialect.d.ts +18 -0
  12. package/dist/core/database/factory.d.ts +1 -0
  13. package/dist/core/database/index.d.ts +8 -0
  14. package/dist/core/database/mysqlConnection.d.ts +12 -0
  15. package/dist/core/database/namedConnections.d.ts +15 -0
  16. package/dist/core/database/repositoryQuery.d.ts +1 -0
  17. package/dist/core/database/sqliteConnection.d.ts +7 -0
  18. package/dist/core/http/loginThrottleMiddleware.d.ts +5 -2
  19. package/dist/core/http/resources.d.ts +2 -2
  20. package/dist/core/http/response.d.ts +2 -1
  21. package/dist/core/http/statelessAuth.d.ts +8 -0
  22. package/dist/core/http/throttleResponse.d.ts +2 -0
  23. package/dist/core/runtime/frontendMode.d.ts +10 -2
  24. package/dist/entries/auth/basicAuthGuard.js +137 -0
  25. package/dist/entries/auth/jwt.js +135 -0
  26. package/dist/entries/auth/jwtGuard.js +203 -0
  27. package/dist/entries/auth/sessionGuard.js +3 -21
  28. package/dist/entries/auth/tokenAbilityChecker.js +24 -0
  29. package/dist/entries/cache/tags.js +7 -1
  30. package/dist/entries/database/connectionContext.js +1 -0
  31. package/dist/entries/database/dialect.js +1 -0
  32. package/dist/entries/database/factory.js +5 -4
  33. package/dist/entries/database/model.js +49 -32
  34. package/dist/entries/database/mysqlConnection.js +35 -0
  35. package/dist/entries/database/namedConnections.js +1 -0
  36. package/dist/entries/database/query.js +28 -15
  37. package/dist/entries/database/relationships.js +14 -6
  38. package/dist/entries/database/repositoryQuery.js +96 -81
  39. package/dist/entries/database/schema.js +28 -15
  40. package/dist/entries/database/sqliteConnection.js +34 -0
  41. package/dist/entries/facades.js +1 -1
  42. package/dist/entries/http/contentNegotiation.js +5 -2
  43. package/dist/entries/http/csrfMiddleware.js +45 -0
  44. package/dist/entries/http/loginThrottleMiddleware.js +246 -7
  45. package/dist/entries/http/memoryThrottleMiddleware.js +208 -6
  46. package/dist/entries/http/requireAbilityMiddleware.js +35 -11
  47. package/dist/entries/http/requirePasswordConfirmMiddleware.js +5 -2
  48. package/dist/entries/http/requireVerifiedMiddleware.js +5 -2
  49. package/dist/entries/http/requireWebAuthMiddleware.js +12 -3
  50. package/dist/entries/http/resources.js +4 -1
  51. package/dist/entries/http/response.js +46 -12
  52. package/dist/entries/http/statelessAuth.js +48 -0
  53. package/dist/entries/http/throttleMiddleware.js +208 -6
  54. package/dist/entries/http/webErrorResponse.js +35 -11
  55. package/dist/entries/http/webFormRequest.js +5 -2
  56. package/dist/entries/mail/mailer.js +1 -1
  57. package/dist/entries/openapi/generator.js +26 -3
  58. package/dist/entries/runtime/frontendMode.js +39 -10
  59. package/dist/framework/public-api.d.ts +11 -1
  60. package/dist/index.js +873 -270
  61. package/package.json +56 -5
@@ -1,6 +1,14 @@
1
- type FrontendMode = "api" | "server-htmx" | "spa-react";
1
+ declare const FRONTEND_MODES: readonly ["api", "server-htmx", "spa-react", "hybrid"];
2
+ declare const DEFAULT_SPA_PREFIX = "/app";
3
+ type FrontendMode = (typeof FRONTEND_MODES)[number];
4
+ declare const FRONTEND_MODE_PATTERN: RegExp;
5
+ declare function parseFrontendMode(value: string | undefined): FrontendMode;
2
6
  declare function readFrontendMode(): FrontendMode;
7
+ declare function isViewsMode(mode: FrontendMode): boolean;
8
+ declare function isSpaMode(mode: FrontendMode): boolean;
3
9
  declare function isViewsEnabled(): boolean;
4
10
  declare function isSpaEnabled(): boolean;
11
+ declare function normalizeSpaPrefix(value: string | undefined): string;
12
+ declare function readSpaPrefix(): string;
5
13
  export type { FrontendMode };
6
- export { isSpaEnabled, isViewsEnabled, readFrontendMode };
14
+ export { DEFAULT_SPA_PREFIX, FRONTEND_MODE_PATTERN, FRONTEND_MODES, isSpaEnabled, isSpaMode, isViewsEnabled, isViewsMode, normalizeSpaPrefix, parseFrontendMode, readFrontendMode, readSpaPrefix, };
@@ -0,0 +1,137 @@
1
+ // @bun
2
+ // ../../src/core/contracts/serviceTokens.ts
3
+ var CORE_CONFIG_TOKEN = "core.config";
4
+ var CORE_CACHE_TOKEN = "core.cache";
5
+ var CORE_QUEUE_TOKEN = "core.queue";
6
+ var CORE_EVENT_BUS_TOKEN = "core.eventBus";
7
+ var CORE_POLICY_GATE_TOKEN = "core.policyGate";
8
+ var CORE_AUTH_TOKEN = "core.auth";
9
+ var CORE_TOKEN_SERVICE_TOKEN = "core.tokenService";
10
+ var CORE_ABILITY_CHECKER_TOKEN = "core.abilityChecker";
11
+ var CORE_AUTH_USER_DIRECTORY_TOKEN = "core.authUserDirectory";
12
+ function isAbilityChecker(value) {
13
+ if (!value || typeof value !== "object") {
14
+ return false;
15
+ }
16
+ const candidate = value;
17
+ return typeof candidate.tokenCan === "function" && typeof candidate.requireAbility === "function";
18
+ }
19
+ function isAuthUserDirectory(value) {
20
+ if (!value || typeof value !== "object") {
21
+ return false;
22
+ }
23
+ return typeof value.findByIdOrThrow === "function";
24
+ }
25
+ function resolveAbilityChecker(container) {
26
+ if (container.has(CORE_ABILITY_CHECKER_TOKEN)) {
27
+ return container.resolve(CORE_ABILITY_CHECKER_TOKEN);
28
+ }
29
+ return container.resolve(CORE_TOKEN_SERVICE_TOKEN);
30
+ }
31
+ function resolveAuthUserDirectory(container) {
32
+ if (container.has(CORE_AUTH_USER_DIRECTORY_TOKEN)) {
33
+ const directory = container.resolve(CORE_AUTH_USER_DIRECTORY_TOKEN);
34
+ if (isAuthUserDirectory(directory)) {
35
+ return directory;
36
+ }
37
+ }
38
+ if (container.has(CORE_TOKEN_SERVICE_TOKEN)) {
39
+ const tokenService = container.resolve(CORE_TOKEN_SERVICE_TOKEN);
40
+ if (isAuthUserDirectory(tokenService)) {
41
+ return tokenService;
42
+ }
43
+ }
44
+ return null;
45
+ }
46
+
47
+ // ../../src/core/http/statelessAuth.ts
48
+ function authorizationScheme(request) {
49
+ const header = request.headers.get("authorization")?.trim() ?? "";
50
+ const scheme = header.split(/\s+/, 1)[0];
51
+ return scheme ? scheme.toLowerCase() : "";
52
+ }
53
+ function requestUsesHeaderCredentials(request) {
54
+ const scheme = authorizationScheme(request);
55
+ return scheme === "bearer" || scheme === "basic";
56
+ }
57
+ function readBearerToken(request) {
58
+ const header = request.headers.get("authorization")?.trim() ?? "";
59
+ if (!header.toLowerCase().startsWith("bearer ")) {
60
+ return null;
61
+ }
62
+ const token = header.slice("Bearer ".length).trim();
63
+ return token.length > 0 ? token : null;
64
+ }
65
+ function readBasicCredentials(request) {
66
+ const header = request.headers.get("authorization")?.trim() ?? "";
67
+ if (!header.toLowerCase().startsWith("basic ")) {
68
+ return null;
69
+ }
70
+ const encoded = header.slice("Basic ".length).trim();
71
+ if (!encoded) {
72
+ return null;
73
+ }
74
+ try {
75
+ const decoded = Buffer.from(encoded, "base64").toString("utf8");
76
+ const separator = decoded.indexOf(":");
77
+ if (separator < 0) {
78
+ return null;
79
+ }
80
+ return {
81
+ username: decoded.slice(0, separator),
82
+ password: decoded.slice(separator + 1)
83
+ };
84
+ } catch {
85
+ return null;
86
+ }
87
+ }
88
+
89
+ // ../../src/core/auth/password.ts
90
+ async function hashPassword(password) {
91
+ return await Bun.password.hash(password, {
92
+ algorithm: "bcrypt",
93
+ cost: 10
94
+ });
95
+ }
96
+ async function verifyPassword(password, passwordHash) {
97
+ return await Bun.password.verify(password, passwordHash);
98
+ }
99
+
100
+ // ../../src/core/auth/basicAuthGuard.ts
101
+ class BasicAuthGuard {
102
+ container;
103
+ constructor(container) {
104
+ this.container = container;
105
+ }
106
+ async resolve(request) {
107
+ const credentials = readBasicCredentials(request);
108
+ if (!credentials) {
109
+ return null;
110
+ }
111
+ const directory = resolveAuthUserDirectory(this.container);
112
+ if (!directory) {
113
+ return null;
114
+ }
115
+ if (typeof directory.verifyCredentials === "function") {
116
+ return await directory.verifyCredentials(credentials.username, credentials.password);
117
+ }
118
+ if (typeof directory.findByEmail !== "function") {
119
+ return null;
120
+ }
121
+ const record = await directory.findByEmail(credentials.username);
122
+ if (!record?.password) {
123
+ return null;
124
+ }
125
+ if (!await verifyPassword(credentials.password, record.password)) {
126
+ return null;
127
+ }
128
+ return {
129
+ id: record.id,
130
+ role: record.role,
131
+ emailVerifiedAt: record.email_verified_at ?? null
132
+ };
133
+ }
134
+ }
135
+ export {
136
+ BasicAuthGuard
137
+ };
@@ -0,0 +1,135 @@
1
+ // @bun
2
+ // ../../src/core/auth/jwt.ts
3
+ import { createHmac, timingSafeEqual } from "crypto";
4
+
5
+ // ../../src/core/runtime/appKeyPrefix.ts
6
+ function appKeyPrefix() {
7
+ return process.env.APP_KEY_PREFIX?.trim() || "strata";
8
+ }
9
+ function appCookieName(kind) {
10
+ return `${appKeyPrefix()}_${kind}`;
11
+ }
12
+ function appDevSecret(kind) {
13
+ return `${appKeyPrefix()}-dev-${kind}`;
14
+ }
15
+ function namespacedRedisKey(kind) {
16
+ return `${appKeyPrefix()}:${kind}`;
17
+ }
18
+ function smtpEhloHost() {
19
+ const raw = process.env.MAIL_EHLO?.trim() || `${appKeyPrefix()}.local`;
20
+ const safe = raw.replace(/[^a-zA-Z0-9.-]/g, "");
21
+ return safe || "strata.local";
22
+ }
23
+ function siemEventType() {
24
+ return process.env.SIEM_EVENT_TYPE?.trim() || `${appKeyPrefix()}.audit`;
25
+ }
26
+ function appUserAgent() {
27
+ return process.env.APP_USER_AGENT?.trim() || appKeyPrefix();
28
+ }
29
+ function otelServiceName() {
30
+ return process.env.OTEL_SERVICE_NAME?.trim() || `${appKeyPrefix()}-api`;
31
+ }
32
+ function webhookSignatureHeader() {
33
+ return process.env.WEBHOOK_SIGNATURE_HEADER?.trim() || `x-${appKeyPrefix()}-signature`;
34
+ }
35
+ function appDisplayName() {
36
+ return process.env.APP_NAME?.trim() || "Strata";
37
+ }
38
+ function appEnv() {
39
+ return process.env.APP_ENV?.trim() || "local";
40
+ }
41
+ function appUrl() {
42
+ return (process.env.APP_URL?.trim() || "http://localhost:3000").replace(/\/$/, "");
43
+ }
44
+ function apiPrefix() {
45
+ const raw = process.env.API_PREFIX?.trim() || "/api/v1";
46
+ const withSlash = raw.startsWith("/") ? raw : `/${raw}`;
47
+ const trimmed = withSlash.replace(/\/+$/, "");
48
+ return trimmed || "/api/v1";
49
+ }
50
+ function sdkClientClassName() {
51
+ const override = process.env.APP_SDK_CLASS?.trim();
52
+ if (override && /^[A-Za-z_][A-Za-z0-9_]*$/.test(override)) {
53
+ return override;
54
+ }
55
+ const fromName = appDisplayName().replace(/[^A-Za-z0-9]/g, "");
56
+ return fromName ? `${fromName}Client` : "AppClient";
57
+ }
58
+
59
+ // ../../src/core/auth/jwt.ts
60
+ function resolveJwtSecret(secret) {
61
+ return secret?.trim() || process.env.JWT_SECRET?.trim() || process.env.SESSION_SECRET?.trim() || appDevSecret("jwt-secret");
62
+ }
63
+ function jwtTtlSeconds(override) {
64
+ if (typeof override === "number" && Number.isInteger(override) && override > 0) {
65
+ return override;
66
+ }
67
+ const parsed = Number.parseInt(process.env.JWT_TTL_SECONDS ?? "", 10);
68
+ return Number.isInteger(parsed) && parsed > 0 ? parsed : 3600;
69
+ }
70
+ function encodeJson(value) {
71
+ return Buffer.from(JSON.stringify(value)).toString("base64url");
72
+ }
73
+ function decodeJson(value) {
74
+ try {
75
+ return JSON.parse(Buffer.from(value, "base64url").toString("utf8"));
76
+ } catch {
77
+ return null;
78
+ }
79
+ }
80
+ function signPart(headerAndPayload, secret) {
81
+ return createHmac("sha256", secret).update(headerAndPayload).digest("base64url");
82
+ }
83
+ function signaturesMatch(left, right) {
84
+ const leftBuffer = Buffer.from(left);
85
+ const rightBuffer = Buffer.from(right);
86
+ if (leftBuffer.length !== rightBuffer.length) {
87
+ return false;
88
+ }
89
+ return timingSafeEqual(leftBuffer, rightBuffer);
90
+ }
91
+ function signJwt(payload, options = {}) {
92
+ const now = Math.floor(Date.now() / 1000);
93
+ const body = {
94
+ ...payload,
95
+ iat: now,
96
+ exp: now + jwtTtlSeconds(options.ttlSeconds)
97
+ };
98
+ const header = encodeJson({ alg: "HS256", typ: "JWT" });
99
+ const data = encodeJson(body);
100
+ const unsigned = `${header}.${data}`;
101
+ const signature = signPart(unsigned, resolveJwtSecret(options.secret));
102
+ return `${unsigned}.${signature}`;
103
+ }
104
+ function verifyJwt(token, secret) {
105
+ const parts = token.split(".");
106
+ if (parts.length !== 3) {
107
+ return null;
108
+ }
109
+ const [header, data, signature] = parts;
110
+ if (!header || !data || !signature) {
111
+ return null;
112
+ }
113
+ const expected = signPart(`${header}.${data}`, resolveJwtSecret(secret));
114
+ if (!signaturesMatch(signature, expected)) {
115
+ return null;
116
+ }
117
+ const parsedHeader = decodeJson(header);
118
+ if (parsedHeader?.alg !== "HS256") {
119
+ return null;
120
+ }
121
+ const payload = decodeJson(data);
122
+ if (!payload || payload.sub === undefined || payload.sub === null) {
123
+ return null;
124
+ }
125
+ if (typeof payload.exp === "number" && payload.exp * 1000 <= Date.now()) {
126
+ return null;
127
+ }
128
+ return payload;
129
+ }
130
+ export {
131
+ jwtTtlSeconds,
132
+ resolveJwtSecret,
133
+ signJwt,
134
+ verifyJwt
135
+ };
@@ -0,0 +1,203 @@
1
+ // @bun
2
+ // ../../src/core/http/statelessAuth.ts
3
+ function authorizationScheme(request) {
4
+ const header = request.headers.get("authorization")?.trim() ?? "";
5
+ const scheme = header.split(/\s+/, 1)[0];
6
+ return scheme ? scheme.toLowerCase() : "";
7
+ }
8
+ function requestUsesHeaderCredentials(request) {
9
+ const scheme = authorizationScheme(request);
10
+ return scheme === "bearer" || scheme === "basic";
11
+ }
12
+ function readBearerToken(request) {
13
+ const header = request.headers.get("authorization")?.trim() ?? "";
14
+ if (!header.toLowerCase().startsWith("bearer ")) {
15
+ return null;
16
+ }
17
+ const token = header.slice("Bearer ".length).trim();
18
+ return token.length > 0 ? token : null;
19
+ }
20
+ function readBasicCredentials(request) {
21
+ const header = request.headers.get("authorization")?.trim() ?? "";
22
+ if (!header.toLowerCase().startsWith("basic ")) {
23
+ return null;
24
+ }
25
+ const encoded = header.slice("Basic ".length).trim();
26
+ if (!encoded) {
27
+ return null;
28
+ }
29
+ try {
30
+ const decoded = Buffer.from(encoded, "base64").toString("utf8");
31
+ const separator = decoded.indexOf(":");
32
+ if (separator < 0) {
33
+ return null;
34
+ }
35
+ return {
36
+ username: decoded.slice(0, separator),
37
+ password: decoded.slice(separator + 1)
38
+ };
39
+ } catch {
40
+ return null;
41
+ }
42
+ }
43
+
44
+ // ../../src/core/auth/jwt.ts
45
+ import { createHmac, timingSafeEqual } from "crypto";
46
+
47
+ // ../../src/core/runtime/appKeyPrefix.ts
48
+ function appKeyPrefix() {
49
+ return process.env.APP_KEY_PREFIX?.trim() || "strata";
50
+ }
51
+ function appCookieName(kind) {
52
+ return `${appKeyPrefix()}_${kind}`;
53
+ }
54
+ function appDevSecret(kind) {
55
+ return `${appKeyPrefix()}-dev-${kind}`;
56
+ }
57
+ function namespacedRedisKey(kind) {
58
+ return `${appKeyPrefix()}:${kind}`;
59
+ }
60
+ function smtpEhloHost() {
61
+ const raw = process.env.MAIL_EHLO?.trim() || `${appKeyPrefix()}.local`;
62
+ const safe = raw.replace(/[^a-zA-Z0-9.-]/g, "");
63
+ return safe || "strata.local";
64
+ }
65
+ function siemEventType() {
66
+ return process.env.SIEM_EVENT_TYPE?.trim() || `${appKeyPrefix()}.audit`;
67
+ }
68
+ function appUserAgent() {
69
+ return process.env.APP_USER_AGENT?.trim() || appKeyPrefix();
70
+ }
71
+ function otelServiceName() {
72
+ return process.env.OTEL_SERVICE_NAME?.trim() || `${appKeyPrefix()}-api`;
73
+ }
74
+ function webhookSignatureHeader() {
75
+ return process.env.WEBHOOK_SIGNATURE_HEADER?.trim() || `x-${appKeyPrefix()}-signature`;
76
+ }
77
+ function appDisplayName() {
78
+ return process.env.APP_NAME?.trim() || "Strata";
79
+ }
80
+ function appEnv() {
81
+ return process.env.APP_ENV?.trim() || "local";
82
+ }
83
+ function appUrl() {
84
+ return (process.env.APP_URL?.trim() || "http://localhost:3000").replace(/\/$/, "");
85
+ }
86
+ function apiPrefix() {
87
+ const raw = process.env.API_PREFIX?.trim() || "/api/v1";
88
+ const withSlash = raw.startsWith("/") ? raw : `/${raw}`;
89
+ const trimmed = withSlash.replace(/\/+$/, "");
90
+ return trimmed || "/api/v1";
91
+ }
92
+ function sdkClientClassName() {
93
+ const override = process.env.APP_SDK_CLASS?.trim();
94
+ if (override && /^[A-Za-z_][A-Za-z0-9_]*$/.test(override)) {
95
+ return override;
96
+ }
97
+ const fromName = appDisplayName().replace(/[^A-Za-z0-9]/g, "");
98
+ return fromName ? `${fromName}Client` : "AppClient";
99
+ }
100
+
101
+ // ../../src/core/auth/jwt.ts
102
+ function resolveJwtSecret(secret) {
103
+ return secret?.trim() || process.env.JWT_SECRET?.trim() || process.env.SESSION_SECRET?.trim() || appDevSecret("jwt-secret");
104
+ }
105
+ function jwtTtlSeconds(override) {
106
+ if (typeof override === "number" && Number.isInteger(override) && override > 0) {
107
+ return override;
108
+ }
109
+ const parsed = Number.parseInt(process.env.JWT_TTL_SECONDS ?? "", 10);
110
+ return Number.isInteger(parsed) && parsed > 0 ? parsed : 3600;
111
+ }
112
+ function encodeJson(value) {
113
+ return Buffer.from(JSON.stringify(value)).toString("base64url");
114
+ }
115
+ function decodeJson(value) {
116
+ try {
117
+ return JSON.parse(Buffer.from(value, "base64url").toString("utf8"));
118
+ } catch {
119
+ return null;
120
+ }
121
+ }
122
+ function signPart(headerAndPayload, secret) {
123
+ return createHmac("sha256", secret).update(headerAndPayload).digest("base64url");
124
+ }
125
+ function signaturesMatch(left, right) {
126
+ const leftBuffer = Buffer.from(left);
127
+ const rightBuffer = Buffer.from(right);
128
+ if (leftBuffer.length !== rightBuffer.length) {
129
+ return false;
130
+ }
131
+ return timingSafeEqual(leftBuffer, rightBuffer);
132
+ }
133
+ function signJwt(payload, options = {}) {
134
+ const now = Math.floor(Date.now() / 1000);
135
+ const body = {
136
+ ...payload,
137
+ iat: now,
138
+ exp: now + jwtTtlSeconds(options.ttlSeconds)
139
+ };
140
+ const header = encodeJson({ alg: "HS256", typ: "JWT" });
141
+ const data = encodeJson(body);
142
+ const unsigned = `${header}.${data}`;
143
+ const signature = signPart(unsigned, resolveJwtSecret(options.secret));
144
+ return `${unsigned}.${signature}`;
145
+ }
146
+ function verifyJwt(token, secret) {
147
+ const parts = token.split(".");
148
+ if (parts.length !== 3) {
149
+ return null;
150
+ }
151
+ const [header, data, signature] = parts;
152
+ if (!header || !data || !signature) {
153
+ return null;
154
+ }
155
+ const expected = signPart(`${header}.${data}`, resolveJwtSecret(secret));
156
+ if (!signaturesMatch(signature, expected)) {
157
+ return null;
158
+ }
159
+ const parsedHeader = decodeJson(header);
160
+ if (parsedHeader?.alg !== "HS256") {
161
+ return null;
162
+ }
163
+ const payload = decodeJson(data);
164
+ if (!payload || payload.sub === undefined || payload.sub === null) {
165
+ return null;
166
+ }
167
+ if (typeof payload.exp === "number" && payload.exp * 1000 <= Date.now()) {
168
+ return null;
169
+ }
170
+ return payload;
171
+ }
172
+
173
+ // ../../src/core/auth/jwtGuard.ts
174
+ function authUserFromJwt(payload) {
175
+ return {
176
+ id: payload.sub,
177
+ ...payload.role ? { role: String(payload.role) } : {},
178
+ ...Array.isArray(payload.abilities) ? { abilities: payload.abilities.map(String) } : {},
179
+ ...payload.emailVerifiedAt !== undefined ? { emailVerifiedAt: payload.emailVerifiedAt } : {}
180
+ };
181
+ }
182
+
183
+ class JwtGuard {
184
+ options;
185
+ constructor(options = {}) {
186
+ this.options = options;
187
+ }
188
+ resolve(request) {
189
+ const token = readBearerToken(request);
190
+ if (token?.split(".").length !== 3) {
191
+ return null;
192
+ }
193
+ const payload = verifyJwt(token, this.options.secret);
194
+ if (!payload) {
195
+ return null;
196
+ }
197
+ return authUserFromJwt(payload);
198
+ }
199
+ }
200
+ export {
201
+ JwtGuard,
202
+ authUserFromJwt
203
+ };
@@ -46,35 +46,17 @@ function resolveAuthUserDirectory(container) {
46
46
 
47
47
  // ../../src/core/auth/abilityCatalog.ts
48
48
  var MEMBER_ABILITIES = [
49
- "organizations:read",
50
- "organizations:create",
51
- "projects:read",
52
- "projects:create",
53
- "tasks:read",
54
- "tasks:create",
55
- "comments:read",
56
- "comments:create",
57
- "attachments:read",
58
- "attachments:create",
49
+ "profile:read",
59
50
  "auth:tokens:read",
60
51
  "auth:tokens:write",
61
52
  "auth:tokens:delete"
62
53
  ];
63
54
  var ADMIN_ABILITIES = [
64
55
  ...MEMBER_ABILITIES,
65
- "organizations:create",
66
- "organizations:update",
67
- "organizations:delete",
68
- "projects:update",
69
- "projects:delete",
70
- "tasks:update",
71
- "tasks:delete",
72
- "comments:update",
73
- "comments:delete",
74
- "attachments:delete",
75
56
  "webhooks:read",
76
57
  "webhooks:write",
77
- "audit:read"
58
+ "audit:read",
59
+ "audit:export"
78
60
  ];
79
61
  var PLATFORM_ADMIN_ABILITIES = ["*"];
80
62
  function resolveAbilitiesForRole(role) {
@@ -0,0 +1,24 @@
1
+ // @bun
2
+ // ../../src/core/auth/tokenAbilityChecker.ts
3
+ import { ForbiddenError } from "@getstrata/core/errors/http";
4
+ function tokenCan(user, ability) {
5
+ if (!user) {
6
+ return false;
7
+ }
8
+ const abilities = user.abilities ?? [];
9
+ return abilities.includes("*") || abilities.includes(ability);
10
+ }
11
+ function createTokenAbilityChecker() {
12
+ return {
13
+ tokenCan,
14
+ requireAbility(user, ability) {
15
+ if (!tokenCan(user, ability)) {
16
+ throw new ForbiddenError(`Missing ability: ${ability}`);
17
+ }
18
+ }
19
+ };
20
+ }
21
+ export {
22
+ createTokenAbilityChecker,
23
+ tokenCan
24
+ };
@@ -6,7 +6,13 @@ var CACHE_TAGS = {
6
6
  tasks: "tasks",
7
7
  comments: "comments",
8
8
  attachments: "attachments",
9
- reports: "reports"
9
+ reports: "reports",
10
+ users: "users",
11
+ departments: "departments",
12
+ positions: "positions",
13
+ applications: "applications",
14
+ careers: "careers",
15
+ offers: "offers"
10
16
  };
11
17
  export {
12
18
  CACHE_TAGS
@@ -0,0 +1 @@
1
+ export * from "../../index.js";
@@ -0,0 +1 @@
1
+ export * from "../../index.js";
@@ -151,10 +151,11 @@ class Factory {
151
151
  return made;
152
152
  }
153
153
  async createOne(overrides = {}) {
154
- const created = await this.persist(this.insertable(this.makeOne(overrides)));
155
- for (const child of this.children) {
156
- await child.factory.for(created, child.foreignKey).create();
157
- }
154
+ return this.persistCreated(this.makeOne(overrides));
155
+ }
156
+ async persistCreated(record) {
157
+ const created = await this.persist(this.insertable(record));
158
+ await Promise.all(this.children.map((child) => child.factory.for(created, child.foreignKey).create()));
158
159
  for (const callback of this.afterCreatingCallbacks) {
159
160
  await callback(created);
160
161
  }