@getstrata/core 0.4.0 → 0.5.1

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 (114) hide show
  1. package/dist/bootstrap/httpKernel.d.ts +2 -0
  2. package/dist/config/queue.d.ts +8 -0
  3. package/dist/config/rateLimit.d.ts +2 -1
  4. package/dist/core/database/baseRepository.d.ts +16 -4
  5. package/dist/core/database/index.d.ts +10 -4
  6. package/dist/core/database/migrations/advisoryLock.d.ts +6 -0
  7. package/dist/core/database/migrations/runner.d.ts +8 -2
  8. package/dist/core/database/migrations/types.d.ts +1 -0
  9. package/dist/core/database/model.d.ts +46 -17
  10. package/dist/core/database/query.d.ts +25 -14
  11. package/dist/core/database/relationships.d.ts +32 -2
  12. package/dist/core/database/repositoryQuery.d.ts +16 -1
  13. package/dist/core/database/schema/blueprint.d.ts +47 -0
  14. package/dist/core/database/schema/columnDefinition.d.ts +36 -0
  15. package/dist/core/database/schema/driver.d.ts +8 -0
  16. package/dist/core/database/schema/errors.d.ts +4 -0
  17. package/dist/core/database/schema/grammars/compileStatements.d.ts +8 -0
  18. package/dist/core/database/schema/grammars/createGrammar.d.ts +4 -0
  19. package/dist/core/database/schema/grammars/grammar.d.ts +10 -0
  20. package/dist/core/database/schema/grammars/index.d.ts +7 -0
  21. package/dist/core/database/schema/grammars/mysqlGrammar.d.ts +2 -0
  22. package/dist/core/database/schema/grammars/postgresGrammar.d.ts +2 -0
  23. package/dist/core/database/schema/grammars/sqliteGrammar.d.ts +2 -0
  24. package/dist/core/database/schema/index.d.ts +12 -0
  25. package/dist/core/database/schema/schema.d.ts +21 -0
  26. package/dist/core/database/seeders/runner.d.ts +6 -0
  27. package/dist/core/database/seeders/types.d.ts +8 -0
  28. package/dist/core/database/types.d.ts +39 -2
  29. package/dist/core/database/whereBuilder.d.ts +17 -0
  30. package/dist/core/http/index.d.ts +1 -1
  31. package/dist/core/http/securedRouteModelBinding.d.ts +2 -1
  32. package/dist/core/lifecycle/gracefulShutdown.d.ts +6 -0
  33. package/dist/core/pagination/index.d.ts +11 -1
  34. package/dist/core/queue/failedJobRepository.d.ts +6 -0
  35. package/dist/core/queue/failedJobService.d.ts +15 -0
  36. package/dist/core/queue/failedJobTable.d.ts +3 -0
  37. package/dist/core/queue/jobRegistry.d.ts +14 -0
  38. package/dist/core/queue/jobRunner.d.ts +9 -0
  39. package/dist/core/queue/publicQueue.d.ts +15 -0
  40. package/dist/core/queue/redisQueue.d.ts +29 -0
  41. package/dist/core/queue/resilientQueue.d.ts +9 -0
  42. package/dist/core/queue/types.d.ts +8 -0
  43. package/dist/core/scheduler/schedule.d.ts +15 -0
  44. package/dist/entries/auth/accessControl.js +113 -0
  45. package/dist/entries/auth/authContext.js +15 -0
  46. package/dist/entries/auth/guard.js +2871 -0
  47. package/dist/entries/auth/membershipContext.js +276 -0
  48. package/dist/entries/auth/membershipScope.js +390 -0
  49. package/dist/entries/auth/membershipService.js +477 -0
  50. package/dist/entries/auth/oauth/oidcProvider.js +49 -0
  51. package/dist/entries/auth/oauth/providers.js +71 -0
  52. package/dist/entries/auth/oauth/samlProvider.js +26 -0
  53. package/dist/entries/auth/oauth/types.js +1 -0
  54. package/dist/entries/auth/password.js +15 -0
  55. package/dist/entries/auth/policy.js +134 -0
  56. package/dist/entries/auth/sessionCookie.js +75 -0
  57. package/dist/entries/auth/tokenHash.js +17 -0
  58. package/dist/entries/cache/tags.js +13 -0
  59. package/dist/entries/crypto/fieldEncryption.js +93 -0
  60. package/dist/entries/crypto/mfaSecret.js +105 -0
  61. package/dist/entries/database/factory.js +17 -0
  62. package/dist/entries/database/seeders.js +30 -0
  63. package/dist/entries/database/types.js +1 -0
  64. package/dist/entries/database.js +2219 -0
  65. package/dist/entries/errors/http.js +78 -0
  66. package/dist/entries/http/contentNegotiation.js +26 -0
  67. package/dist/entries/http/csrfToken.js +147 -0
  68. package/dist/entries/http/etag.js +170 -0
  69. package/dist/entries/http/flashSession.js +106 -0
  70. package/dist/entries/http/middleware.js +68 -0
  71. package/dist/entries/http/parseFormBody.js +88 -0
  72. package/dist/entries/http/requestMetaContext.js +18 -0
  73. package/dist/entries/http/resources.js +19 -0
  74. package/dist/entries/http/webErrorResponse.js +3144 -0
  75. package/dist/entries/http/webFormRequest.js +293 -0
  76. package/dist/entries/http.js +3925 -0
  77. package/dist/entries/jobs/dispatchWebhookJob.js +342 -0
  78. package/dist/entries/lifecycle/gracefulShutdown.js +50 -0
  79. package/dist/entries/metrics/prometheus.js +70 -0
  80. package/dist/entries/pagination.js +14 -0
  81. package/dist/entries/queue/createAppQueue.js +2849 -0
  82. package/dist/entries/queue/failedJobService.js +38 -0
  83. package/dist/entries/queue/jobRegistry.js +32 -0
  84. package/dist/entries/queue/jobRunner.js +75 -0
  85. package/dist/entries/queue/publicQueue.js +2477 -0
  86. package/dist/entries/queue/queueMetrics.js +2899 -0
  87. package/dist/entries/queue/types.js +1 -0
  88. package/dist/entries/security/oauthState.js +75 -0
  89. package/dist/entries/security/publicReads.js +33 -0
  90. package/dist/entries/security/safeUrl.js +143 -0
  91. package/dist/entries/security/securityEvents.js +41 -0
  92. package/dist/entries/security/stripeWebhook.js +115 -0
  93. package/dist/entries/security/tokenExpiry.js +16 -0
  94. package/dist/entries/security/totp.js +51 -0
  95. package/dist/entries/storage/storage.js +123 -0
  96. package/dist/entries/tenant/tenantContext.js +30 -0
  97. package/dist/entries/tenant/tenantMiddleware.js +312 -0
  98. package/dist/entries/tracing/traceContext.js +15 -0
  99. package/dist/entries/validation/rules.js +232 -0
  100. package/dist/entries/view.js +3073 -0
  101. package/dist/framework/public-api.d.ts +65 -38
  102. package/dist/index.js +3199 -308
  103. package/dist/modules/user/apiTokenRepository.d.ts +1 -1
  104. package/dist/modules/user/apiTokenTable.d.ts +1 -1
  105. package/dist/modules/user/authService.d.ts +1 -1
  106. package/dist/modules/user/notificationRepository.d.ts +1 -1
  107. package/dist/modules/user/notificationService.d.ts +1 -1
  108. package/dist/modules/user/notificationTable.d.ts +1 -1
  109. package/dist/modules/user/oauthIdentityRepository.d.ts +2 -2
  110. package/dist/modules/user/provider.d.ts +1 -1
  111. package/dist/modules/user/repository.d.ts +1 -1
  112. package/dist/modules/user/table.d.ts +1 -1
  113. package/dist/modules/user/tokenService.d.ts +1 -1
  114. package/package.json +289 -3
