@getstrata/core 0.4.0 → 0.5.0

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 +2870 -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 +2218 -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 +3143 -0
  75. package/dist/entries/http/webFormRequest.js +293 -0
  76. package/dist/entries/http.js +3924 -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 +2848 -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 +2476 -0
  86. package/dist/entries/queue/queueMetrics.js +2898 -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 +3072 -0
  101. package/dist/framework/public-api.d.ts +65 -38
  102. package/dist/index.js +3198 -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,293 @@
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/http/contentNegotiation.ts
69
+ function requestPrefersJson(request) {
70
+ if (!request) {
71
+ return true;
72
+ }
73
+ if (request.headers.get("HX-Request") === "true") {
74
+ return false;
75
+ }
76
+ const accept = request.headers.get("accept")?.toLowerCase() ?? "";
77
+ if (accept.includes("text/html")) {
78
+ return false;
79
+ }
80
+ if (accept.includes("application/json")) {
81
+ return true;
82
+ }
83
+ const contentType = request.headers.get("content-type")?.toLowerCase() ?? "";
84
+ if (contentType.includes("application/x-www-form-urlencoded") || contentType.includes("multipart/form-data")) {
85
+ return false;
86
+ }
87
+ const pathname = new URL(request.url).pathname;
88
+ return pathname.startsWith("/api/");
89
+ }
90
+
91
+ // ../../src/core/http/parseFormBody.ts
92
+ async function parseFormBody(request) {
93
+ const contentType = request.headers.get("content-type")?.toLowerCase() ?? "";
94
+ if (!contentType.includes("application/x-www-form-urlencoded") && !contentType.includes("multipart/form-data")) {
95
+ throw new BadRequestError("Expected a form submission.");
96
+ }
97
+ return formDataToRecord(await request.formData());
98
+ }
99
+ function formDataToRecord(formData) {
100
+ const values = {};
101
+ for (const [key, value] of formData.entries()) {
102
+ if (typeof value === "string") {
103
+ values[key] = value;
104
+ }
105
+ }
106
+ return values;
107
+ }
108
+
109
+ // ../../src/core/auth/authContext.ts
110
+ import { AsyncLocalStorage } from "async_hooks";
111
+ var authContext = new AsyncLocalStorage;
112
+ function runWithAuthUser(user, callback) {
113
+ return authContext.run(user, callback);
114
+ }
115
+ function currentAuthUser() {
116
+ return authContext.getStore() ?? null;
117
+ }
118
+
119
+ // ../../src/core/tenant/tenantContext.ts
120
+ import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
121
+ var tenantContext = new AsyncLocalStorage2;
122
+ function runWithTenant(tenant, callback) {
123
+ return tenantContext.run(tenant, callback);
124
+ }
125
+ function currentTenant() {
126
+ return tenantContext.getStore() ?? null;
127
+ }
128
+ function currentTenantId() {
129
+ return currentTenant()?.id ?? 1;
130
+ }
131
+ function rateLimitMultiplierForPlan(plan) {
132
+ switch (plan) {
133
+ case "enterprise":
134
+ return 4;
135
+ case "pro":
136
+ return 2;
137
+ default:
138
+ return 1;
139
+ }
140
+ }
141
+
142
+ // ../../src/core/http/validation.ts
143
+ function buildRequestCacheKey(fallbackPath, request) {
144
+ if (!request) {
145
+ return fallbackPath;
146
+ }
147
+ const url = new URL(request.url);
148
+ const user = currentAuthUser();
149
+ const authScope = user ? `u:${user.id}` : "guest";
150
+ const tenantScope = `t:${currentTenantId()}`;
151
+ return `${authScope}|${tenantScope}|${url.pathname}${url.search}`;
152
+ }
153
+ function getQueryParams(request) {
154
+ if (!request) {
155
+ return new URLSearchParams;
156
+ }
157
+ return new URL(request.url).searchParams;
158
+ }
159
+ function parseOptionalPositiveIntQueryParam(params, name) {
160
+ const value = params.get(name);
161
+ if (value === null || value.trim() === "") {
162
+ return;
163
+ }
164
+ const parsed = Number.parseInt(value, 10);
165
+ if (!Number.isInteger(parsed) || parsed <= 0) {
166
+ throw new BadRequestError(`Invalid query parameter "${name}". Expected a positive integer.`);
167
+ }
168
+ return parsed;
169
+ }
170
+ function parseOptionalBooleanQueryParam(params, name) {
171
+ const value = params.get(name);
172
+ if (value === null || value.trim() === "") {
173
+ return;
174
+ }
175
+ switch (value.toLowerCase()) {
176
+ case "true":
177
+ case "1":
178
+ return true;
179
+ case "false":
180
+ case "0":
181
+ return false;
182
+ default:
183
+ throw new BadRequestError(`Invalid query parameter "${name}". Expected a boolean.`);
184
+ }
185
+ }
186
+ function parseOptionalEnumQueryParam(params, name, allowedValues) {
187
+ const value = params.get(name);
188
+ if (value === null || value.trim() === "") {
189
+ return;
190
+ }
191
+ if (!allowedValues.includes(value)) {
192
+ throw new BadRequestError(`Invalid query parameter "${name}". Expected one of: ${allowedValues.join(", ")}.`);
193
+ }
194
+ return value;
195
+ }
196
+ function expectObject(value, label = "request body") {
197
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
198
+ throw new BadRequestError(`${label} must be a JSON object.`);
199
+ }
200
+ return value;
201
+ }
202
+ async function parseJsonBody(request, validator) {
203
+ let payload;
204
+ try {
205
+ payload = await request.json();
206
+ } catch {
207
+ throw new BadRequestError("Request body must be valid JSON.");
208
+ }
209
+ return validator(payload);
210
+ }
211
+ function readRequiredString(payload, field, options = {}) {
212
+ const value = payload[field];
213
+ if (typeof value !== "string" || value.trim() === "") {
214
+ throw new BadRequestError(`"${field}" is required and must be a string.`);
215
+ }
216
+ const trimmed = value.trim();
217
+ if (options.minLength !== undefined && trimmed.length < options.minLength) {
218
+ throw new BadRequestError(`"${field}" must be at least ${options.minLength} characters.`);
219
+ }
220
+ if (options.maxLength !== undefined && trimmed.length > options.maxLength) {
221
+ throw new BadRequestError(`"${field}" must be at most ${options.maxLength} characters.`);
222
+ }
223
+ if (options.pattern && !options.pattern.test(trimmed)) {
224
+ throw new BadRequestError(`"${field}" has an invalid format.`);
225
+ }
226
+ return trimmed;
227
+ }
228
+ function readOptionalString(payload, field, options = {}) {
229
+ if (!(field in payload) || payload[field] === undefined) {
230
+ return;
231
+ }
232
+ return readRequiredString(payload, field, options);
233
+ }
234
+ function readRequiredEnum(payload, field, allowedValues) {
235
+ const value = readRequiredString(payload, field);
236
+ if (!allowedValues.includes(value)) {
237
+ throw new BadRequestError(`"${field}" must be one of: ${allowedValues.join(", ")}.`);
238
+ }
239
+ return value;
240
+ }
241
+ function readOptionalEnum(payload, field, allowedValues) {
242
+ if (!(field in payload) || payload[field] === undefined) {
243
+ return;
244
+ }
245
+ return readRequiredEnum(payload, field, allowedValues);
246
+ }
247
+ function readRequiredPositiveInt(payload, field) {
248
+ const value = payload[field];
249
+ if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
250
+ throw new BadRequestError(`"${field}" is required and must be a positive integer.`);
251
+ }
252
+ return value;
253
+ }
254
+ function readOptionalPositiveInt(payload, field) {
255
+ if (!(field in payload) || payload[field] === undefined) {
256
+ return;
257
+ }
258
+ return readRequiredPositiveInt(payload, field);
259
+ }
260
+ function parsePositiveIntParam(value, name = "id") {
261
+ const parsed = Number.parseInt(value, 10);
262
+ if (!Number.isInteger(parsed) || parsed <= 0) {
263
+ throw new BadRequestError(`Invalid ${name}. Expected a positive integer.`);
264
+ }
265
+ return parsed;
266
+ }
267
+
268
+ // ../../src/core/http/webFormRequest.ts
269
+ class WebFormRequest {
270
+ authorize(_request) {
271
+ return true;
272
+ }
273
+ validatePayload(payload) {
274
+ return this.parse(payload);
275
+ }
276
+ async validate(request) {
277
+ if (!await this.authorize(request)) {
278
+ throw new ForbiddenError;
279
+ }
280
+ const payload = requestPrefersJson(request) ? await parseJsonBody(request, (body) => body) : await parseFormBody(request);
281
+ try {
282
+ return this.validatePayload(payload);
283
+ } catch (error) {
284
+ if (error instanceof ValidationError) {
285
+ throw error;
286
+ }
287
+ throw error;
288
+ }
289
+ }
290
+ }
291
+ export {
292
+ WebFormRequest
293
+ };