@zerotal/core 1.0.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 (201) hide show
  1. package/CHANGELOG.md +79 -0
  2. package/LICENSE +21 -0
  3. package/README.md +128 -0
  4. package/package.json +72 -0
  5. package/src/application/Application.ts +1671 -0
  6. package/src/application/BootDoctor.ts +108 -0
  7. package/src/application/DevErrorPage.ts +567 -0
  8. package/src/application/ExceptionHandler.ts +183 -0
  9. package/src/application/currentApp.ts +73 -0
  10. package/src/assets/assets.ts +79 -0
  11. package/src/assets/index.ts +16 -0
  12. package/src/auth/AuthenticatedUser.ts +18 -0
  13. package/src/build/PackageLinter.ts +146 -0
  14. package/src/build/PackageScaffold.ts +127 -0
  15. package/src/build/codemod.ts +64 -0
  16. package/src/build/index.ts +12 -0
  17. package/src/command/Command.ts +254 -0
  18. package/src/command/CommandRunner.ts +593 -0
  19. package/src/command/OutputWriter.ts +61 -0
  20. package/src/command/builtin/CompileCommand.ts +46 -0
  21. package/src/command/builtin/CssBuildCommand.ts +71 -0
  22. package/src/command/builtin/KeyGenerateCommand.ts +58 -0
  23. package/src/command/builtin/LintPackagesCommand.ts +72 -0
  24. package/src/command/builtin/MakeCommandCommand.ts +85 -0
  25. package/src/command/builtin/MakeControllerCommand.ts +95 -0
  26. package/src/command/builtin/MakeEventCommand.ts +85 -0
  27. package/src/command/builtin/MakeJobCommand.ts +53 -0
  28. package/src/command/builtin/MakeListenerCommand.ts +35 -0
  29. package/src/command/builtin/MakeMiddlewareCommand.ts +63 -0
  30. package/src/command/builtin/MakeNotificationCommand.ts +48 -0
  31. package/src/command/builtin/MakeObserverCommand.ts +78 -0
  32. package/src/command/builtin/MakePackageCommand.ts +45 -0
  33. package/src/command/builtin/MakePolicyCommand.ts +66 -0
  34. package/src/command/builtin/MakeProviderCommand.ts +75 -0
  35. package/src/command/builtin/MakeRequestCommand.ts +47 -0
  36. package/src/command/builtin/MakeResourceCommand.ts +61 -0
  37. package/src/command/builtin/MakeTestCommand.ts +120 -0
  38. package/src/command/builtin/ReloadCommand.ts +52 -0
  39. package/src/command/builtin/ReplCommand.ts +174 -0
  40. package/src/command/builtin/RouteListCommand.ts +188 -0
  41. package/src/command/builtin/ServeCommand.ts +321 -0
  42. package/src/command/builtin/StartCommand.ts +3 -0
  43. package/src/command/builtin/StatusCommand.ts +71 -0
  44. package/src/command/builtin/TestCommand.ts +172 -0
  45. package/src/command/builtin/WorkerCommand.ts +27 -0
  46. package/src/command/builtin/index.ts +53 -0
  47. package/src/command/scaffold/worker.ts.txt +12 -0
  48. package/src/command/scaffold/zerotal.ts.txt +26 -0
  49. package/src/command/startZerotal.ts +55 -0
  50. package/src/config/AppConfig.ts +253 -0
  51. package/src/config/ConfigLoader.ts +117 -0
  52. package/src/config/ConfigManager.ts +169 -0
  53. package/src/config/index.ts +46 -0
  54. package/src/config/registry.ts +59 -0
  55. package/src/config/validation.ts +117 -0
  56. package/src/container/Container.ts +606 -0
  57. package/src/container/ContextualBindingBuilder.ts +57 -0
  58. package/src/container/ScopedResolver.ts +117 -0
  59. package/src/container/index.ts +32 -0
  60. package/src/container/inject.ts +55 -0
  61. package/src/container/types.ts +71 -0
  62. package/src/context/RequestContext.ts +91 -0
  63. package/src/contracts/auth.ts +24 -0
  64. package/src/contracts/index.ts +23 -0
  65. package/src/contracts/session.ts +70 -0
  66. package/src/contracts/transaction.ts +26 -0
  67. package/src/conventions/ConventionLoader.ts +128 -0
  68. package/src/conventions/builtinConcerns.ts +131 -0
  69. package/src/crypt/Crypt.ts +141 -0
  70. package/src/crypt/URLSigner.ts +96 -0
  71. package/src/datetime/Carbon.ts +1396 -0
  72. package/src/datetime/CarbonInterval.ts +421 -0
  73. package/src/datetime/clock.ts +28 -0
  74. package/src/datetime/index.ts +23 -0
  75. package/src/datetime/temporal-shim.ts +1 -0
  76. package/src/dev/BuildOutput.ts +131 -0
  77. package/src/dev/CssPlugins.ts +184 -0
  78. package/src/dev/DevBuildHook.ts +74 -0
  79. package/src/dev/DevOrchestrator.ts +213 -0
  80. package/src/dev/DevReloadMiddleware.ts +101 -0
  81. package/src/dev/DevReloadServer.ts +85 -0
  82. package/src/dev/DevWsServer.ts +45 -0
  83. package/src/dev/index.ts +19 -0
  84. package/src/dev/reloadClient.ts +39 -0
  85. package/src/env/Def.ts +232 -0
  86. package/src/env/EnvSchema.ts +105 -0
  87. package/src/env/index.ts +34 -0
  88. package/src/env/t.ts +128 -0
  89. package/src/errors/ConfigError.ts +12 -0
  90. package/src/errors/ContainerErrors.ts +143 -0
  91. package/src/errors/HttpError.ts +127 -0
  92. package/src/errors/ValidationError.ts +19 -0
  93. package/src/errors/ZerotalError.ts +25 -0
  94. package/src/errors/index.ts +46 -0
  95. package/src/events/CallQueuedListener.ts +66 -0
  96. package/src/events/Emitter.ts +280 -0
  97. package/src/events/EventFake.ts +160 -0
  98. package/src/events/FrameworkEvents.ts +252 -0
  99. package/src/facade/Facade.ts +101 -0
  100. package/src/facade/facades/App.ts +155 -0
  101. package/src/facade/facades/Artisan.ts +63 -0
  102. package/src/facade/facades/Config.ts +21 -0
  103. package/src/facade/facades/Events.ts +19 -0
  104. package/src/facade/facades/index.ts +28 -0
  105. package/src/global.d.ts +9 -0
  106. package/src/hash/Hash.ts +60 -0
  107. package/src/health/Health.ts +221 -0
  108. package/src/health/index.ts +27 -0
  109. package/src/helpers/Collection.ts +435 -0
  110. package/src/helpers/config.ts +59 -0
  111. package/src/helpers/fluent.ts +52 -0
  112. package/src/helpers/html.ts +11 -0
  113. package/src/helpers/index.ts +266 -0
  114. package/src/helpers/make.ts +35 -0
  115. package/src/helpers/markdown.ts +73 -0
  116. package/src/helpers/pageElements.ts +27 -0
  117. package/src/helpers/request.ts +62 -0
  118. package/src/helpers/response.ts +411 -0
  119. package/src/helpers/str.ts +208 -0
  120. package/src/http/Http.ts +298 -0
  121. package/src/http/HttpClient.ts +289 -0
  122. package/src/http/Resource.ts +171 -0
  123. package/src/http/UploadedFile.ts +204 -0
  124. package/src/http/Uri.ts +490 -0
  125. package/src/http/index.ts +46 -0
  126. package/src/http/negotiate.ts +213 -0
  127. package/src/http/originGuard.ts +76 -0
  128. package/src/http/sniffContentType.ts +105 -0
  129. package/src/http/url.ts +204 -0
  130. package/src/http/withHeaders.ts +24 -0
  131. package/src/index.ts +250 -0
  132. package/src/lock/LockManager.ts +228 -0
  133. package/src/lock/config.ts +49 -0
  134. package/src/lock/drivers/LockDriver.ts +32 -0
  135. package/src/lock/drivers/MemoryLockDriver.ts +52 -0
  136. package/src/lock/drivers/RedisLockDriver.ts +58 -0
  137. package/src/lock/drivers/SqliteLockDriver.ts +85 -0
  138. package/src/lock/errors.ts +20 -0
  139. package/src/lock/facades/Lock.ts +114 -0
  140. package/src/lock/index.ts +53 -0
  141. package/src/logger/Log.ts +35 -0
  142. package/src/logger/LogManager.ts +430 -0
  143. package/src/logger/LoggerMiddleware.ts +125 -0
  144. package/src/logger/channels/ConsoleChannel.ts +139 -0
  145. package/src/logger/channels/DailyChannel.ts +74 -0
  146. package/src/logger/channels/NullChannel.ts +17 -0
  147. package/src/logger/channels/SingleChannel.ts +34 -0
  148. package/src/logger/channels/StackChannel.ts +29 -0
  149. package/src/logger/config.ts +90 -0
  150. package/src/logger/format.ts +96 -0
  151. package/src/logger/frameworkLog.ts +93 -0
  152. package/src/logger/index.ts +68 -0
  153. package/src/logger/renderTable.ts +111 -0
  154. package/src/logger/types.ts +212 -0
  155. package/src/macros/config.macro.ts +50 -0
  156. package/src/metrics/HttpMetrics.ts +114 -0
  157. package/src/metrics/index.ts +18 -0
  158. package/src/middleware/BaseMiddleware.ts +72 -0
  159. package/src/middleware/CorsMiddleware.ts +152 -0
  160. package/src/middleware/RateLimiter.ts +255 -0
  161. package/src/middleware/SecureHeadersMiddleware.ts +127 -0
  162. package/src/middleware/ThrottleMiddleware.ts +252 -0
  163. package/src/middleware/WebhookMiddleware.ts +204 -0
  164. package/src/pipeline/ContextRegistry.ts +42 -0
  165. package/src/pipeline/HttpContext.ts +865 -0
  166. package/src/pipeline/Pipeline.ts +150 -0
  167. package/src/pipeline/currentPage.ts +46 -0
  168. package/src/pipeline/types.ts +80 -0
  169. package/src/provider/LockProvider.ts +64 -0
  170. package/src/provider/LogProvider.ts +137 -0
  171. package/src/provider/ServiceProvider.ts +84 -0
  172. package/src/provider/StorageProvider.ts +45 -0
  173. package/src/router/FileRouter.ts +526 -0
  174. package/src/router/Route.ts +76 -0
  175. package/src/router/RouteHandler.ts +335 -0
  176. package/src/router/Router.ts +1247 -0
  177. package/src/router/domain.ts +65 -0
  178. package/src/security/index.ts +22 -0
  179. package/src/storage/FakeDisk.ts +233 -0
  180. package/src/storage/StorageFilesMiddleware.ts +150 -0
  181. package/src/storage/StorageManager.ts +173 -0
  182. package/src/storage/config.ts +47 -0
  183. package/src/storage/drivers/LocalDriver.ts +138 -0
  184. package/src/storage/drivers/S3Driver.ts +169 -0
  185. package/src/storage/errors.ts +135 -0
  186. package/src/storage/facades/Storage.ts +3 -0
  187. package/src/storage/global.d.ts +7 -0
  188. package/src/storage/index.ts +22 -0
  189. package/src/storage/root.ts +59 -0
  190. package/src/storage/types.ts +104 -0
  191. package/src/support/appKey.ts +38 -0
  192. package/src/support/cookie.ts +72 -0
  193. package/src/support/crypto.ts +52 -0
  194. package/src/support/deepMerge.ts +117 -0
  195. package/src/support/env.ts +71 -0
  196. package/src/support/network.ts +79 -0
  197. package/src/support/port.ts +197 -0
  198. package/src/support/str.ts +122 -0
  199. package/src/view/FileRouteResolver.ts +59 -0
  200. package/src/view/index.ts +144 -0
  201. package/src/view/jsx-runtime.ts +233 -0
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Errors raised by the IoC container — unknown bindings, scope-lifetime misuse,
3
+ * synchronous resolution of async bindings, circular dependencies, and facades
4
+ * touched before the container is ready.
5
+ */
6
+ import { ZerotalError } from "./ZerotalError.ts";
7
+
8
+ /** Raised when resolving a token that no provider has registered. */
9
+ export class BindingNotFoundError extends ZerotalError {
10
+ constructor(token: string) {
11
+ super(
12
+ `No binding registered for token: "${token}". Register it in a ServiceProvider's onRegister() method.`,
13
+ "E_BINDING_NOT_FOUND",
14
+ 500,
15
+ { token },
16
+ );
17
+ }
18
+ }
19
+
20
+ /** Raised when a request-scoped binding is resolved with no request in scope. */
21
+ export class ScopedOutsideRequestError extends ZerotalError {
22
+ constructor(message: string) {
23
+ super(message, "E_SCOPED_OUTSIDE_REQUEST", 500);
24
+ }
25
+ }
26
+
27
+ /** Raised when a request-scoped binding is resolved after the scope was flushed. */
28
+ export class ScopedAfterFlushError extends ZerotalError {
29
+ constructor(message: string) {
30
+ super(message, "E_SCOPED_AFTER_FLUSH", 500);
31
+ }
32
+ }
33
+
34
+ /** Raised when an async-only binding is resolved through the synchronous API. */
35
+ export class SyncResolutionError extends ZerotalError {
36
+ constructor(message: string) {
37
+ super(message, "E_SYNC_RESOLUTION", 500);
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Raised when application code tries to register a binding through the `App`
43
+ * facade after `Application.boot()` has completed.
44
+ *
45
+ * The container is process-global and shared across every concurrent request,
46
+ * so mutating it at request time would leak state between requests. Register
47
+ * bindings at boot — in the bootstrap `App.bind()` callback, a `ServiceProvider`,
48
+ * the `app/services` convention, or top-of-module code — and use a `scoped`
49
+ * binding for anything that must be per-request.
50
+ */
51
+ export class ContainerLockedError extends ZerotalError {
52
+ constructor(method: string) {
53
+ super(
54
+ [
55
+ `App.${method}() was called after the application finished booting.`,
56
+ ``,
57
+ `The container is locked once boot() completes: it is shared across every`,
58
+ `concurrent request, so registering a binding now would leak state between`,
59
+ `requests. Register bindings during boot instead:`,
60
+ ``,
61
+ ` • the bootstrap App.bind((c) => { ... }) callback`,
62
+ ` • a ServiceProvider's onRegister()`,
63
+ ` • the app/services convention (static lifetime = "singleton")`,
64
+ ``,
65
+ `Need per-request state? Register a scoped binding at boot — it resolves a`,
66
+ `fresh instance for each request without mutating the global container.`,
67
+ ].join("\n"),
68
+ "E_CONTAINER_LOCKED",
69
+ 500,
70
+ { method },
71
+ );
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Raised when bindings depend on each other in a cycle.
77
+ *
78
+ * @param chain - The resolution path that closed the loop, in order.
79
+ */
80
+ export class CircularDependencyError extends ZerotalError {
81
+ constructor(chain: string[]) {
82
+ super(`Circular dependency detected: ${chain.join(" → ")}`, "E_CIRCULAR_DEPENDENCY", 500, {
83
+ chain,
84
+ });
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Raised when a facade is used before `Application.boot()` has wired the
90
+ * container — typically from module-level code that runs on import.
91
+ */
92
+ export class FacadeAccessedBeforeBootError extends ZerotalError {
93
+ constructor(facadeKey: string) {
94
+ super(
95
+ [
96
+ `You attempted to use the "${facadeKey}" facade before the container was ready.`,
97
+ ``,
98
+ `Facades are resolved from the IoC container, which is only available after`,
99
+ `Application.boot() completes. Using a facade at module scope (top-level code`,
100
+ `that runs on import) is not safe because the app may not have booted yet.`,
101
+ ``,
102
+ `Common causes:`,
103
+ ` • A top-level variable initialised with a Facade call:`,
104
+ ` const name = Config.get('app.name') // ← runs before boot`,
105
+ ` • Module-level code outside of any function or class`,
106
+ ``,
107
+ `Fix: move Facade usage inside a function, controller method, or service`,
108
+ `provider hook (onRegister / onBooting / onBooted) where the container is`,
109
+ `guaranteed to be ready.`,
110
+ ].join("\n"),
111
+ "E_FACADE_BEFORE_BOOT",
112
+ 500,
113
+ { facade: facadeKey },
114
+ );
115
+ }
116
+ }
117
+
118
+ /**
119
+ * Thrown when a facade is used after the app has booted but nothing provides its
120
+ * binding — almost always a missing ServiceProvider in `bootstrap/providers.ts`.
121
+ */
122
+ export class FacadeBindingMissingError extends ZerotalError {
123
+ constructor(facadeKey: string) {
124
+ super(
125
+ [
126
+ `Could not resolve the "${facadeKey}" facade.`,
127
+ ``,
128
+ `The application has booted, but nothing provides the "${facadeKey}" binding —`,
129
+ `usually because the ServiceProvider that registers it isn't in your providers`,
130
+ `list (and occasionally because the binding hasn't been pre-resolved yet).`,
131
+ ``,
132
+ `Fix: register the provider in bootstrap/providers.ts. For example, the "log"`,
133
+ `facade is provided by LogProvider:`,
134
+ ``,
135
+ ` import { LogProvider } from "@zerotal/core/logger";`,
136
+ ` export default [/* …your providers… */, LogProvider];`,
137
+ ].join("\n"),
138
+ "E_FACADE_BINDING_MISSING",
139
+ 500,
140
+ { facade: facadeKey },
141
+ );
142
+ }
143
+ }
@@ -0,0 +1,127 @@
1
+ /**
2
+ * The HTTP error hierarchy. `HttpError` carries a status and optional response
3
+ * headers; the named subclasses below are convenience constructors for the
4
+ * common status codes, each with a default message and a stable error code.
5
+ */
6
+ import { ZerotalError } from "./ZerotalError.ts";
7
+
8
+ /**
9
+ * An error that maps directly onto an HTTP response.
10
+ *
11
+ * @param code - Defaults to `E_HTTP_<status>` when omitted.
12
+ * @param headers - Response headers to merge in (e.g. `Allow`, `Retry-After`).
13
+ */
14
+ export class HttpError extends ZerotalError {
15
+ headers?: Record<string, string>;
16
+
17
+ constructor(message: string, status: number, code?: string, headers?: Record<string, string>) {
18
+ super(message, code ?? `E_HTTP_${status}`, status);
19
+ if (headers) this.headers = headers;
20
+ }
21
+ }
22
+
23
+ /** 400 Bad Request — the request was malformed or failed basic validation. */
24
+ export class BadRequestError extends HttpError {
25
+ constructor(message = "Bad Request") {
26
+ super(message, 400, "E_BAD_REQUEST");
27
+ }
28
+ }
29
+ /** 401 Unauthorized — authentication is required and has failed or is missing. */
30
+ export class UnauthorizedError extends HttpError {
31
+ constructor(message = "Unauthorized") {
32
+ super(message, 401, "E_UNAUTHORIZED");
33
+ }
34
+ }
35
+ /** 403 Forbidden — authenticated, but not allowed to perform this action. */
36
+ export class ForbiddenError extends HttpError {
37
+ constructor(message = "Forbidden") {
38
+ super(message, 403, "E_FORBIDDEN");
39
+ }
40
+ }
41
+ /** 404 Not Found — no resource matches the request. */
42
+ export class NotFoundError extends HttpError {
43
+ constructor(message = "Not Found") {
44
+ super(message, 404, "E_NOT_FOUND");
45
+ }
46
+ }
47
+ /**
48
+ * 405 Method Not Allowed — the route exists but not for this HTTP method.
49
+ *
50
+ * @param allowed - Methods the route does accept; populates the `Allow` header.
51
+ */
52
+ export class MethodNotAllowedError extends HttpError {
53
+ constructor(allowed: string[] = [], message = "Method Not Allowed") {
54
+ super(
55
+ message,
56
+ 405,
57
+ "E_METHOD_NOT_ALLOWED",
58
+ allowed.length ? { Allow: allowed.join(", ") } : undefined,
59
+ );
60
+ }
61
+ }
62
+ /** 409 Conflict — the request conflicts with the current state of the resource. */
63
+ export class ConflictError extends HttpError {
64
+ constructor(message = "Conflict") {
65
+ super(message, 409, "E_CONFLICT");
66
+ }
67
+ }
68
+ /** 410 Gone — the resource existed once but has been permanently removed. */
69
+ export class GoneError extends HttpError {
70
+ constructor(message = "Gone") {
71
+ super(message, 410, "E_GONE");
72
+ }
73
+ }
74
+ /** 422 Unprocessable Entity — well-formed but semantically invalid request. */
75
+ export class UnprocessableEntityError extends HttpError {
76
+ constructor(message = "Unprocessable Entity") {
77
+ super(message, 422, "E_UNPROCESSABLE_ENTITY");
78
+ }
79
+ }
80
+ /**
81
+ * 429 Too Many Requests — the client has exceeded a rate limit.
82
+ *
83
+ * @param retryAfter - Seconds to wait before retrying; populates `Retry-After`.
84
+ */
85
+ export class TooManyRequestsError extends HttpError {
86
+ readonly retryAfter?: number | undefined;
87
+ constructor(retryAfter?: number, message = "Too Many Requests") {
88
+ super(
89
+ message,
90
+ 429,
91
+ "E_TOO_MANY_REQUESTS",
92
+ retryAfter !== undefined ? { "Retry-After": String(retryAfter) } : undefined,
93
+ );
94
+ this.retryAfter = retryAfter;
95
+ }
96
+ }
97
+ /**
98
+ * 503 Service Unavailable — the server is temporarily unable to handle the request.
99
+ *
100
+ * @param reason - Appended to the message for context (e.g. "database down").
101
+ * @param retryAfter - Seconds to wait before retrying; populates `Retry-After`.
102
+ */
103
+ export class ServiceUnavailableError extends HttpError {
104
+ readonly retryAfter?: number | undefined;
105
+ constructor(reason?: string, retryAfter?: number) {
106
+ super(
107
+ reason ? `Service Unavailable: ${reason}` : "Service Unavailable",
108
+ 503,
109
+ "E_SERVICE_UNAVAILABLE",
110
+ retryAfter !== undefined ? { "Retry-After": String(retryAfter) } : undefined,
111
+ );
112
+ this.retryAfter = retryAfter;
113
+ }
114
+ }
115
+ /**
116
+ * 500 — `RequestContext` was accessed with no active HTTP request in scope
117
+ * (e.g. from module-level code or a background task).
118
+ */
119
+ export class ContextOutsideRequestError extends HttpError {
120
+ constructor() {
121
+ super(
122
+ "Attempted to access RequestContext outside of an active HTTP request lifecycle.",
123
+ 500,
124
+ "E_CONTEXT_OUTSIDE_REQUEST",
125
+ );
126
+ }
127
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * The error raised when request input fails validation. Carries the per-field
3
+ * messages so the handler can render them back to the client (422).
4
+ */
5
+ import { HttpError } from "./HttpError.ts";
6
+
7
+ /**
8
+ * Raised when input validation fails.
9
+ *
10
+ * @param errors - Field name → list of messages for that field.
11
+ */
12
+ export class ValidationError extends HttpError {
13
+ constructor(
14
+ message: string,
15
+ public readonly errors: Record<string, string[]>,
16
+ ) {
17
+ super(message, 422, "E_VALIDATION_FAILED");
18
+ }
19
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * The base error type for the framework. Every error Zerotal throws extends this,
3
+ * carrying a machine-readable `code`, an HTTP `status`, and optional structured
4
+ * `context` so handlers can render and log it consistently.
5
+ */
6
+
7
+ /**
8
+ * Base class for all framework errors.
9
+ *
10
+ * @param code - Stable machine-readable identifier (e.g. `E_BINDING_NOT_FOUND`),
11
+ * safe to switch on; unlike the message, it is not meant to change.
12
+ * @param status - HTTP status to respond with when this error reaches the handler.
13
+ * @param context - Structured detail attached to logs and error pages.
14
+ */
15
+ export class ZerotalError extends Error {
16
+ constructor(
17
+ message: string,
18
+ public readonly code: string,
19
+ public readonly status: number = 500,
20
+ public readonly context?: Record<string, unknown>,
21
+ ) {
22
+ super(message);
23
+ this.name = this.constructor.name;
24
+ }
25
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Public barrel for the framework's error types (the `@zerotal/core/errors`
3
+ * subpath) — the base {@link ZerotalError}, the {@link HttpError} hierarchy of
4
+ * status-coded convenience classes, and the validation/config/container errors.
5
+ * Every error carries a stable machine-readable `code` and an HTTP `status`.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * import { NotFoundError, ValidationError } from "@zerotal/core/errors";
10
+ *
11
+ * throw new NotFoundError("User not found");
12
+ * throw new ValidationError("Invalid input", { email: ["is required"] });
13
+ * ```
14
+ *
15
+ * @packageDocumentation
16
+ */
17
+ export { ZerotalError } from "./ZerotalError.ts";
18
+ export {
19
+ HttpError,
20
+ BadRequestError,
21
+ UnauthorizedError,
22
+ ForbiddenError,
23
+ NotFoundError,
24
+ MethodNotAllowedError,
25
+ ConflictError,
26
+ GoneError,
27
+ UnprocessableEntityError,
28
+ TooManyRequestsError,
29
+ ServiceUnavailableError,
30
+ ContextOutsideRequestError,
31
+ } from "./HttpError.ts";
32
+ export { ValidationError } from "./ValidationError.ts";
33
+ export { ConfigError } from "./ConfigError.ts";
34
+ export { BootCheckError } from "../application/BootDoctor.ts";
35
+ export type { BootCheckFailure } from "../application/BootDoctor.ts";
36
+ export { ConfigValidationError } from "../config/validation.ts";
37
+ export {
38
+ BindingNotFoundError,
39
+ ScopedOutsideRequestError,
40
+ ScopedAfterFlushError,
41
+ SyncResolutionError,
42
+ CircularDependencyError,
43
+ FacadeAccessedBeforeBootError,
44
+ FacadeBindingMissingError,
45
+ ContainerLockedError,
46
+ } from "./ContainerErrors.ts";
@@ -0,0 +1,66 @@
1
+ /**
2
+ * A serialisable wrapper that lets an event listener be deferred to a queue:
3
+ * it captures the listener name and event payload, can round-trip through a
4
+ * plain object, and re-dispatches the listener when the worker runs it.
5
+ */
6
+ import { currentApp } from "../application/currentApp.ts";
7
+
8
+ /** A queued event listener, carrying everything the worker needs to run it later. */
9
+ export class CallQueuedListener {
10
+ readonly queue: string;
11
+ readonly maxAttempts: number;
12
+ readonly retryDelay: number;
13
+
14
+ constructor(
15
+ public readonly listenerName: string,
16
+ public readonly eventName: string,
17
+ public readonly eventPayload: any,
18
+ queueName: string | boolean = "default",
19
+ maxAttempts = 3,
20
+ retryDelay = 1000,
21
+ ) {
22
+ this.queue = typeof queueName === "string" ? queueName : "default";
23
+ this.maxAttempts = maxAttempts;
24
+ this.retryDelay = retryDelay;
25
+ }
26
+
27
+ /** Serialise this listener to a plain object for storage on the queue. */
28
+ payload(): Record<string, unknown> {
29
+ return {
30
+ listenerName: this.listenerName,
31
+ eventName: this.eventName,
32
+ eventPayload: this.eventPayload,
33
+ queue: this.queue,
34
+ maxAttempts: this.maxAttempts,
35
+ retryDelay: this.retryDelay,
36
+ };
37
+ }
38
+
39
+ /** The job class name the queue uses to route this listener back to {@link fromPayload}. */
40
+ get className(): string {
41
+ return "CallQueuedListener";
42
+ }
43
+
44
+ /** Reconstruct a queued listener from the plain object produced by {@link payload}. */
45
+ static fromPayload(payload: Record<string, unknown>) {
46
+ return new CallQueuedListener(
47
+ payload.listenerName as string,
48
+ payload.eventName as string,
49
+ payload.eventPayload,
50
+ payload.queue as string,
51
+ payload.maxAttempts as number,
52
+ payload.retryDelay as number,
53
+ );
54
+ }
55
+
56
+ /** Run the wrapped listener through the live emitter. No-op if no emitter is bound. */
57
+ async handle(): Promise<void> {
58
+ const application = currentApp();
59
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- the events binding has no cross-package type here; the dispatch method is checked at the call site.
60
+ const emitter = application.container.tryMake("events") as any;
61
+ if (!emitter) return;
62
+
63
+ // Emitter needs to expose a way to execute a listener by name with a raw payload
64
+ await emitter.dispatchQueuedListener(this.listenerName, this.eventPayload);
65
+ }
66
+ }