@@ -0,0 +1,134 @@
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/policy.ts
79
+ class Policy {
80
+ constructor() {}
81
+ view(_user, _resource) {
82
+ return false;
83
+ }
84
+ create(_user) {
85
+ return false;
86
+ }
87
+ update(_user, _resource) {
88
+ return false;
89
+ }
90
+ delete(_user, _resource) {
91
+ return false;
92
+ }
93
+ }
94
+ var BLOCKED_POLICY_ACTIONS = new Set([
95
+ "constructor",
96
+ "toString",
97
+ "valueOf",
98
+ "hasOwnProperty",
99
+ "isPrototypeOf",
100
+ "propertyIsEnumerable",
101
+ "__proto__"
102
+ ]);
103
+
104
+ class PolicyGate {
105
+ constructor() {}
106
+ policies = new Map;
107
+ register(resource, policy) {
108
+ this.policies.set(resource, policy);
109
+ }
110
+ allows(resource, action, user, model) {
111
+ const policy = this.policies.get(resource);
112
+ if (!policy) {
113
+ return false;
114
+ }
115
+ if (BLOCKED_POLICY_ACTIONS.has(action) || !(action in policy)) {
116
+ return false;
117
+ }
118
+ const handler = policy[action];
119
+ if (typeof handler !== "function") {
120
+ return false;
121
+ }
122
+ const resolvedUser = user === undefined ? currentAuthUser() : user;
123
+ return model === undefined ? handler.call(policy, resolvedUser) : handler.call(policy, resolvedUser, model);
124
+ }
125
+ authorize(resource, action, user, model) {
126
+ if (!this.allows(resource, action, user, model)) {
127
+ throw new ForbiddenError;
128
+ }
129
+ }
130
+ }
131
+ export {
132
+ PolicyGate,
133
+ Policy
134
+ };
@@ -0,0 +1,75 @@
1
+ // @bun
2
+ // ../../src/core/auth/sessionCookie.ts
3
+ import { createHmac, timingSafeEqual } from "crypto";
4
+ var SESSION_COOKIE = "workhub_session";
5
+ var SESSION_TTL_SECONDS = 60 * 60 * 24 * 7;
6
+ function resolveSessionSecret() {
7
+ return process.env.SESSION_SECRET?.trim() || process.env.OAUTH_STATE_SECRET?.trim() || process.env.ADMIN_API_TOKEN?.trim() || "workhub-dev-session-secret";
8
+ }
9
+ function signSession(userId, issuedAt) {
10
+ const payload = `${userId}.${issuedAt}`;
11
+ const signature = createHmac("sha256", resolveSessionSecret()).update(payload).digest("hex");
12
+ return `${payload}.${signature}`;
13
+ }
14
+ function readCookieValue(request, cookieName) {
15
+ const cookieHeader = request.headers.get("cookie");
16
+ if (!cookieHeader) {
17
+ return null;
18
+ }
19
+ for (const part of cookieHeader.split(";")) {
20
+ const [name, ...rest] = part.trim().split("=");
21
+ if (name === cookieName) {
22
+ return decodeURIComponent(rest.join("="));
23
+ }
24
+ }
25
+ return null;
26
+ }
27
+ function readSessionUserId(request) {
28
+ const cookieValue = readCookieValue(request, SESSION_COOKIE);
29
+ if (!cookieValue) {
30
+ return null;
31
+ }
32
+ const parts = cookieValue.split(".");
33
+ if (parts.length !== 3) {
34
+ return null;
35
+ }
36
+ const [userIdRaw, issuedAtRaw, cookieSignature] = parts;
37
+ const userId = Number.parseInt(String(userIdRaw), 10);
38
+ const issuedAt = Number.parseInt(String(issuedAtRaw), 10);
39
+ if (!Number.isInteger(userId) || userId <= 0 || !Number.isFinite(issuedAt)) {
40
+ return null;
41
+ }
42
+ if (Date.now() - issuedAt > SESSION_TTL_SECONDS * 1000) {
43
+ return null;
44
+ }
45
+ const expectedSignature = signSession(userId, issuedAt).split(".").pop();
46
+ if (!expectedSignature || !cookieSignature) {
47
+ return null;
48
+ }
49
+ const expectedBuffer = Buffer.from(expectedSignature);
50
+ const actualBuffer = Buffer.from(cookieSignature);
51
+ if (expectedBuffer.length !== actualBuffer.length) {
52
+ return null;
53
+ }
54
+ if (!timingSafeEqual(expectedBuffer, actualBuffer)) {
55
+ return null;
56
+ }
57
+ return userId;
58
+ }
59
+ function createSessionCookie(userId) {
60
+ const issuedAt = Date.now();
61
+ const value = signSession(userId, issuedAt);
62
+ const secure = process.env.APP_ENV === "production" ? "; Secure" : "";
63
+ return `${SESSION_COOKIE}=${encodeURIComponent(value)}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${SESSION_TTL_SECONDS}${secure}`;
64
+ }
65
+ function clearSessionCookie() {
66
+ const secure = process.env.APP_ENV === "production" ? "; Secure" : "";
67
+ return `${SESSION_COOKIE}=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0${secure}`;
68
+ }
69
+ export {
70
+ readSessionUserId,
71
+ createSessionCookie,
72
+ clearSessionCookie,
73
+ SESSION_TTL_SECONDS,
74
+ SESSION_COOKIE
75
+ };
@@ -0,0 +1,17 @@
1
+ // @bun
2
+ // ../../src/core/auth/tokenHash.ts
3
+ import { createHash, createHmac } from "crypto";
4
+ function resolveTokenPepper() {
5
+ return process.env.TOKEN_HASH_PEPPER?.trim() ?? "workhub-dev-token-pepper";
6
+ }
7
+ function hashApiToken(token) {
8
+ const pepper = resolveTokenPepper();
9
+ if (pepper && pepper !== "workhub-dev-token-pepper") {
10
+ return createHmac("sha256", pepper).update(token).digest("hex");
11
+ }
12
+ return createHash("sha256").update(token).digest("hex");
13
+ }
14
+ export {
15
+ resolveTokenPepper,
16
+ hashApiToken
17
+ };
@@ -0,0 +1,13 @@
1
+ // @bun
2
+ // ../../src/core/cache/tags.ts
3
+ var CACHE_TAGS = {
4
+ organizations: "organizations",
5
+ projects: "projects",
6
+ tasks: "tasks",
7
+ comments: "comments",
8
+ attachments: "attachments",
9
+ reports: "reports"
10
+ };
11
+ export {
12
+ CACHE_TAGS
13
+ };
@@ -0,0 +1,93 @@
1
+ // @bun
2
+ // ../../src/core/crypto/fieldEncryption.ts
3
+ import { createCipheriv, createDecipheriv, createHmac, randomBytes } from "crypto";
4
+ var ENCRYPTION_PREFIX = "enc:v1:";
5
+ var IV_LENGTH = 12;
6
+ var TAG_LENGTH = 16;
7
+ function resolveEncryptionKey() {
8
+ const raw = process.env.KMS_ENCRYPTION_KEY?.trim();
9
+ if (!raw) {
10
+ return null;
11
+ }
12
+ if (/^[0-9a-f]{64}$/i.test(raw)) {
13
+ return Buffer.from(raw, "hex");
14
+ }
15
+ const decoded = Buffer.from(raw, "base64");
16
+ if (decoded.length === 32) {
17
+ return decoded;
18
+ }
19
+ throw new Error("KMS_ENCRYPTION_KEY must be 32 bytes (hex or base64).");
20
+ }
21
+ function isFieldEncryptionEnabled() {
22
+ const featureFlag = process.env.FEATURE_FIELD_ENCRYPTION;
23
+ if (featureFlag === "false") {
24
+ return false;
25
+ }
26
+ if (featureFlag === "true") {
27
+ return true;
28
+ }
29
+ return (process.env.APP_ENV ?? "local") === "production";
30
+ }
31
+ function encryptField(plaintext, key) {
32
+ const iv = randomBytes(IV_LENGTH);
33
+ const cipher = createCipheriv("aes-256-gcm", key, iv);
34
+ const encrypted = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
35
+ const tag = cipher.getAuthTag();
36
+ const payload = Buffer.concat([iv, encrypted, tag]).toString("base64");
37
+ return `${ENCRYPTION_PREFIX}${payload}`;
38
+ }
39
+ function decryptField(value, key) {
40
+ if (!value.startsWith(ENCRYPTION_PREFIX)) {
41
+ return value;
42
+ }
43
+ const payload = Buffer.from(value.slice(ENCRYPTION_PREFIX.length), "base64");
44
+ const iv = payload.subarray(0, IV_LENGTH);
45
+ const tag = payload.subarray(payload.length - TAG_LENGTH);
46
+ const ciphertext = payload.subarray(IV_LENGTH, payload.length - TAG_LENGTH);
47
+ const decipher = createDecipheriv("aes-256-gcm", key, iv);
48
+ decipher.setAuthTag(tag);
49
+ return Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString("utf8");
50
+ }
51
+ function hashLookupValue(normalizedValue, key) {
52
+ return createHmac("sha256", key).update(normalizedValue).digest("hex");
53
+ }
54
+ function normalizeEmail(email) {
55
+ return email.trim().toLowerCase();
56
+ }
57
+ function protectEmail(email) {
58
+ const normalized = normalizeEmail(email);
59
+ const key = resolveEncryptionKey();
60
+ if (!key || !isFieldEncryptionEnabled()) {
61
+ return { storedEmail: normalized, emailLookup: normalized };
62
+ }
63
+ return {
64
+ storedEmail: encryptField(normalized, key),
65
+ emailLookup: hashLookupValue(normalized, key)
66
+ };
67
+ }
68
+ function revealEmail(storedEmail) {
69
+ const key = resolveEncryptionKey();
70
+ if (!key || !storedEmail.startsWith(ENCRYPTION_PREFIX)) {
71
+ return storedEmail;
72
+ }
73
+ return decryptField(storedEmail, key);
74
+ }
75
+ function emailLookupForQuery(email) {
76
+ const normalized = normalizeEmail(email);
77
+ const key = resolveEncryptionKey();
78
+ if (!key || !isFieldEncryptionEnabled()) {
79
+ return normalized;
80
+ }
81
+ return hashLookupValue(normalized, key);
82
+ }
83
+ export {
84
+ revealEmail,
85
+ resolveEncryptionKey,
86
+ protectEmail,
87
+ normalizeEmail,
88
+ isFieldEncryptionEnabled,
89
+ hashLookupValue,
90
+ encryptField,
91
+ emailLookupForQuery,
92
+ decryptField
93
+ };
@@ -0,0 +1,105 @@
1
+ // @bun
2
+ // ../../src/core/crypto/fieldEncryption.ts
3
+ import { createCipheriv, createDecipheriv, createHmac, randomBytes } from "crypto";
4
+ var ENCRYPTION_PREFIX = "enc:v1:";
5
+ var IV_LENGTH = 12;
6
+ var TAG_LENGTH = 16;
7
+ function resolveEncryptionKey() {
8
+ const raw = process.env.KMS_ENCRYPTION_KEY?.trim();
9
+ if (!raw) {
10
+ return null;
11
+ }
12
+ if (/^[0-9a-f]{64}$/i.test(raw)) {
13
+ return Buffer.from(raw, "hex");
14
+ }
15
+ const decoded = Buffer.from(raw, "base64");
16
+ if (decoded.length === 32) {
17
+ return decoded;
18
+ }
19
+ throw new Error("KMS_ENCRYPTION_KEY must be 32 bytes (hex or base64).");
20
+ }
21
+ function isFieldEncryptionEnabled() {
22
+ const featureFlag = process.env.FEATURE_FIELD_ENCRYPTION;
23
+ if (featureFlag === "false") {
24
+ return false;
25
+ }
26
+ if (featureFlag === "true") {
27
+ return true;
28
+ }
29
+ return (process.env.APP_ENV ?? "local") === "production";
30
+ }
31
+ function encryptField(plaintext, key) {
32
+ const iv = randomBytes(IV_LENGTH);
33
+ const cipher = createCipheriv("aes-256-gcm", key, iv);
34
+ const encrypted = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
35
+ const tag = cipher.getAuthTag();
36
+ const payload = Buffer.concat([iv, encrypted, tag]).toString("base64");
37
+ return `${ENCRYPTION_PREFIX}${payload}`;
38
+ }
39
+ function decryptField(value, key) {
40
+ if (!value.startsWith(ENCRYPTION_PREFIX)) {
41
+ return value;
42
+ }
43
+ const payload = Buffer.from(value.slice(ENCRYPTION_PREFIX.length), "base64");
44
+ const iv = payload.subarray(0, IV_LENGTH);
45
+ const tag = payload.subarray(payload.length - TAG_LENGTH);
46
+ const ciphertext = payload.subarray(IV_LENGTH, payload.length - TAG_LENGTH);
47
+ const decipher = createDecipheriv("aes-256-gcm", key, iv);
48
+ decipher.setAuthTag(tag);
49
+ return Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString("utf8");
50
+ }
51
+ function hashLookupValue(normalizedValue, key) {
52
+ return createHmac("sha256", key).update(normalizedValue).digest("hex");
53
+ }
54
+ function normalizeEmail(email) {
55
+ return email.trim().toLowerCase();
56
+ }
57
+ function protectEmail(email) {
58
+ const normalized = normalizeEmail(email);
59
+ const key = resolveEncryptionKey();
60
+ if (!key || !isFieldEncryptionEnabled()) {
61
+ return { storedEmail: normalized, emailLookup: normalized };
62
+ }
63
+ return {
64
+ storedEmail: encryptField(normalized, key),
65
+ emailLookup: hashLookupValue(normalized, key)
66
+ };
67
+ }
68
+ function revealEmail(storedEmail) {
69
+ const key = resolveEncryptionKey();
70
+ if (!key || !storedEmail.startsWith(ENCRYPTION_PREFIX)) {
71
+ return storedEmail;
72
+ }
73
+ return decryptField(storedEmail, key);
74
+ }
75
+ function emailLookupForQuery(email) {
76
+ const normalized = normalizeEmail(email);
77
+ const key = resolveEncryptionKey();
78
+ if (!key || !isFieldEncryptionEnabled()) {
79
+ return normalized;
80
+ }
81
+ return hashLookupValue(normalized, key);
82
+ }
83
+
84
+ // ../../src/core/crypto/mfaSecret.ts
85
+ function protectMfaSecret(secret) {
86
+ const key = resolveEncryptionKey();
87
+ if (!isFieldEncryptionEnabled() || !key) {
88
+ return secret;
89
+ }
90
+ return encryptField(secret, key);
91
+ }
92
+ function revealMfaSecret(stored) {
93
+ if (!stored) {
94
+ return null;
95
+ }
96
+ const key = resolveEncryptionKey();
97
+ if (!isFieldEncryptionEnabled() || !key || !stored.startsWith("enc:v1:")) {
98
+ return stored;
99
+ }
100
+ return decryptField(stored, key);
101
+ }
102
+ export {
103
+ revealMfaSecret,
104
+ protectMfaSecret
105
+ };
@@ -0,0 +1,17 @@
1
+ // @bun
2
+ // ../../src/core/database/factory.ts
3
+ class Factory {
4
+ constructor() {}
5
+ definition() {
6
+ throw new Error("Factory definition must be implemented by subclass.");
7
+ }
8
+ make(overrides = {}) {
9
+ return {
10
+ ...this.definition(),
11
+ ...overrides
12
+ };
13
+ }
14
+ }
15
+ export {
16
+ Factory
17
+ };
@@ -0,0 +1,30 @@
1
+ // @bun
2
+ // ../../src/core/database/seeders/runner.ts
3
+ import { readdir } from "fs/promises";
4
+ import { join } from "path";
5
+ import { pathToFileURL } from "url";
6
+ async function loadSeedersFromDirectory(directory) {
7
+ const entries = await readdir(directory);
8
+ const seederFiles = entries.filter((entry) => (entry.endsWith(".ts") || entry.endsWith(".js")) && entry !== "types.ts" && entry !== "index.ts" && entry !== "runner.ts").sort();
9
+ const loadedSeeders = await Promise.all(seederFiles.map(async (fileName) => {
10
+ const moduleUrl = pathToFileURL(join(directory, fileName)).href;
11
+ const module = await import(moduleUrl);
12
+ return module.default;
13
+ }));
14
+ return loadedSeeders.filter((seeder) => seeder?.name !== undefined);
15
+ }
16
+ async function runSeedersFromDirectory(directory, db, options) {
17
+ const seeders = await loadSeedersFromDirectory(directory);
18
+ if (seeders.length === 0) {
19
+ return 0;
20
+ }
21
+ for (const seeder of seeders) {
22
+ options?.onSeeder?.(seeder.name);
23
+ await seeder.run(db);
24
+ }
25
+ return seeders.length;
26
+ }
27
+ export {
28
+ runSeedersFromDirectory,
29
+ loadSeedersFromDirectory
30
+ };
@@ -0,0 +1 @@
1
+ // @bun