@elysiumjs/core 0.8.0 → 0.10.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,83 @@
1
1
  # @elysiumjs/core
2
2
 
3
+ ## 0.10.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`68a0cdd`](https://github.com/na2axl/elysiumjs/commit/68a0cdd8b2e4a003d6e0af2d78985623cae40428) Thanks [@na2axl](https://github.com/na2axl)! - Route the framework's own diagnostics through the logger service.
8
+
9
+ Breaking changes:
10
+
11
+ - `@Event.listen()` with a mode other than `on` or `once` now throws an `ElysiumError` when the class is defined, instead of logging to the console and ignoring the listener.
12
+
13
+ Fixes and additions:
14
+
15
+ - `getLogger(context?)` and `LOGGER_SERVICE` (`'logger'`) are exported by `@elysiumjs/core`. `getLogger()` returns the `logger` service when one is registered (such as the one `HermesModule` provides), and a `ConsoleLogger` otherwise or when the service cannot be built or fails to create a child logger. It is resolved at call time from the global scope only, so a module's own `logger` service does not replace it, and returns a child logger bound to `context` when one is given.
16
+ - `Middleware.logger` (used by `RequestLoggerMiddleware`) falls back to a `ConsoleLogger` when no `logger` service is registered. It threw before.
17
+ - Core diagnostics go through the logger with structured context instead of `console.*`: the debug-mode request log (`debug`, with `method` and `url`), `onDestroy` failures on shutdown (`error`, with `target` and `error`), WAMP connection attempts (`warn` while retrying, `error` when giving up, with `connection`, `attempt`/`attempts`, `maxRetries` and `error`), and WAMP registrations and subscriptions (`info`, with `connection`, `topic` and the registration or subscription id).
18
+ - In debug mode, incoming requests are logged through the logger at `debug` level instead of printed with `console.log`. With `HermesModule`, they show only when its `level` is `debug` or `trace`.
19
+ - A failed WAMP registration or subscription is logged at `error` level and emitted as `elysium:error` with an error naming the topic and the connection, instead of becoming an unhandled rejection.
20
+ - The `elysium:error` emitted for a WAMP client error names the connection and its URL and includes the client's message (or the value it reported, when it is not an `Error`), with that value as `cause` when there is one.
21
+ - `HermesLogger` serialises an `error` field of the log context like pino's `err` (type, message and stack), so logged errors keep their stack trace. Elysium logs errors under `error`.
22
+ - `@Logged` resolves its fallback logger with `getLogger()`, and logs a thrown error whole under `error` (it logged only its message), so its stack trace is kept.
23
+ - The `elysium:error` emitted when a WAMP connection gives up states the number of attempts actually made (the first one plus the retries), as the logged `attempts` does. It stated `maxRetries` before.
24
+
25
+ - [`76ba0ff`](https://github.com/na2axl/elysiumjs/commit/76ba0ff18f3c196cebc2189426328f67cb699f46) Thanks [@na2axl](https://github.com/na2axl)! - `onRequest` middlewares registered on a module or a controller now run only for its routes. Elysia runs `onRequest` hooks on every request of the app, so such a middleware could reject requests meant for other modules.
26
+
27
+ They now run in the transform step of their own routes, so after routing and body parsing, and each one runs right before its own `onTransform`: module `onRequest`, module `onTransform`, then controller `onRequest`, controller `onTransform`. Any value they return still stops the request and is sent as the response. Application middlewares still run `onRequest` before routing, for every request.
28
+
29
+ - [`bacce71`](https://github.com/na2axl/elysiumjs/commit/bacce7146f667a1a46bf474dc0344fb0111ae12a) Thanks [@na2axl](https://github.com/na2axl)! - WebSocket controllers now run the middlewares registered on them with `@Middleware.register()`, on the upgrade request. `onRequest`, `onTransform` and `onBeforeHandle` can refuse the connection by returning a response. `onAfterHandle` and `onAfterResponse` run after the upgrade, with no response to read.
30
+
31
+ ### Patch Changes
32
+
33
+ - [`2c49acd`](https://github.com/na2axl/elysiumjs/commit/2c49acd3b552aa43765d70339fa216961499e1b5) Thanks [@na2axl](https://github.com/na2axl)! - The tenant set by a middleware, such as a `TenantMiddleware` reading its own header, is no longer replaced by the `x-tenant-id` header (or `public`) before the handler runs.
34
+
35
+ - [`4ebf029`](https://github.com/na2axl/elysiumjs/commit/4ebf029611e6d753a4f1bfdbe247a75d954d9ded) Thanks [@na2axl](https://github.com/na2axl)! - WAMP clients use Bun's own `WebSocket` again. The subclass that faked the `protocol` field is gone, since Bun now fills it (oven-sh/bun#18744).
36
+
37
+ ## 0.9.0
38
+
39
+ ### Minor Changes
40
+
41
+ - [`5fef4a7`](https://github.com/na2axl/elysiumjs/commit/5fef4a7199f502fc4fe035299e7670739459a108) Thanks [@na2axl](https://github.com/na2axl)! - Fix tenant isolation, transactions and query caching in the database layer.
42
+
43
+ Breaking changes:
44
+
45
+ - `TenantMiddleware.validateTenant()` now rejects every tenant unless you override it, and tenant identifiers must match `TENANT_ID_PATTERN` (1 to 63 ASCII letters, digits, `_` or `-`, not starting with `pg_` and other than `information_schema`, in any case) before it is called. Requests that send any other tenant now fail with `Invalid tenant`. Requests without a tenant still resolve to `public` on `SimpleTenantMiddleware`. To migrate, check the tenant against your own data:
46
+
47
+ ```ts
48
+ class OrganizationMiddleware extends SimpleTenantMiddleware {
49
+ protected async validateTenant(tenant: string): Promise<boolean> {
50
+ return Organization.exists(tenant);
51
+ }
52
+ }
53
+ ```
54
+
55
+ - `getTenantSchema()`, `registerTenantSchema()` and `wrapTenantSchema()` throw on tenant identifiers that do not match `TENANT_ID_PATTERN`.
56
+ - `setConnectionTenant()` is removed. It set the tenant on a pooled connection for the whole session, so the tenant leaked to other requests. Use `withRLS()`, or `Database.transaction()` in RLS mode, which scope the tenant to a transaction.
57
+ - `RepositoryInterface.update()` and `delete()`, and the `DrizzleRepository` implementations, return `null` when no row has the id. They returned `undefined` while typed as a row. `DrizzleRepository` also requires a model whose table has an `id` column.
58
+ - `@transactional` handlers run through the `db.transaction` service that `DrizzleModule` provides (its name is exported by `@elysiumjs/core` as `DATABASE_TRANSACTION_SERVICE`, and still re-exported by `@elysiumjs/mnemosyne`). A transactional handler without a database module now fails with an error naming the service.
59
+ - `@transactional` generator handlers commit only when the whole stream was sent. A stream closed early, or whose client disconnects (even while the handler is waiting), rolls back.
60
+ - In RLS mode, a transaction is bound to one tenant for its whole life: the tenant set (with a transaction-local `set_config`) when `Database.transaction()`, a `@transactional` handler, `withRLS()` or `withIsolation()` opens it, from the explicit tenant or the current one (`public` when there is none). Work that finds a transaction open on its connection (`withRLS()`, `withIsolation()`, `Database.transaction()`, repository calls) joins it only when its tenant is the transaction's tenant, and throws `TenantMismatchError` otherwise, without running anything: the tenant of an open transaction never changes. Inside a transaction, `Database.getConnection()` only returns it to work for its tenant (the current tenant, or `public`), and throws `TenantMismatchError` otherwise, so raw queries never run for another tenant; queries on the `tx` handle a callback received run as the transaction's tenant. Work for several tenants, such as an admin fan-out, must use one `withRLS()` per tenant outside any transaction, not nested in one.
61
+ - In RLS mode, each repository call made outside a transaction runs in its own transaction bound to the current tenant: `BEGIN`, `set_config` of the tenant, the query, `COMMIT`. These calls do not use the query cache, since the cache is bypassed inside transactions. Group calls in `Database.transaction()` or `withRLS()` to share one transaction.
62
+ - In RLS mode, code running without a tenant in context (background jobs, CLI commands, scripts) is scoped to the `public` tenant. Set the tenant with `withTenant(tenant, () => ...)` around the work (repository calls and `Database.transaction()` then use it), or with `withRLS(tenant, (tx) => ...)`.
63
+ - `migration:run` validates `--tenant` against `TENANT_ID_PATTERN`. Without a terminal it no longer creates a missing schema: pass `--create-schema` (which also skips the question in a terminal), or it fails.
64
+ - Query cache keys start with the connection name, and each connection has its own cache transaction scope. When several applications or environments share one cache store and use the same connection names for different databases, set `cache: { prefix }` on each connection: the prefix comes before the connection name in every key (empty by default).
65
+ - `DrizzleDatabaseCache` keys and values changed: entries written by earlier versions are ignored. The default TTL of cached queries (including with `cache: true`) is now 60 seconds instead of 1 second, and is set with `cache: { ttl }` in seconds.
66
+ - `Cache.memory` keeps a separate LRU per tag namespace, so `lruSize` applies to each namespace.
67
+
68
+ Fixes and additions:
69
+
70
+ - In RLS mode, repository operations outside a transaction run in one bound to the current tenant, through the strategy's `withIsolation()`. Transactions opened with `Database.transaction()` or by `@transactional` handlers set the tenant variable when they open. `withIsolation()` and `withRLS()` expose their transaction to repositories and `Database.getConnection()`. `TenantMismatchError` is exported.
71
+ - `Database.transaction(callback, connection?)` opens a transaction and registers it for the callback. When a transaction is already open on the same connection, the callback joins it (no savepoint: an error rolls back the whole transaction); in RLS mode only if the current tenant is the transaction's tenant (see above). Transactions are tracked by connection, so every name of a connection (`default` and the name it aliases) sees them, and named connections take part in transactions. `Database.getBaseConnection()`, `Database.inTransaction()` and `Database.runInTransaction()` are added. The `db:tx` context entry is still set for the default connection.
72
+ - `@transactional` generator handlers keep their transaction open until the stream ends, then commit (or roll back if the handler throws). They previously committed before running and failed with `generator.next is not a function`. Generator handlers also see the request context (`tenant`, `http:context`).
73
+ - The query cache is scoped to the connection and the current tenant, is shared correctly by several app instances (table versions stored in the cache), is not read or written inside transactions, and invalidates the tables a transaction changed once it commits. A result is only stored when no mutation happened since its query started, including when concurrent queries of the same key finish out of order. Cached queries honour Drizzle's `ex`, `px`, `exat`, `pxat` and `keepTtl` options.
74
+ - Cache namespaces (`Cache.redis.tags(...)`, `Cache.memory.tags(...)`) no longer overwrite each other: reads, deletes and `clear()` stay within their namespace.
75
+ - `getSessionTenant()` reads the Bun SQL result correctly, and `getCurrentTenant()` works without an application instance (for example under `Application.testing`).
76
+ - `migration:run` takes an advisory lock per tenant, adds a unique index on `elysium_migrations.name` (also to existing tables), finds the migrations from the project root (`.elysiumrc`), sets the `search_path` for the transaction only, asks before opening the transaction whether to create a missing schema, and always closes its client. Existing duplicate migration names must be removed before the unique index can be created.
77
+ - `migration:generate` only removes `"public".` qualifiers from the migration it generates, and leaves string literals, comments and dollar-quoted bodies (`DO` blocks, functions) untouched.
78
+ - `DatabaseDriver.createConnection()` receives the connection name as an optional second argument.
79
+ - `KeyvRedis` deletes with a single `UNLINK` (or `DEL`) per call or batch and reports failed deletes in `clear()` as `error` events.
80
+
3
81
  ## 0.8.0
4
82
 
5
83
  ### Minor Changes
package/README.md CHANGED
@@ -133,4 +133,4 @@ Feel free to open issues for any bugs or questions you may have. Pull requests a
133
133
 
134
134
  ## License
135
135
 
136
- This project is licensed under the Apache License, Version 2.0. See the [LICENSE](https://github.com/workbud/elysium/blob/main/LICENSE) file for more information.
136
+ This project is licensed under the Apache License, Version 2.0. See the [LICENSE](https://github.com/na2axl/elysiumjs/blob/main/LICENSE) file for more information.
package/dist/app.d.ts CHANGED
@@ -51,9 +51,15 @@ export type AppProps = {
51
51
  };
52
52
  /**
53
53
  * Type for the application context.
54
+ *
55
+ * - `tenant`: the current tenant.
56
+ * - `http:context`: the context of the HTTP request being handled.
57
+ * - `db:transactions`: the open database transactions, keyed by the connection they run on.
58
+ * - `db:tx`: the transaction open on the default database connection. Kept for existing callers;
59
+ * prefer `db:transactions`.
54
60
  * @author Axel Nana <axel.nana@workbud.com>
55
61
  */
56
- export type AppContext = AsyncLocalStorage<Map<'tenant' | 'http:context' | 'db:tx', unknown>>;
62
+ export type AppContext = AsyncLocalStorage<Map<'tenant' | 'http:context' | 'db:transactions' | 'db:tx', unknown>>;
57
63
  /**
58
64
  * Base class for the application main entry.
59
65
  * @author Axel Nana <axel.nana@workbud.com>
@@ -99,6 +99,12 @@ export declare namespace Container {
99
99
  * @throws {ServiceNotReadyError} If the service or its module is still initialising.
100
100
  */
101
101
  function get(name: string): unknown;
102
+ /**
103
+ * Gets a service of the global scope, ignoring the services of the current module context.
104
+ * @returns The service, or `null` if there is no global service with that name.
105
+ * @throws {ServiceNotReadyError} If the service or its module is still initialising.
106
+ */
107
+ function getGlobal(name: string): unknown;
102
108
  /**
103
109
  * Like {@link get}, but awaits the `onInit` of the service and of its owning module.
104
110
  * @throws {ServiceNotFoundError | ServiceNotVisibleError | EnvironmentSkippedModuleError}
package/dist/event.d.ts CHANGED
@@ -125,6 +125,7 @@ export declare namespace Event {
125
125
  /**
126
126
  * Marks a method as an event listener.
127
127
  * @param options The decorator options.
128
+ * @throws {ElysiumError} If `options.mode` is neither `on` nor `once`.
128
129
  */
129
130
  const listen: (options: ListenProps) => MethodDecorator;
130
131
  }
package/dist/http.d.ts CHANGED
@@ -62,6 +62,12 @@ export type Context<TBody = unknown, TController = unknown, TModule extends Modu
62
62
  body: TBody;
63
63
  }, Singleton<TController, TModule>>;
64
64
  export type ElysiaApp = Elysia<Route, Singleton>;
65
+ /**
66
+ * Name of the global service running a callback in a database transaction, used by
67
+ * `@transactional` handlers. Provided by database modules such as `DrizzleModule`.
68
+ * @author Axel Nana <axel.nana@workbud.com>
69
+ */
70
+ export declare const DATABASE_TRANSACTION_SERVICE = "db.transaction";
65
71
  /**
66
72
  * The scope of an HTTP controller.
67
73
  * @author Axel Nana <axel.nana@workbud.com>
package/dist/index.d.ts CHANGED
@@ -5,8 +5,8 @@ export { type CommandArgumentProps, type CommandClass, Command, CommandArgumentT
5
5
  export { ConsoleFormat, InteractsWithConsole } from './console';
6
6
  export { Env } from './env';
7
7
  export { type EventData, type EventHandler, Event } from './event';
8
- export { type LoggerInterface, ConsoleLogger } from './logger';
9
- export { type Context, type Route, HttpControllerScope, Http } from './http';
8
+ export { type LoggerInterface, ConsoleLogger, getLogger, LOGGER_SERVICE } from './logger';
9
+ export { type Context, type Route, DATABASE_TRANSACTION_SERVICE, HttpControllerScope, Http } from './http';
10
10
  export { Metadata } from './metadata';
11
11
  export { Middleware } from './middleware';
12
12
  export { RequestLoggerMiddleware } from './middlewares/request-logger.middleware';
package/dist/index.js CHANGED
@@ -16,4 +16,4 @@ var __legacyDecorateClassTS=function(decorators,target,key,desc){var c=arguments
16
16
  `}}else helpText+=`This command has no arguments.
17
17
  `;return trim(helpText,`
18
18
  `)}validateArgs(args){let missing=[],invalid=[],commandArgs=this.getArguments();if(Object.keys(args).includes("help"))return{valid:!0,missing:[]};for(let arg of commandArgs){if(arg.required&&(args[arg.name]===void 0||args[arg.name]===null)){missing.push(arg.name);continue}if(args[arg.name]===void 0)continue;if(arg.type==="enum"&&arg.enum&&!arg.enum.includes(args[arg.name]))invalid.push(`${arg.name} (must be one of: ${arg.enum.join(", ")})`);if(arg.type==="array"&&arg.arrayType==="enum"&&arg.enum){let invalidItems=args[arg.name].filter((item)=>!arg.enum.includes(item));if(invalidItems.length>0)invalid.push(`${arg.name} (contains invalid values: ${invalidItems.join(", ")}. Must be one of: ${arg.enum.join(", ")})`)}}if(invalid.length>0)console.error(`Invalid argument values: ${invalid.join(", ")}
19
- `);return{valid:missing.length===0&&invalid.length===0,missing}}processArgs(argv){let processed={},commandArgs=this.getArguments();for(let i=0;i<argv.length;i++){let arg=argv[i];if(arg.startsWith("--")){let argName=arg.slice(2);if(i+1<argv.length&&!argv[i+1].startsWith("--"))processed[argName]=argv[i+1],i++;else processed[argName]=!0}}for(let arg of commandArgs){if(processed[arg.name]===void 0&&arg.default!==void 0){processed[arg.name]=arg.default;continue}if(processed[arg.name]===void 0)continue;if(typeof processed[arg.name]==="string")switch(arg.type||this.inferType(arg.default)){case"number":processed[arg.name]=Number(processed[arg.name]);break;case"boolean":let value=processed[arg.name].toLowerCase();processed[arg.name]=value==="true"||value==="1"||value==="yes";break;case"enum":if(arg.enum&&arg.enum.length>0&&typeof arg.enum[0]==="number")processed[arg.name]=Number(processed[arg.name]);break;case"array":let arrayValues=processed[arg.name].split(",").map((item)=>item.trim());if(arg.arrayType)processed[arg.name]=arrayValues.map((item)=>{switch(arg.arrayType){case"number":return Number(item);case"boolean":return item.toLowerCase()==="true"||item.toLowerCase()==="1"||item.toLowerCase()==="yes";case"enum":return item;default:return item}});else processed[arg.name]=arrayValues;break;case"string":default:break}}return processed}progress(total,title="Processing"){return new CommandProgressBar(total,title)}spinner(title="Processing",frames,frameDelay){return new CommandSpinner(title,frames,frameDelay)}inferType(defaultValue){if(defaultValue===void 0)return"string";switch(typeof defaultValue){case"number":return"number";case"boolean":return"boolean";default:return isArray(defaultValue)?"array":"string"}}}class CommandProgressBar extends InteractsWithConsole{#active=!1;#total=0;#current=0;#title="";#startTime=0;constructor(total=0,title="Processing"){super();this.#total=total,this.#title=title,this.#active=!0,this.#current=0,this.#startTime=Date.now(),this.update(0)}update(increment=1,newTitle){if(!this.#active)return;if(this.#current+=increment,newTitle)this.#title=newTitle;let percentage=Math.min(100,Math.floor(this.#current/this.#total*100)),elapsedSec=(Date.now()-this.#startTime)/1000,estimatedTotalSec=0,estimatedRemainingSec=0,timeInfo="";if(this.#current>0)estimatedTotalSec=elapsedSec/this.#current*this.#total,estimatedRemainingSec=Math.max(0,estimatedTotalSec-elapsedSec),timeInfo=` | ${this.formatTime(elapsedSec)} elapsed | ${this.formatTime(estimatedRemainingSec)} remaining`;let barWidth=30,completedWidth=Math.floor(percentage/100*barWidth),remainingWidth=barWidth-completedWidth,bar="["+"=".repeat(completedWidth)+(remainingWidth>0?">":"")+" ".repeat(Math.max(0,remainingWidth-(remainingWidth>0?1:0)))+"]",progressLine=`${this.#title}: ${bar} ${percentage}% (${this.#current}/${this.#total})${timeInfo}`;if(this.clearLine(),this.write(progressLine,!1),this.#current>=this.#total)this.complete()}complete(message){if(!this.#active)return;this.#current=this.#total;let elapsedSec=(Date.now()-this.#startTime)/1000;if(this.clearLine(),message)this.write(`${message} (completed in ${this.formatTime(elapsedSec)})`);else this.write(`${this.#title} completed in ${this.formatTime(elapsedSec)}`);this.#active=!1}pause(message){if(!this.#active)return;this.newLine(),this.write(message),this.update(0)}isActive(){return this.#active}getCurrent(){return this.#current}getTotal(){return this.#total}getPercentage(){return Math.min(100,Math.floor(this.#current/this.#total*100))}}class CommandSpinner extends InteractsWithConsole{#active=!1;#title="";#startTime=0;#intervalId=null;#currentFrame=0;#frames=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"];#frameDelay=80;constructor(title="Processing",frames,frameDelay){super();if(this.#title=title,frames)this.#frames=frames;if(frameDelay)this.#frameDelay=frameDelay;if(this.#active)return;this.#active=!0,this.#startTime=Date.now(),this.#currentFrame=0,this.#intervalId=setInterval(()=>{this.render()},this.#frameDelay),this.render()}update(newTitle){if(!this.#active)return;this.#title=newTitle}complete(message,success=!0){if(!this.#active)return;if(this.#intervalId)clearInterval(this.#intervalId),this.#intervalId=null;let elapsedSec=(Date.now()-this.#startTime)/1000;this.clearLine();let time=this.formatTime(elapsedSec);if(message)this[success?"success":"error"](`${message} (${time})`);else this[success?"success":"error"](`${this.#title} completed in ${time}`);this.#active=!1}fail(message){this.complete(message,!1)}pause(message){if(!this.#active)return;if(this.#intervalId)clearInterval(this.#intervalId),this.#intervalId=null;this.clearLine(),this.write(message),this.#intervalId=setInterval(()=>{this.render()},this.#frameDelay)}isActive(){return this.#active}getElapsedTime(){return(Date.now()-this.#startTime)/1000}render(){if(!this.#active)return;let elapsedSec=this.getElapsedTime(),frame=this.#frames[this.#currentFrame];this.#currentFrame=(this.#currentFrame+1)%this.#frames.length,this.clearLine(),this.write(`${frame} ${this.#title} (${this.formatTime(elapsedSec)})`,!1)}}var env=new Map,initEnv=(vars)=>{for(let[key,value]of Object.entries(vars))env.set(key,value)},Env;((Env)=>(Env.get=(name,defaultValue)=>env.get(name)??defaultValue,Env.exists=(name)=>env.has(name),Env.clear=()=>{env.clear()}))(Env||={});class ElysiumError extends Error{constructor(message){super(message);this.name=new.target.name}}class ModuleCycleError extends ElysiumError{path;constructor(path){super(`Module import cycle detected: ${path.join(" -> ")}`);this.path=path}}class InvalidModuleError extends ElysiumError{className;constructor(className){super(`${className} is not a valid module. Decorate it with @Module().`);this.className=className}}class DuplicateGlobalServiceError extends ElysiumError{serviceName;modules;constructor(serviceName,modules){super(`Global service "${serviceName}" is declared by both ${modules[0]} and ${modules[1]}.`);this.serviceName=serviceName;this.modules=modules}}class DuplicateCommandError extends ElysiumError{command;modules;constructor(command,modules){super(`Command "${command}" is registered by both ${modules[0]} and ${modules[1]}.`);this.command=command;this.modules=modules}}class ConflictingModuleConfigError extends ElysiumError{module;sites;constructor(module,sites){super(`Module ${module} is configured with conflicting options at ${sites[0]} and ${sites[1]}.`);this.module=module;this.sites=sites}}class MissingModuleConfigError extends ElysiumError{module;keys;constructor(module,keys){super(`Module ${module} is missing required configuration: ${keys.join(", ")}.`);this.module=module;this.keys=keys}}class InvalidModuleConfigError extends ElysiumError{module;key;reason;constructor(module,key,reason){super(`Module ${module} has an invalid "${key}" configuration: ${reason}.`);this.module=module;this.key=key;this.reason=reason}}class ServiceNotVisibleError extends ElysiumError{service;requester;module;owner;constructor(service,requester,module,owner){let where=module?` in module ${module}`:"",hint=owner?` Either import ${owner} or add it to ${owner}'s exports.`:"";super(`Service "${service}" requested by ${requester}${where} is not visible.${hint}`);this.service=service;this.requester=requester;this.module=module;this.owner=owner}}class ServiceNotFoundError extends ElysiumError{service;constructor(service){super(`Service "${service}" was not found.`);this.service=service}}class ServiceNotReadyError extends ElysiumError{service;owner;constructor(service,owner=null){let where=owner?` Its module ${owner} initialises asynchronously.`:"";super(`Service "${service}" is not ready yet.${where} Use "await Service.resolve()" to await it, or set "static boot = true" on the command to initialise every module before it runs.`);this.service=service;this.owner=owner}}class CircularDependencyError extends ElysiumError{path;constructor(path){super(`Circular service dependency detected: ${path.join(" -> ")}`);this.path=path}}class DuplicateModuleNameError extends ElysiumError{moduleName;constructor(moduleName){super(`Two different modules are both named "${moduleName}". Module names identify modules in configuration and errors, so they must be unique: rename one of them.`);this.moduleName=moduleName}}class EnvironmentSkippedModuleError extends ElysiumError{service;requester;skippedModule;environment;constructor(service,requester,skippedModule,environment){super(`Service "${service}" requested by ${requester} belongs to module ${skippedModule}, which is skipped in the "${environment}" environment.`);this.service=service;this.requester=requester;this.skippedModule=skippedModule;this.environment=environment}}class InvalidControllerError extends ElysiumError{className;moduleName;constructor(className,moduleName){super(`${className}, listed in the controllers of ${moduleName}, is not a controller. Decorate it with @Http.controller(), @Websocket.controller() or @Wamp.controller().`);this.className=className;this.moduleName=moduleName}}class EventListenerNotSupportedError extends ElysiumError{className;method;moduleName;constructor(className,method,moduleName){super(`${className}.${method} (module ${moduleName}) is an instance event listener, but ${className} is not listed in the services of ${moduleName}. Make the method static or register the class as a service.`);this.className=className;this.method=method;this.moduleName=moduleName}}import{EventEmitter}from"events";import{isString}from"radash";class EventBus extends EventEmitter{static instance=new EventBus;constructor(){super();this.on("error",(error)=>this.emitError(error))}emitError(error){this.emit("elysium:error",{data:error,source:null})}emitEvent(event,data,source=null){this.emit(event,{data,source})}}function getEventListeners(cls){return Metadata.getOwn(Symbols.eventListeners,cls)??[]}var recordListener=(target,method,mode,props)=>{let isStatic=typeof target==="function",owner=isStatic?target:target.constructor;Metadata.define(Symbols.eventListeners,[...getEventListeners(owner),{event:props.event,method,mode,prepend:props.prepend??!1,static:isStatic}],owner)},Event;((Event)=>{Event.emit=(event,data,source=null)=>{EventBus.instance.emitEvent(event,data,source)};function on(event,handler){if(isString(event))EventBus.instance.on(event,handler);else return function(target,propertyKey){recordListener(target,propertyKey,"on",event)}}Event.on=on;function once(event,handler){if(isString(event))EventBus.instance.once(event,handler);else return function(target,propertyKey){recordListener(target,propertyKey,"once",event)}}Event.once=once,Event.off=(event)=>{EventBus.instance.removeAllListeners(event)},Event.clear=()=>{EventBus.instance.removeAllListeners()},Event.listen=(options)=>{if(options.mode==="on")return on(options);else if(options.mode==="once")return once(options);return function(_target,_propertyKey,_descriptor){console.error("Unknown mode provided to @listen. Use either 'on' or 'once'.")}}})(Event||={});import{isEqual as isEqual2}from"radash";import{AsyncLocalStorage}from"async_hooks";var storage=new AsyncLocalStorage,ModuleContext;((ModuleContext)=>{function run(node,fn){return storage.run(node,fn)}ModuleContext.run=run;function current(){return storage.getStore()}ModuleContext.current=current})(ModuleContext||={});import{isString as isString2}from"radash";var ServiceScope;((ServiceScope)=>{ServiceScope[ServiceScope.SINGLETON=0]="SINGLETON";ServiceScope[ServiceScope.FACTORY=1]="FACTORY"})(ServiceScope||={});var isClass=(value)=>typeof value==="function"&&value.prototype&&value.prototype.constructor===value,nameOf=(service)=>isString2(service)?service:service.name,Service;((Service)=>{Service.register=(props)=>function(target){Metadata.define(Symbols.serviceOptions,props??{},target)},Service.inject=(service)=>function(target,propertyKey,parameterIndex){let services=Metadata.get(Symbols.services,target,propertyKey)??[],types=Metadata.get("design:paramtypes",target,propertyKey)??[],name=isString2(service)?service:service?.name??types[parameterIndex].name;services.push({name,index:parameterIndex}),Metadata.define(Symbols.services,services,target,propertyKey)},Service.get=(service)=>Container.get(nameOf(service)),Service.resolve=(service)=>Container.resolve(nameOf(service),"Service.resolve"),Service.registerLazy=(name,resolver)=>{addGlobal(name,0,resolver)},Service.make=(service)=>Container.make(service),Service.bind=(service,name)=>{let s=Service.make(service);return Service.instance(name??service.name,s)};function factory(serviceOrName,nameOrFactory){let serviceName=isString2(serviceOrName)?serviceOrName:nameOrFactory??serviceOrName.name,create=()=>isClass(serviceOrName)?Service.make(serviceOrName):nameOrFactory();return addGlobal(serviceName,1,create),create()}Service.factory=factory,Service.instance=(service,instance)=>(addGlobal(nameOf(service),0,()=>instance,{instance}),instance),Service.remove=(service)=>{Container.removeGlobal(nameOf(service))},Service.exists=(service)=>Container.hasGlobal(nameOf(service)),Service.isConstructed=(name)=>Container.constructed().some((entry)=>entry.name===name),Service.clear=()=>{Container.clearGlobal()},Service.keys=(pattern)=>{let regexPattern="^"+pattern.replace(/[-[\]{}()+.,\\^$|#\s]/g,"\\$&").replace(/\*/g,".*").replace(/\?/g,".")+"$",regex=new RegExp(regexPattern);return Container.globalNames().filter((key)=>regex.test(key))};let addGlobal=(name,scope,create,prebuilt)=>{Container.add({name,scope,global:!0,owner:null,create,initialised:prebuilt!==void 0,...prebuilt?{instance:prebuilt.instance}:{}})}})(Service||={});var GLOBAL_OWNER="<global>",globalScope=new Map,moduleScopes=new Map,building=[],constructedEntries=[],overridden=new Set,visibility=null,moduleInitHook=null,skippedLookup=null,isPromise=(value)=>typeof value==="object"&&value!==null&&typeof value.then==="function",ownerId=(owner)=>owner?.id??GLOBAL_OWNER,inOwnerContext=(owner,fn)=>owner?ModuleContext.run(owner,fn):fn(),runOnInit=(entry,instance)=>{let onInit=instance?.onInit;if(typeof onInit!=="function")return;let result=inOwnerContext(entry.owner,()=>onInit.call(instance));return isPromise(result)?result.then(()=>{return}):void 0},construct=(entry)=>{let at=building.indexOf(entry);if(at!==-1)throw new CircularDependencyError([...building.slice(at),entry].map((e)=>e.name));building.push(entry);try{return inOwnerContext(entry.owner,entry.create)}finally{building.pop()}},instantiate=(entry)=>{if(entry.scope===1){let instance=construct(entry);return{instance,ready:runOnInit(entry,instance)}}if(entry.initFailure)throw entry.initFailure.error;if("instance"in entry)return{instance:entry.instance,ready:entry.initialised?void 0:entry.initPromise};let instance=construct(entry);entry.instance=instance,constructedEntries.push({entry,instance});let ready;try{ready=runOnInit(entry,instance)}catch(error){throw entry.initFailure={error},error}if(ready)entry.initPromise=ready.then(()=>{entry.initialised=!0},(error)=>{throw entry.initFailure={error},error}),entry.initPromise.catch(()=>{});else entry.initialised=!0;return{instance,ready:entry.initPromise}},needsOwnerInit=(entry)=>entry.owner!==null&&(entry.scope===1||!("instance"in entry)),entryValue=(entry)=>{if(needsOwnerInit(entry)&&moduleInitHook){let pending=moduleInitHook(entry.owner);if(isPromise(pending))throw pending.catch(()=>{}),new ServiceNotReadyError(entry.name,entry.owner?.id??null)}let{instance,ready}=instantiate(entry);if(ready)throw new ServiceNotReadyError(entry.name,entry.owner?.id??null);return instance},prepare=async(entry,path)=>{let at=path.indexOf(entry);if(at!==-1)throw new CircularDependencyError([...path.slice(at),entry].map((e)=>e.name));let trail=[...path,entry];if(needsOwnerInit(entry)&&moduleInitHook)await moduleInitHook(entry.owner);if(!entry.cls||"instance"in entry&&entry.scope!==1)return;let dependencies=Metadata.get(Symbols.services,entry.cls)??[],found=inOwnerContext(entry.owner,()=>dependencies.map((dependency)=>Container.lookup(dependency.name,ModuleContext.current())));for(let dependency of found){if(!dependency)continue;if(dependency.entry.scope===1)await prepare(dependency.entry,trail);else await entryValueAsync(dependency.entry,trail)}},entryValueAsync=async(entry,path=[])=>{await prepare(entry,path);let{instance,ready}=instantiate(entry);return await ready,instance},Container;((Container)=>{function register(owner,def,config){let isClass=typeof def==="function",options=isClass?Metadata.get(Symbols.serviceOptions,def)??{}:def,name=options.name??(isClass?def.name:"");if(overridden.has(name))return name;return add({name,scope:options.scope??0,global:options.global??owner===null,owner,cls:isClass?def:void 0,create:isClass?()=>make(def):()=>def.factory(config,(service)=>require2(service,def.name)),initialised:!1}),name}Container.register=register;function add(entry){if(entry.global||!entry.owner){let existing=globalScope.get(entry.name);if(existing)throw new DuplicateGlobalServiceError(entry.name,[ownerId(existing.owner),ownerId(entry.owner)]);globalScope.set(entry.name,entry);return}let scope=moduleScopes.get(entry.owner);if(!scope)scope=new Map,moduleScopes.set(entry.owner,scope);if(scope.has(entry.name))throw new ElysiumError(`Service "${entry.name}" is declared twice in module ${entry.owner.id}.`);scope.set(entry.name,entry)}Container.add=add;function setOverrides(names){overridden=new Set(names??[])}Container.setOverrides=setOverrides;function setVisibility(resolver){visibility=resolver}Container.setVisibility=setVisibility;function setModuleInitHook(hook){moduleInitHook=hook}Container.setModuleInitHook=setModuleInitHook;function setSkippedLookup(lookup){skippedLookup=lookup}Container.setSkippedLookup=setSkippedLookup;function lookup(name,from){let found=(entry)=>entry?{entry,owner:entry.owner}:null;if(from){let own=moduleScopes.get(from)?.get(name);if(own)return found(own);for(let imported of visibility?.visibleOwners(from)??[]){let exported=visibility.exportsOf(imported);if(exported===!0||exported.includes(name)){let entry=moduleScopes.get(imported)?.get(name);if(entry)return found(entry)}}}return found(globalScope.get(name))}Container.lookup=lookup;function get(name){let found=lookup(name,ModuleContext.current());return found?entryValue(found.entry):null}Container.get=get;async function resolve(name,requester){let found=lookup(name,ModuleContext.current());if(!found)throw notFound(name,requester);return entryValueAsync(found.entry)}Container.resolve=resolve;function make(cls){let dependencies=Metadata.get(Symbols.services,cls)??[],params=[];for(let dependency of dependencies)params[dependency.index]=require2(dependency.name,cls.name);return new cls(...params)}Container.make=make;function require2(service,requester){let name=typeof service==="string"?service:service.name,found=lookup(name,ModuleContext.current());if(!found)throw notFound(name,requester);return entryValue(found.entry)}function notFound(name,requester){let skipped=skippedLookup?.(name);if(skipped)return new EnvironmentSkippedModuleError(name,requester,skipped.module,skipped.environment);let hidden=[...moduleScopes.values()].map((scope)=>scope.get(name)).find((entry)=>entry!==void 0);return hidden?new ServiceNotVisibleError(name,requester,ModuleContext.current()?.id??null,hidden.owner?.id??null):new ServiceNotFoundError(name)}Container.hasGlobal=(name)=>globalScope.has(name),Container.globalNames=()=>[...globalScope.keys()],Container.removeGlobal=(name)=>{globalScope.delete(name)},Container.clearGlobal=()=>{globalScope.clear()},Container.constructed=()=>constructedEntries.filter(({entry})=>!entry.initFailure).map(({entry,instance})=>({name:entry.name,instance,owner:entry.owner}));function reset(){globalScope.clear(),moduleScopes.clear(),constructedEntries.length=0}Container.reset=reset})(Container||={});var serviceName=(cls)=>Metadata.get(Symbols.serviceOptions,cls)?.name??cls.name,handlerFor=(node,cls,meta)=>{let method=meta.method;return async(data)=>{try{await ModuleContext.run(node,async()=>{if(meta.static)return cls[method](data);return(await Container.resolve(serviceName(cls),cls.name))[method](data)})}catch(e){EventBus.instance.emitError(e)}}};function wireEventListeners(node,owned,services){let plan=[...new Set(owned)].flatMap((cls)=>getEventListeners(cls).map((meta)=>{if(!meta.static&&!services.has(cls))throw new EventListenerNotSupportedError(cls.name,String(meta.method),node.id);return{meta,wrapper:handlerFor(node,cls,meta)}})),bus=EventBus.instance;return plan.map(({meta,wrapper})=>{if(meta.mode==="once")meta.prepend?bus.prependOnceListener(meta.event,wrapper):bus.once(meta.event,wrapper);else meta.prepend?bus.prependListener(meta.event,wrapper):bus.on(meta.event,wrapper);return{event:meta.event,wrapper}})}function isConfiguredModule(value){return typeof value==="object"&&value!==null&&value[Symbols.configuredModule]===!0}function getModuleProps(cls){let props=Metadata.get(Symbols.module,cls);if(props===void 0)throw new InvalidModuleError(cls.name);return props}class Module{static register(props={}){return function(target){Metadata.define(Symbols.module,props,target)}}static configurable(options={}){class ConfigurableModule extends Module{static get config(){return`elysium.module.${this.name}.config`}static with(config){return{[Symbols.configuredModule]:!0,module:this,config}}static resolveConfig(input={}){let resolved=deepMerge({...input},{...options.defaults??{}}),missing=(options.required??[]).filter((key)=>resolved[key]===void 0);if(missing.length>0)throw new MissingModuleConfigError(this.name,missing.map(String));return resolved}get config(){return Metadata.get(Symbols.moduleConfig,this)}}return ConfigurableModule}onInit(){}onDestroy(){}onServe(_app){}}import{isEqual}from"radash";var APP_SITE="<app>",unwrap=(entry)=>isConfiguredModule(entry)?entry.module:entry,isActive=(props,environment)=>props.env===void 0||props.env.includes(environment);function discover(roots,environment){let records=new Map,skipped=[],done=new Set,stack=[],visit=(cls)=>{if(done.has(cls))return;let at=stack.indexOf(cls);if(at!==-1)throw new ModuleCycleError([...stack.slice(at),cls].map((c)=>c.name));let props=getModuleProps(cls);if(!isActive(props,environment)){if(!skipped.includes(cls))skipped.push(cls);return}stack.push(cls);let record={cls,props,imports:[],configs:[]};for(let entry of props.imports??[]){let target=unwrap(entry);if(visit(target),!records.has(target)&&!done.has(target))continue;if(!record.imports.includes(target))record.imports.push(target);if(isConfiguredModule(entry))record.configs.push({target,config:entry.config})}stack.pop(),done.add(cls),records.set(cls,record)};for(let root of roots)visit(unwrap(root));return{records,skipped}}function computeDepths(roots,records){let depths=new Map,frontier=roots.filter((r)=>records.has(r));frontier.forEach((r)=>depths.set(r,1));for(let depth=1;frontier.length>0;depth++){let next=[];for(let cls of frontier)for(let imported of records.get(cls).imports)if(!depths.has(imported))depths.set(imported,depth+1),next.push(imported);frontier=next}return depths}function commandsOf(props,environment){return(props.commands??[]).filter((c)=>!(environment==="production"&&c.dev===!0))}function assertUniqueNames(classes){let seen=new Map;for(let cls of classes){let other=seen.get(cls.name);if(other!==void 0&&other!==cls)throw new DuplicateModuleNameError(cls.name);seen.set(cls.name,cls)}}function assertUniqueCommands(nodes,environment){let seen=new Map;for(let node of nodes)for(let command of commandsOf(node.props,environment)){let name=getCommandName(command),owner=seen.get(name);if(owner!==void 0&&owner!==node.id)throw new DuplicateCommandError(name,[owner,node.id]);seen.set(name,node.id)}}var isModuleClass=(value)=>typeof value==="function"&&Metadata.get(Symbols.module,value)!==void 0,serviceName2=(cls)=>Metadata.get(Symbols.serviceOptions,cls)?.name??cls.name;function buildModuleGraph(rootModules,environment=currentEnvironment()){let{records,skipped}=discover(rootModules,environment);assertUniqueNames([...records.keys()]);let rootClasses=rootModules.map(unwrap),depths=computeDepths(rootClasses,records),sources=new Map,addSource=(target,source)=>sources.set(target,[...sources.get(target)??[],source]);for(let root of rootModules){let target=unwrap(root);if(isConfiguredModule(root)&&records.has(target))addSource(target,{site:APP_SITE,depth:1,config:root.config})}for(let record of records.values())for(let{target,config}of record.configs)addSource(target,{site:record.cls.name,depth:depths.get(record.cls)+1,config});let byClass=new Map;for(let record of records.values())byClass.set(record.cls,{id:record.cls.name,moduleClass:record.cls,props:record.props,imports:record.imports.map((i)=>byClass.get(i)),depth:depths.get(record.cls),configSources:sources.get(record.cls)??[]});let nodes=[...byClass.values()];return assertUniqueCommands(nodes,environment),{nodes,skipped,environment,node:(cls)=>byClass.get(cls),visibleOwners:(node)=>{let result=[],add=(owner)=>{if(owner===void 0||result.includes(owner))return;result.push(owner);let exported=owner.props.exports;if(Array.isArray(exported)){for(let entry of exported)if(isModuleClass(entry))add(byClass.get(entry))}};return node.imports.forEach(add),result},exportsOf:(node)=>{let exported=node.props.exports;if(exported===!0)return!0;return(exported??[]).filter((e)=>!isModuleClass(e)).map((e)=>typeof e==="string"?e:serviceName2(e))},collect:(key)=>nodes.flatMap((module)=>(key==="commands"?commandsOf(module.props,environment):module.props[key]??[]).map((item)=>({item,module})))}}function selectConfig(node){if(node.configSources.length===0)return;let best=Math.min(...node.configSources.map((s)=>s.depth)),tied=node.configSources.filter((s)=>s.depth===best),objects=tied.filter((s)=>typeof s.config!=="function");for(let other of objects.slice(1))if(!isEqual(objects[0].config,other.config))throw new ConflictingModuleConfigError(node.id,[objects[0].site,other.site]);return tied[0]}var KERNEL_SERVICE="elysium.kernel",isConfigurable=(cls)=>typeof cls.resolveConfig==="function",evaluate=(config)=>typeof config==="function"?config():config,scopeOf=(def)=>(typeof def==="function"?Metadata.get(Symbols.serviceOptions,def)?.scope:def.scope)??0,nameOf2=(def)=>typeof def==="function"?Metadata.get(Symbols.serviceOptions,def)?.name??def.name:def.name;class Kernel{moduleGraph;instances=new Map;registered=new Map;initialised=new Set;initialising=new Set;initOrder=[];inFlight=new Map;failures=new Map;ownClasses=new Map;wired=[];shutdownPromise;constructor(moduleGraph){this.moduleGraph=moduleGraph}get graph(){return this.moduleGraph}async register(){let{graph}=this;Service.instance(KERNEL_SERVICE,this),Container.setVisibility(graph),Container.setModuleInitHook((node)=>this.initialised.has(node)||this.initialising.has(node)||this.initSync(node)?void 0:this.initModule(node)),Container.setSkippedLookup(this.buildSkippedLookup());for(let node of graph.nodes){let config=await this.resolveNodeConfig(node),instance=ModuleContext.run(node,()=>Service.make(node.moduleClass));Metadata.define(Symbols.moduleConfig,config,instance),this.instances.set(node,instance);let registered=[];if(isConfigurable(node.moduleClass))Container.register(node,{name:node.moduleClass.config,factory:()=>config},config);let{services}=node.props,defs=typeof services==="function"?services(config):services??[],serviceClasses=defs.filter((d)=>typeof d==="function");this.ownClasses.set(node,{owned:[...serviceClasses,...node.props.controllers??[]],services:new Set(serviceClasses)});for(let def of defs)registered.push({name:Container.register(node,def,config),scope:scopeOf(def)});this.registered.set(node,registered)}}initModule(node){if(this.initialised.has(node))return Promise.resolve();let failure=this.failures.get(node);if(failure)return Promise.reject(failure.error);let pending=this.inFlight.get(node);if(!pending)pending=this.runInit(node),pending.catch(()=>{}),this.inFlight.set(node,pending);return pending}async bootEager(){for(let node of this.graph.nodes)await this.initModule(node);for(let node of this.graph.nodes)for(let{name,scope}of this.registered.get(node)??[]){if(scope===1)continue;await ModuleContext.run(node,()=>Service.resolve(name))}}async prepare(cls,owner){await this.initModule(owner);let dependencies=Metadata.get(Symbols.services,cls)??[];for(let{name}of dependencies)await ModuleContext.run(owner,()=>Container.resolve(name,cls.name));return ModuleContext.run(owner,()=>Service.make(cls))}moduleInstance(node){let instance=this.instances.get(node);if(!instance)throw new ElysiumError(`Module ${node.id} has not been registered.`);return instance}shutdown(){return this.shutdownPromise??=this.runShutdown(),this.shutdownPromise}async runInit(node){for(let imported of node.imports)await this.initModule(imported);await this.initOwn(node)}initOwn(node){let instance=this.moduleInstance(node),own=this.ownClasses.get(node);if(own)this.wired.push(...wireEventListeners(node,own.owned,own.services));this.initialising.add(node);let result;try{result=ModuleContext.run(node,()=>instance.onInit())}catch(error){throw this.initialising.delete(node),error}let done=()=>{this.initialising.delete(node),this.initialised.add(node),this.initOrder.push(node)};if(typeof result?.then==="function")return Promise.resolve(result).then(done,(error)=>{throw this.initialising.delete(node),error});done();return}initSync(node){if(this.initialised.has(node)||this.initialising.has(node))return!0;let failure=this.failures.get(node);if(failure)throw failure.error;if(this.inFlight.has(node))return!1;for(let imported of node.imports)if(!this.initSync(imported))return!1;let pending;try{pending=this.initOwn(node)}catch(error){throw this.failures.set(node,{error}),error}if(!pending)return!0;return pending.catch(()=>{}),this.inFlight.set(node,pending),!1}async runShutdown(){for(let{event,wrapper}of this.wired.splice(0))EventBus.instance.removeListener(event,wrapper);let targets=[...Container.constructed().map(({name,instance,owner})=>({label:`service ${name}`,instance,owner})).reverse(),...[...this.initOrder].reverse().map((node)=>({label:`module ${node.id}`,instance:this.instances.get(node),owner:node}))];for(let{label,instance,owner}of targets){let onDestroy=instance?.onDestroy;if(typeof onDestroy!=="function")continue;try{let call=()=>onDestroy.call(instance);await(owner?ModuleContext.run(owner,call):call())}catch(error){console.error(`Error while destroying ${label}:`,error)}}}async resolveNodeConfig(node){let selected=selectConfig(node),input;if(selected){input=await evaluate(selected.config);let tied=node.configSources.filter((s)=>s.depth===selected.depth);if(tied.length>1&&tied.some((s)=>typeof s.config==="function")){for(let other of tied.filter((s)=>s!==selected))if(!isEqual2(input,await evaluate(other.config)))throw new ConflictingModuleConfigError(node.id,[selected.site,other.site])}}return isConfigurable(node.moduleClass)?node.moduleClass.resolveConfig(input):void 0}buildSkippedLookup(){let owners=new Map;for(let cls of this.graph.skipped){let{services}=getModuleProps(cls);if(!Array.isArray(services))continue;for(let def of services)owners.set(nameOf2(def),cls.name)}let{environment}=this.graph;return(name)=>{let module=owners.get(name);return module===void 0?null:{module,environment}}}}var executeMiddlewareChain=(middlewares,context,method,node=null)=>{let chain=async()=>{for(let middleware of middlewares){let result=await middleware[method](context);if(result!==void 0)return result}};return Application.context.run(new Map([["tenant",context.tenant],["http:context",context]]),()=>node?ModuleContext.run(node,chain):chain())},makeMiddlewares=(middlewares,node)=>middlewares.map((middleware)=>node?ModuleContext.run(node,()=>Service.make(middleware)):Service.make(middleware)),applyMiddlewares=(middlewares,plugin,node)=>{let mi=makeMiddlewares(middlewares,node);plugin.onRequest((c)=>executeMiddlewareChain(mi,c,"onRequest",node)),plugin.onTransform((c)=>executeMiddlewareChain(mi,c,"onTransform",node)),plugin.onBeforeHandle((c)=>executeMiddlewareChain(mi,c,"onBeforeHandle",node)),plugin.onAfterHandle((c)=>executeMiddlewareChain(mi,c,"onAfterHandle",node)),plugin.onAfterResponse((c)=>executeMiddlewareChain(mi,c,"onAfterResponse",node))};class Middleware{static register(...middlewares){return function(target,propertyKey,descriptor){if(propertyKey===void 0&&descriptor===void 0){let m=Metadata.get(Symbols.middlewares,target)??[];m.push(...middlewares),Metadata.define(Symbols.middlewares,m,target)}else{let m=Metadata.get(Symbols.middlewares,target,propertyKey)??[];m.push(...middlewares),Metadata.define(Symbols.middlewares,m,target,propertyKey)}}}static guards(guards){class GuardedMiddleware extends this{}return Metadata.define(Symbols.middlewareGuards,guards,GuardedMiddleware),GuardedMiddleware}get logger(){return Service.get("logger").child({middleware:this.constructor.name})}get guards(){return Metadata.get(Symbols.middlewareGuards,this.constructor)??[]}onRequest(_ctx){}onTransform(_ctx){}onAfterHandle(_ctx){}onBeforeHandle(_ctx){}onAfterResponse(_ctx){}}var resetContainer=()=>{Container.reset(),Container.setOverrides(null),Container.setVisibility(null),Container.setModuleInitHook(null),Container.setSkippedLookup(null)};async function createTestingApp(options){let{modules,overrides={},environment="test",boot="lazy"}=options;resetContainer();try{let graph=buildModuleGraph(modules,environment),kernel=new Kernel(graph);Container.setOverrides(Object.keys(overrides));for(let[name,value]of Object.entries(overrides))Service.instance(name,value);if(await kernel.register(),boot==="eager")await kernel.bootEager();return{kernel,get:async(name,from)=>{let resolve=()=>Service.resolve(name);if(!from)return resolve();let node=graph.node(from);if(!node)throw new ElysiumError(`Module ${from.name} is not part of the test graph.`);return ModuleContext.run(node,resolve)},shutdown:async()=>{await kernel.shutdown(),resetContainer()}}}catch(error){throw resetContainer(),error}}var fallbackContext=new AsyncLocalStorage2;class Application extends InteractsWithConsole{#elysia;#debug;#kernel;#onSignal;_appContextStorage=new AsyncLocalStorage2;static get instance(){return Service.get("elysium.app")}static get context(){return Application.instance?._appContextStorage??fallbackContext}static register(props={}){return function(target){let parentMetadata,proto=Object.getPrototypeOf(target);while(proto&&proto!==Object.prototype){if(parentMetadata=Metadata.getOwn(Symbols.app,proto),parentMetadata)break;proto=Object.getPrototypeOf(proto)}let ownMetadata=Metadata.getOwn(Symbols.app,target),modules=[...parentMetadata?.modules??[],...ownMetadata?.modules??[],...props.modules??[]],withoutModules=({modules:_modules,...rest})=>rest;props={...deepMerge(withoutModules(props),withoutModules(ownMetadata??{}),withoutModules(parentMetadata??{}),{openapi:!1}),modules},Metadata.define(Symbols.app,props,target)}}constructor(){super();let{env,debug}=Metadata.get(Symbols.app,this.constructor)??{};if(this.#debug=debug??!1,env){let rawEnv=Value.Convert(env,Value.Clone(Bun.env));if(this.#debug&&!Value.Check(env,rawEnv)){let error=Value.Errors(env,rawEnv).First();this.trace(error,"Invalid environment configuration")}initEnv(Value.Clean(env,rawEnv))}else initEnv(Bun.env);Service.remove("elysium.app"),Service.instance("elysium.app",this)}static start(){return new this().run()}static testing(options){return createTestingApp(options)}get kernel(){if(!this.#kernel)throw new ElysiumError("The application has not been started yet.");return this.#kernel}get elysia(){return this.#elysia}get isDebug(){return this.#debug}set isDebug(debug){this.#debug=debug}async onError(_error){return!0}async onStart(_elysia){}async onStop(_elysia){}async run(argv=process.argv.slice(2)){this.installSignalHandlers();try{let{modules}=this.props();return this.#kernel=new Kernel(buildModuleGraph(modules??[])),await this.dispatch(argv)}catch(error){return this.fail(error)}}props(){return Metadata.get(Symbols.app,this.constructor)??{}}async dispatch([action,...rest]){switch(action){case void 0:return this.commandDescribe(),this.exit(0);case"serve":return this.commandServe();case"list":return this.write(this.bold("Available commands:")),this.commandList(),this.exit(0);case"help":return this.commandHelp(rest[0]);default:return this.commandExec(action,rest)}}async fail(error){let message=error instanceof Error?error.message:String(error);if(this.error(message),!(error instanceof ElysiumError)&&this.isDebug)this.trace(error);return Event.emit("elysium:error",error),await this.#kernel?.shutdown(),this.exit(1)}exit(code){return this.removeSignalHandlers(),process.exit(code)}removeSignalHandlers(){if(!this.#onSignal)return;process.off("SIGINT",this.#onSignal),process.off("SIGTERM",this.#onSignal),this.#onSignal=void 0}installSignalHandlers(){this.removeSignalHandlers();let onSignal=async()=>{this.removeSignalHandlers();let code=0;try{await this.#elysia?.stop()}catch(error){code=1,this.error(error instanceof Error?error.message:String(error))}finally{try{await this.#kernel?.shutdown()}catch(error){code=1,this.error(error instanceof Error?error.message:String(error))}this.exit(code)}};this.#onSignal=onSignal,process.on("SIGINT",onSignal),process.on("SIGTERM",onSignal)}getCommands(){return this.kernel.graph.collect("commands")}findCommand(name){return this.getCommands().find(({item})=>getCommandName(item)===name)}async prepareCommand({item,module},forHelp=!1){let eager=item.boot&&!forHelp;if(await this.bootKernel(eager),!eager)return this.kernel.prepare(item,module);return ModuleContext.run(module,()=>Service.make(item))}async bootKernel(eager){if(await this.kernel.register(),eager)await this.kernel.bootEager();Event.emit("elysium:app:launched",this,this)}async commandHelp(name){let found=name?this.findCommand(name):void 0;if(!found)return this.error(name?`Command <${name}> not found.`:"No command provided. Usage: styx help <command>"),this.commandList(),this.exit(1);let command=await this.prepareCommand(found,!0);return this.write(await ModuleContext.run(found.module,()=>command.help())),await this.kernel.shutdown(),this.exit(0)}async commandExec(name,argv){let found=this.findCommand(name);if(!found)return this.error(`Command ${name} not found`),this.commandList(),this.exit(1);let command=await this.prepareCommand(found);return await ModuleContext.run(found.module,async()=>{if(await command.init(...argv))await command.run();else this.write(await command.help())}),await this.kernel.shutdown(),this.exit(0)}async commandServe(){let{server,openapi,plugins}=this.props();if(await this.bootKernel(!0),this.#elysia=new Elysia(server).onRequest((c)=>{if(this.isDebug)console.log(c.request.method,c.request.url)}),Event.emit("elysium:server:before-init",this,this),this.#elysia.resolve({as:"global"},({request})=>({tenant:request.headers.get("x-tenant-id")??"public"})),openapi)this.#elysia.use(openapiPlugin(openapi));for(let plugin of plugins??[])this.#elysia.use(await plugin(this));this.setupServerHooks();let middlewares=Metadata.get(Symbols.middlewares,this.constructor)??[];applyMiddlewares(middlewares,this.#elysia,null),await this.serveModules(),Event.emit("elysium:server:after-init",this,this),this.#elysia.listen(server?.port??(parseInt(process.env.PORT,10)||3000))}async serveModules(){let{nodes}=this.kernel.graph;for(let node of nodes){let instance=this.kernel.moduleInstance(node);await ModuleContext.run(node,()=>instance.onServe(this.#elysia))}for(let node of nodes)if(node.props.controllers?.length)this.debug(`Mounting module ${node.id}...`),this.#elysia.use(await this.mountModule(node))}async mountModule(node){let app=new Elysia({name:node.id,prefix:node.props.prefix}),middlewares=Metadata.get(Symbols.middlewares,node.moduleClass)??[];applyMiddlewares(middlewares,app,node);for(let controller of node.props.controllers??[]){let plugin=Metadata.get(Symbols.elysiaPlugin,controller);if(!plugin)throw new InvalidControllerError(controller.name,node.id);app.use(await plugin(node))}return app}setupServerHooks(){this.#elysia.onError({as:"global"},(e)=>this.handleServerError(e)).onStart(async(elysia)=>{await this.onStart(elysia),Event.emit("elysium:server:start",elysia,this)}).onStop(async(elysia)=>{await this.onStop(elysia),Event.emit("elysium:server:stop",elysia,this),this.removeSignalHandlers(),await this.#kernel?.shutdown(),this._appContextStorage.disable()})}async handleServerError(e){if(!await this.onError(e))return;if(Event.emit("elysium:error",e),this.trace({name:"HttpError",message:"Unexpected error while handling HTTP request",stack:e.error.stack,...e.error}),typeof e.code==="number")return e.error.response;let data={};switch(e.code){case"VALIDATION":data={scope:e.error.type,code:e.error.message};break;case"INTERNAL_SERVER_ERROR":data={message:e.error.message};break}return{type:e.code,data}}commandDescribe(){this.section(`${this.bold("Usage:")} ${this.format("styx",5)} <command> [options]`),this.section(this.bold("Core Commands:")),this.newLine(),this.commandDescription("serve","","Starts the server."),this.commandDescription("help","<command>","Displays help for a command."),this.commandDescription("list","","List all available application commands."),this.section(this.bold("Application Commands:")),this.commandList()}commandList(){let groups=this.getCommands().reduce((acc,{item})=>{let groupKey=first(getCommandName(item).split(":"));return acc.set(groupKey,[...acc.get(groupKey)??[],item]),acc},new Map);for(let[group,groupCommands]of groups){this.section(this.format(group,4));for(let commandClass of groupCommands)this.commandDescription(getCommandName(commandClass),"",commandClass.description)}}commandDescription(command,args,description,width=InteractsWithConsole.SPACE_WIDTH){args=args.length===0?" ":` ${args} `,this.write(` ${this.format(command,6)}${args}${"\u2219".repeat(width-command.length-args.length)} ${description}`)}}class ConsoleLogger{context;constructor(context={}){this.context=context}trace(msg,ctx){console.debug(`[TRACE] ${msg}`,{...this.context,...ctx})}debug(msg,ctx){console.debug(`[DEBUG] ${msg}`,{...this.context,...ctx})}info(msg,ctx){console.info(`[INFO] ${msg}`,{...this.context,...ctx})}warn(msg,ctx){console.warn(`[WARN] ${msg}`,{...this.context,...ctx})}error(msg,ctx){console.error(`[ERROR] ${msg}`,{...this.context,...ctx})}fatal(msg,ctx){console.error(`[FATAL] ${msg}`,{...this.context,...ctx})}child(ctx){return new ConsoleLogger({...this.context,...ctx})}}import{Elysia as Elysia2,sse as esse,t}from"elysia";import{assign,isEmpty as isEmpty2,objectify}from"radash";async function*iterateInModule(node,generator){let finished=!1;try{while(!0){let step=await ModuleContext.run(node,()=>generator.next());if(step.done){finished=!0;return}yield step.value}}catch(error){throw finished=!0,error}finally{if(!finished)await ModuleContext.run(node,()=>generator.return(void 0))}}var registerHttpRequestHandler=(props)=>{let body=Metadata.get("http:body",props.target,props.propertyKey),params=Metadata.get("http:params",props.target,props.propertyKey)??[],query=Metadata.get("http:query",props.target,props.propertyKey),rawContext=Metadata.get("http:rawContext",props.target,props.propertyKey),customDecorators=Metadata.get("http:customDecorators",props.target,props.propertyKey)??[],middlewares=Metadata.get(Symbols.middlewares,props.target,props.propertyKey)??[],services=Metadata.get(Symbols.services,props.target,props.propertyKey)??[],{path,method,handler,target,response,operationId,description,transactional}=props,metadata=Metadata.get(Symbols.http,target.constructor)??[];metadata.push({path,method,body,params,query,customDecorators,rawContext,handler,middlewares,response,operationId,description,services,transactional}),Metadata.define(Symbols.http,metadata,target.constructor)},HttpControllerScope;((HttpControllerScope)=>{HttpControllerScope[HttpControllerScope.SERVER=0]="SERVER";HttpControllerScope[HttpControllerScope.REQUEST=1]="REQUEST"})(HttpControllerScope||={});var Http;((Http)=>(Http.controller=(props={path:"/"})=>function(target){async function handleHttp(node){await nextTick(),props=assign({path:"/",scope:0,tags:[]},props);let app=new Elysia2({prefix:props.path,name:`${node.id}:${target.name}`}),getController=(()=>{if(props.scope===0){let controller=ModuleContext.run(node,()=>Service.make(target));return()=>()=>controller}else if(props.scope===1)return()=>()=>ModuleContext.run(node,()=>Service.make(target));else throw Error(`Invalid scope for controller: ${target.name}`)})();app.resolve({as:"local"},()=>({controller:getController()}));let onRequest=Metadata.get("http:onRequest",target)??{};if(onRequest.handler)app.onRequest((c)=>{let controller=getController()();return ModuleContext.run(node,()=>onRequest.handler.call(controller,c))});let middlewares=Metadata.get(Symbols.middlewares,target)??[];applyMiddlewares(middlewares,app,node);let metadata=Metadata.get(Symbols.http,target)??[];for(let route of metadata){let getParameters=async(c)=>{let parameters=[];if(route.rawContext)parameters[route.rawContext.index]=c;if(route.body)parameters[route.body.index]=c.body;if(route.query)parameters[route.query.index]=c.query;if(!isEmpty2(route.params))for(let param of route.params)parameters[param.index]=c.params[param.slug];if(!isEmpty2(route.customDecorators))for(let customDecorator of route.customDecorators)parameters[customDecorator.index]=await ModuleContext.run(node,()=>customDecorator.handler(c));if(!isEmpty2(route.services))for(let service of route.services)parameters[service.index]=ModuleContext.run(node,()=>Service.get(service.name));return parameters},isGenerator=route.handler.constructor.name.includes("GeneratorFunction"),isSSE=route.method==="elysium:SSE",isTransactional=route.transactional??props.transactional??!1,isValidator=route.body&&route.body.schema===void 0&&route.body.validator!==void 0;route.method=isSSE?"GET":route.method;let getHandler=()=>{let initTransactedHandler=(c)=>{if(isTransactional)return function(...args){return Service.get("db.connection.default").transaction((tx)=>Application.context.run(new Map([["tenant",c.tenant],["http:context",c],["db:tx",tx]]),()=>ModuleContext.run(node,()=>{try{return route.handler.call(c.controller(),...args)}catch(e){throw tx.rollback(),e}})))};return function(...args){return Application.context.run(new Map([["tenant",c.tenant],["http:context",c]]),()=>ModuleContext.run(node,()=>route.handler.call(c.controller(),...args)))}};if(isGenerator)return async function*(c){let handler=initTransactedHandler(c),params=await getParameters(c);try{yield*iterateInModule(node,handler(...params))}catch(error){yield error}};if(isSSE)return async function*(c){let handler=initTransactedHandler(c);c.set.headers["content-type"]="text/event-stream",c.set.headers["cache-control"]="no-cache",c.set.headers.connection="keep-alive";while(!0)yield esse(await handler(...await getParameters(c))),await Bun.sleep(10)};if(isValidator)return async function(c){if(await ModuleContext.run(node,()=>route.body.validator.validate(c)))return await initTransactedHandler(c)(...await getParameters(c));throw c.status(400,"Invalid request body")};return async function(c){return await initTransactedHandler(c)(...await getParameters(c))}},params=objectify(route.params??[],(p)=>p.slug,(p)=>p.schema),mi=makeMiddlewares(route.middlewares,node);app.route(route.method,route.path,getHandler(),{config:{},async transform(c){if(isValidator&&!await ModuleContext.run(node,()=>route.body.validator.authorize(c)))throw c.status(401,"Unauthorized");return executeMiddlewareChain(mi,c,"onTransform",node)},beforeHandle(c){return executeMiddlewareChain(mi,c,"onBeforeHandle",node)},afterHandle(c){return executeMiddlewareChain(mi,c,"onAfterHandle",node)},afterResponse(c){return executeMiddlewareChain(mi,c,"onAfterResponse",node)},detail:{tags:props.tags,description:route.description,operationId:route.operationId},body:isValidator?route.body?.validator?.schema:route.body?.schema,response:route.response,params:isEmpty2(params)?void 0:t.Object(params)})}return app}Metadata.define(Symbols.elysiaPlugin,handleHttp,target)},Http.sse=(props={})=>{let{path="/",...rest}=props;return Http.custom({method:"elysium:SSE",path,...rest})},Http.get=(props={})=>{let{path="/",...rest}=props;return Http.custom({method:"GET",path,...rest})},Http.post=(props={})=>{let{path="/",...rest}=props;return Http.custom({method:"POST",path,...rest})},Http.put=(props={})=>{let{path="/",...rest}=props;return Http.custom({method:"PUT",path,...rest})},Http.del=(props={})=>{let{path="/",...rest}=props;return Http.custom({method:"DELETE",path,...rest})},Http.patch=(props={})=>{let{path="/",...rest}=props;return Http.custom({method:"PATCH",path,...rest})},Http.head=(props={})=>{let{path="/",...rest}=props??{};return Http.custom({method:"HEAD",path,...rest})},Http.options=(props={})=>{let{path="/",...rest}=props;return Http.custom({method:"OPTIONS",path,...rest})},Http.trace=(props={})=>{let{path="/",...rest}=props;return Http.custom({method:"TRACE",path,...rest})},Http.custom=({path="/",...rest})=>function(target,propertyKey,descriptor){process.nextTick(()=>{registerHttpRequestHandler({path,handler:descriptor.value,target,propertyKey,...rest})})},Http.onRequest=()=>function(target,_,descriptor){let metadata=Metadata.get("http:onRequest",target.constructor)??{};metadata.handler=descriptor.value,Metadata.define("http:onRequest",metadata,target.constructor)},Http.context=()=>function(target,propertyKey,parameterIndex){Metadata.define("http:rawContext",{index:parameterIndex},target,propertyKey)},Http.param=(slug,schema)=>function(target,propertyKey,parameterIndex){let params=Metadata.get("http:params",target,propertyKey)??[];params.push({slug,schema:schema??t.String(),index:parameterIndex}),Metadata.define("http:params",params,target,propertyKey)},Http.query=(schema)=>function(target,propertyKey,parameterIndex){Metadata.define("http:query",{schema,index:parameterIndex},target,propertyKey)},Http.body=(schema)=>function(target,propertyKey,parameterIndex){if(typeof schema==="function")Metadata.define("http:body",{validator:schema,index:parameterIndex},target,propertyKey);else Metadata.define("http:body",{schema,index:parameterIndex},target,propertyKey)},Http.decorate=(handler)=>()=>function(target,propertyKey,parameterIndex){let decorators=Metadata.get("http:customDecorators",target,propertyKey)??[];decorators.push({handler,index:parameterIndex}),Metadata.define("http:customDecorators",decorators,target,propertyKey)}))(Http||={});class RequestLoggerMiddleware extends Middleware{async onRequest(ctx){ctx.__requestStartTime=Date.now(),this.logger.info("Incoming request",{method:ctx.request.method,path:new URL(ctx.request.url).pathname})}async onAfterResponse(ctx){let duration=Date.now()-(ctx.__requestStartTime??0);this.logger.info("Request completed",{method:ctx.request.method,path:new URL(ctx.request.url).pathname,duration:`${duration}ms`})}}var DEFAULT="default";function needsDefaultAlias(module,config){if(!Object.hasOwn(config.connections,config.default))throw new InvalidModuleConfigError(module,"default",`"${config.default}" is not one of the declared connections`);if(config.default===DEFAULT)return!1;if(Object.hasOwn(config.connections,DEFAULT))throw new InvalidModuleConfigError(module,"connections",`a connection named "default" conflicts with the default alias to "${config.default}"`);return!0}async function runAll(tasks){let errors=(await Promise.allSettled(tasks.map(async(task)=>task()))).flatMap((r)=>r.status==="rejected"?[r.reason]:[]);if(errors.length===1)throw errors[0];if(errors.length>1)throw AggregateError(errors,"Several connections failed to close.")}import{omit as omit2}from"radash";var Redis;((Redis)=>(Redis.getConnectionName=(name)=>`redis.connection.${name}`,Redis.getConnection=(name="default")=>{let client=Service.get(Redis.getConnectionName(name));if(client===null)throw new ServiceNotFoundError(Redis.getConnectionName(name));return client},Redis.connectionExists=(name)=>Service.exists(Redis.getConnectionName(name)),Redis.getDefaultConnection=()=>Redis.getConnection("default")))(Redis||={});class RedisModule extends Module.configurable({defaults:{default:"default"},required:["connections"]}){async onDestroy(){let names=Object.keys(this.config.connections).map(Redis.getConnectionName),clients=Container.constructed().filter(({name})=>names.includes(name));await runAll(clients.map(({instance})=>()=>instance.close()))}}RedisModule=__legacyDecorateClassTS([Module.register({services:(config)=>{let alias=needsDefaultAlias("RedisModule",config);return[...Object.entries(config.connections).map(([name,props])=>({name:Redis.getConnectionName(name),global:!0,factory:()=>new Bun.RedisClient(props.url,omit2(props,["url"]))})),...alias?[{name:Redis.getConnectionName("default"),global:!0,factory:(_config,get)=>get(Redis.getConnectionName(config.default))}]:[]]}})],RedisModule);import{Elysia as Elysia3}from"elysia";import{omit as omit3}from"radash";import Wampy from"wampy";class WampWebsocket extends WebSocket{protocols;constructor(url,protocols){super(url,protocols);this.protocols=protocols}get protocol(){return this.protocols?.[0]??"wamp.2.json"}}var Wamp;((Wamp2)=>(Wamp2.getConnectionName=(name)=>`wamp.connection.${name}`,Wamp2.getConnection=(name="default")=>{let client=Service.get(Wamp2.getConnectionName(name));if(client===null)throw new ServiceNotFoundError(Wamp2.getConnectionName(name));return client},Wamp2.connectionExists=(name)=>Service.exists(Wamp2.getConnectionName(name)),Wamp2.getDefaultConnection=()=>Wamp2.getConnection("default"),Wamp2.createClient=(name,config)=>{let client=new Wampy(config.url,{ws:WampWebsocket,...omit3(config,["url","onOpen"]),onError(error){config.onError?.(error),Event.emit("elysium:error",Error(`An error occurred in WAMP connection ${name}`))}}),maxRetries=config.maxRetries??5,retryInterval=config.retryInterval??5000,retryCount=0,closed=!1,timer,disconnect=client.disconnect.bind(client);client.disconnect=()=>(closed=!0,clearTimeout(timer),disconnect());let tryConnect=async()=>{if(closed)return;try{await client.connect()}catch(e){if(console.error(`Failed to connect to WAMP server (attempt ${retryCount+1}/${maxRetries}):`,e),retryCount<maxRetries)retryCount++,timer=setTimeout(tryConnect,retryInterval);else console.error(`Max retry attempts (${maxRetries}) reached. Giving up.`),Event.emit("elysium:error",Error(`Failed to establish WAMP connection ${name} after ${maxRetries} attempts`));return}if(closed)return;try{await config.onOpen?.()}catch(error){Event.emit("elysium:error",error)}};return tryConnect(),client},Wamp2.controller=(options)=>function(target){async function handleWamp(node){await nextTick();let controller=ModuleContext.run(node,()=>Service.make(target)),inModule=(handler)=>(...args)=>ModuleContext.run(node,()=>handler.apply(controller,args)),metadata=Metadata.get(Symbols.wamp,target)??{},w=Wamp.getConnection(options.connection??"default"),app=new Elysia3({name:`${node.id}:${target.name}`});return app.onStart(async(_)=>{let registrations=metadata.registrations??[],subscriptions=metadata.subscriptions??[];for(let registration of registrations)w.register(registration.topic,inModule(registration.handler),registration.options).then((r)=>{console.log(`Registered ${registration.topic} with id ${r.registrationId}`)});for(let subscription of subscriptions)w.subscribe(subscription.topic,inModule(subscription.handler),subscription.options).then((s)=>{console.log(`Subscribed ${subscription.topic} with id ${s.subscriptionId}`)})}),app}Metadata.define(Symbols.elysiaPlugin,handleWamp,target)},Wamp2.register=(topic,options)=>function(target,_propertyKey,descriptor){let metadata=Metadata.get(Symbols.wamp,target.constructor)??{};metadata.registrations??=[],metadata.registrations.push({topic,options,handler:descriptor.value}),Metadata.define(Symbols.wamp,metadata,target.constructor)},Wamp2.subscribe=(topic,options)=>function(target,_propertyKey,descriptor){let metadata=Metadata.get(Symbols.wamp,target.constructor)??{};metadata.subscriptions??=[],metadata.subscriptions.push({topic,options,handler:descriptor.value}),Metadata.define(Symbols.wamp,metadata,target.constructor)}))(Wamp||={});class WampModule extends Module.configurable({defaults:{default:"default"},required:["connections"]}){async onDestroy(){let names=Object.keys(this.config.connections).map(Wamp.getConnectionName),clients=Container.constructed().filter(({name})=>names.includes(name));await runAll(clients.map(({instance})=>()=>instance.disconnect()))}}WampModule=__legacyDecorateClassTS([Module.register({services:(config)=>{let alias=needsDefaultAlias("WampModule",config);return[...Object.entries(config.connections).map(([name,props])=>({name:Wamp.getConnectionName(name),global:!0,factory:()=>Wamp.createClient(name,props)})),...alias?[{name:Wamp.getConnectionName("default"),global:!0,factory:(_config,get)=>get(Wamp.getConnectionName(config.default))}]:[]]}})],WampModule);import{Elysia as Elysia4}from"elysia";var Websocket;((Websocket)=>(Websocket.controller=(props)=>function(target){async function handleWebsocket(node){await nextTick();let controller=ModuleContext.run(node,()=>Service.make(target)),metadata=Metadata.get(Symbols.websocket,target)??{},inModule=(handler)=>(...args)=>ModuleContext.run(node,()=>handler?.apply(controller,args)),open=metadata.open&&inModule(metadata.open),close=metadata.close&&inModule(metadata.close),message=metadata.message&&inModule(metadata.message),drain=metadata.drain&&inModule(metadata.drain),error=metadata.error?inModule(metadata.error):(e)=>Event.emit("elysium:error",e.error),app=new Elysia4({name:`${node.id}:${target.name}`});return app.ws(props.path,{open,close,message,drain,error,body:metadata.body,...props.options}),app}Metadata.define(Symbols.elysiaPlugin,handleWebsocket,target)},Websocket.onOpen=()=>function(target,_,descriptor){let metadata=Metadata.get(Symbols.websocket,target.constructor)??{};metadata.open=descriptor.value,Metadata.define(Symbols.websocket,metadata,target.constructor)},Websocket.onClose=()=>function(target,_,descriptor){let metadata=Metadata.get(Symbols.websocket,target.constructor)??{};metadata.close=descriptor.value,Metadata.define(Symbols.websocket,metadata,target.constructor)},Websocket.onMessage=(schema)=>function(target,_,descriptor){let metadata=Metadata.get(Symbols.websocket,target.constructor)??{};metadata.message=descriptor.value,metadata.body=schema,Metadata.define(Symbols.websocket,metadata,target.constructor)},Websocket.onDrain=()=>function(target,_,descriptor){let metadata=Metadata.get(Symbols.websocket,target.constructor)??{};metadata.drain=descriptor.value,Metadata.define(Symbols.websocket,metadata,target.constructor)},Websocket.onError=()=>function(target,_,descriptor){let metadata=Metadata.get(Symbols.websocket,target.constructor)??{};metadata.error=descriptor.value,Metadata.define(Symbols.websocket,metadata,target.constructor)}))(Websocket||={});export{Application,CircularDependencyError,Command,CommandArgumentType,ConflictingModuleConfigError,ConsoleFormat,ConsoleLogger,DuplicateCommandError,DuplicateGlobalServiceError,DuplicateModuleNameError,ElysiumError,Env,EnvironmentSkippedModuleError,Event,EventListenerNotSupportedError,Http,HttpControllerScope,InteractsWithConsole,InvalidModuleConfigError,InvalidModuleError,KERNEL_SERVICE,Kernel,Metadata,Middleware,MissingModuleConfigError,Module,ModuleContext,ModuleCycleError,Redis,RedisModule,RequestLoggerMiddleware,Service,ServiceNotFoundError,ServiceNotReadyError,ServiceNotVisibleError,ServiceScope,Wamp,WampModule,Websocket,needsDefaultAlias,runAll,t2 as t};
19
+ `);return{valid:missing.length===0&&invalid.length===0,missing}}processArgs(argv){let processed={},commandArgs=this.getArguments();for(let i=0;i<argv.length;i++){let arg=argv[i];if(arg.startsWith("--")){let argName=arg.slice(2);if(i+1<argv.length&&!argv[i+1].startsWith("--"))processed[argName]=argv[i+1],i++;else processed[argName]=!0}}for(let arg of commandArgs){if(processed[arg.name]===void 0&&arg.default!==void 0){processed[arg.name]=arg.default;continue}if(processed[arg.name]===void 0)continue;if(typeof processed[arg.name]==="string")switch(arg.type||this.inferType(arg.default)){case"number":processed[arg.name]=Number(processed[arg.name]);break;case"boolean":let value=processed[arg.name].toLowerCase();processed[arg.name]=value==="true"||value==="1"||value==="yes";break;case"enum":if(arg.enum&&arg.enum.length>0&&typeof arg.enum[0]==="number")processed[arg.name]=Number(processed[arg.name]);break;case"array":let arrayValues=processed[arg.name].split(",").map((item)=>item.trim());if(arg.arrayType)processed[arg.name]=arrayValues.map((item)=>{switch(arg.arrayType){case"number":return Number(item);case"boolean":return item.toLowerCase()==="true"||item.toLowerCase()==="1"||item.toLowerCase()==="yes";case"enum":return item;default:return item}});else processed[arg.name]=arrayValues;break;case"string":default:break}}return processed}progress(total,title="Processing"){return new CommandProgressBar(total,title)}spinner(title="Processing",frames,frameDelay){return new CommandSpinner(title,frames,frameDelay)}inferType(defaultValue){if(defaultValue===void 0)return"string";switch(typeof defaultValue){case"number":return"number";case"boolean":return"boolean";default:return isArray(defaultValue)?"array":"string"}}}class CommandProgressBar extends InteractsWithConsole{#active=!1;#total=0;#current=0;#title="";#startTime=0;constructor(total=0,title="Processing"){super();this.#total=total,this.#title=title,this.#active=!0,this.#current=0,this.#startTime=Date.now(),this.update(0)}update(increment=1,newTitle){if(!this.#active)return;if(this.#current+=increment,newTitle)this.#title=newTitle;let percentage=Math.min(100,Math.floor(this.#current/this.#total*100)),elapsedSec=(Date.now()-this.#startTime)/1000,estimatedTotalSec=0,estimatedRemainingSec=0,timeInfo="";if(this.#current>0)estimatedTotalSec=elapsedSec/this.#current*this.#total,estimatedRemainingSec=Math.max(0,estimatedTotalSec-elapsedSec),timeInfo=` | ${this.formatTime(elapsedSec)} elapsed | ${this.formatTime(estimatedRemainingSec)} remaining`;let barWidth=30,completedWidth=Math.floor(percentage/100*barWidth),remainingWidth=barWidth-completedWidth,bar="["+"=".repeat(completedWidth)+(remainingWidth>0?">":"")+" ".repeat(Math.max(0,remainingWidth-(remainingWidth>0?1:0)))+"]",progressLine=`${this.#title}: ${bar} ${percentage}% (${this.#current}/${this.#total})${timeInfo}`;if(this.clearLine(),this.write(progressLine,!1),this.#current>=this.#total)this.complete()}complete(message){if(!this.#active)return;this.#current=this.#total;let elapsedSec=(Date.now()-this.#startTime)/1000;if(this.clearLine(),message)this.write(`${message} (completed in ${this.formatTime(elapsedSec)})`);else this.write(`${this.#title} completed in ${this.formatTime(elapsedSec)}`);this.#active=!1}pause(message){if(!this.#active)return;this.newLine(),this.write(message),this.update(0)}isActive(){return this.#active}getCurrent(){return this.#current}getTotal(){return this.#total}getPercentage(){return Math.min(100,Math.floor(this.#current/this.#total*100))}}class CommandSpinner extends InteractsWithConsole{#active=!1;#title="";#startTime=0;#intervalId=null;#currentFrame=0;#frames=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"];#frameDelay=80;constructor(title="Processing",frames,frameDelay){super();if(this.#title=title,frames)this.#frames=frames;if(frameDelay)this.#frameDelay=frameDelay;if(this.#active)return;this.#active=!0,this.#startTime=Date.now(),this.#currentFrame=0,this.#intervalId=setInterval(()=>{this.render()},this.#frameDelay),this.render()}update(newTitle){if(!this.#active)return;this.#title=newTitle}complete(message,success=!0){if(!this.#active)return;if(this.#intervalId)clearInterval(this.#intervalId),this.#intervalId=null;let elapsedSec=(Date.now()-this.#startTime)/1000;this.clearLine();let time=this.formatTime(elapsedSec);if(message)this[success?"success":"error"](`${message} (${time})`);else this[success?"success":"error"](`${this.#title} completed in ${time}`);this.#active=!1}fail(message){this.complete(message,!1)}pause(message){if(!this.#active)return;if(this.#intervalId)clearInterval(this.#intervalId),this.#intervalId=null;this.clearLine(),this.write(message),this.#intervalId=setInterval(()=>{this.render()},this.#frameDelay)}isActive(){return this.#active}getElapsedTime(){return(Date.now()-this.#startTime)/1000}render(){if(!this.#active)return;let elapsedSec=this.getElapsedTime(),frame=this.#frames[this.#currentFrame];this.#currentFrame=(this.#currentFrame+1)%this.#frames.length,this.clearLine(),this.write(`${frame} ${this.#title} (${this.formatTime(elapsedSec)})`,!1)}}var env=new Map,initEnv=(vars)=>{for(let[key,value]of Object.entries(vars))env.set(key,value)},Env;((Env)=>(Env.get=(name,defaultValue)=>env.get(name)??defaultValue,Env.exists=(name)=>env.has(name),Env.clear=()=>{env.clear()}))(Env||={});class ElysiumError extends Error{constructor(message){super(message);this.name=new.target.name}}class ModuleCycleError extends ElysiumError{path;constructor(path){super(`Module import cycle detected: ${path.join(" -> ")}`);this.path=path}}class InvalidModuleError extends ElysiumError{className;constructor(className){super(`${className} is not a valid module. Decorate it with @Module().`);this.className=className}}class DuplicateGlobalServiceError extends ElysiumError{serviceName;modules;constructor(serviceName,modules){super(`Global service "${serviceName}" is declared by both ${modules[0]} and ${modules[1]}.`);this.serviceName=serviceName;this.modules=modules}}class DuplicateCommandError extends ElysiumError{command;modules;constructor(command,modules){super(`Command "${command}" is registered by both ${modules[0]} and ${modules[1]}.`);this.command=command;this.modules=modules}}class ConflictingModuleConfigError extends ElysiumError{module;sites;constructor(module,sites){super(`Module ${module} is configured with conflicting options at ${sites[0]} and ${sites[1]}.`);this.module=module;this.sites=sites}}class MissingModuleConfigError extends ElysiumError{module;keys;constructor(module,keys){super(`Module ${module} is missing required configuration: ${keys.join(", ")}.`);this.module=module;this.keys=keys}}class InvalidModuleConfigError extends ElysiumError{module;key;reason;constructor(module,key,reason){super(`Module ${module} has an invalid "${key}" configuration: ${reason}.`);this.module=module;this.key=key;this.reason=reason}}class ServiceNotVisibleError extends ElysiumError{service;requester;module;owner;constructor(service,requester,module,owner){let where=module?` in module ${module}`:"",hint=owner?` Either import ${owner} or add it to ${owner}'s exports.`:"";super(`Service "${service}" requested by ${requester}${where} is not visible.${hint}`);this.service=service;this.requester=requester;this.module=module;this.owner=owner}}class ServiceNotFoundError extends ElysiumError{service;constructor(service){super(`Service "${service}" was not found.`);this.service=service}}class ServiceNotReadyError extends ElysiumError{service;owner;constructor(service,owner=null){let where=owner?` Its module ${owner} initialises asynchronously.`:"";super(`Service "${service}" is not ready yet.${where} Use "await Service.resolve()" to wait for it. In a command, set "static boot = true" to initialise every module before it runs.`);this.service=service;this.owner=owner}}class CircularDependencyError extends ElysiumError{path;constructor(path){super(`Circular service dependency detected: ${path.join(" -> ")}`);this.path=path}}class DuplicateModuleNameError extends ElysiumError{moduleName;constructor(moduleName){super(`Two different modules are both named "${moduleName}". Module names identify modules in configuration and errors, so they must be unique: rename one of them.`);this.moduleName=moduleName}}class EnvironmentSkippedModuleError extends ElysiumError{service;requester;skippedModule;environment;constructor(service,requester,skippedModule,environment){super(`Service "${service}" requested by ${requester} belongs to module ${skippedModule}, which is skipped in the "${environment}" environment.`);this.service=service;this.requester=requester;this.skippedModule=skippedModule;this.environment=environment}}class InvalidControllerError extends ElysiumError{className;moduleName;constructor(className,moduleName){super(`${className}, listed in the controllers of ${moduleName}, is not a controller. Decorate it with @Http.controller(), @Websocket.controller() or @Wamp.controller().`);this.className=className;this.moduleName=moduleName}}class EventListenerNotSupportedError extends ElysiumError{className;method;moduleName;constructor(className,method,moduleName){super(`${className}.${method} (module ${moduleName}) is an instance event listener, but ${className} is not listed in the services of ${moduleName}. Make the method static or register the class as a service.`);this.className=className;this.method=method;this.moduleName=moduleName}}import{EventEmitter}from"events";import{isString}from"radash";class EventBus extends EventEmitter{static instance=new EventBus;constructor(){super();this.on("error",(error)=>this.emitError(error))}emitError(error){this.emit("elysium:error",{data:error,source:null})}emitEvent(event,data,source=null){this.emit(event,{data,source})}}function getEventListeners(cls){return Metadata.getOwn(Symbols.eventListeners,cls)??[]}var recordListener=(target,method,mode,props)=>{let isStatic=typeof target==="function",owner=isStatic?target:target.constructor;Metadata.define(Symbols.eventListeners,[...getEventListeners(owner),{event:props.event,method,mode,prepend:props.prepend??!1,static:isStatic}],owner)},Event;((Event)=>{Event.emit=(event,data,source=null)=>{EventBus.instance.emitEvent(event,data,source)};function on(event,handler){if(isString(event))EventBus.instance.on(event,handler);else return function(target,propertyKey){recordListener(target,propertyKey,"on",event)}}Event.on=on;function once(event,handler){if(isString(event))EventBus.instance.once(event,handler);else return function(target,propertyKey){recordListener(target,propertyKey,"once",event)}}Event.once=once,Event.off=(event)=>{EventBus.instance.removeAllListeners(event)},Event.clear=()=>{EventBus.instance.removeAllListeners()},Event.listen=(options)=>{if(options.mode==="on")return on(options);else if(options.mode==="once")return once(options);throw new ElysiumError("Unknown mode provided to @listen. Use either 'on' or 'once'.")}})(Event||={});import{isEqual as isEqual2}from"radash";import{AsyncLocalStorage}from"async_hooks";var storage=new AsyncLocalStorage,ModuleContext;((ModuleContext)=>{function run(node,fn){return storage.run(node,fn)}ModuleContext.run=run;function current(){return storage.getStore()}ModuleContext.current=current})(ModuleContext||={});import{isString as isString2}from"radash";var ServiceScope;((ServiceScope)=>{ServiceScope[ServiceScope.SINGLETON=0]="SINGLETON";ServiceScope[ServiceScope.FACTORY=1]="FACTORY"})(ServiceScope||={});var isClass=(value)=>typeof value==="function"&&value.prototype&&value.prototype.constructor===value,nameOf=(service)=>isString2(service)?service:service.name,Service;((Service)=>{Service.register=(props)=>function(target){Metadata.define(Symbols.serviceOptions,props??{},target)},Service.inject=(service)=>function(target,propertyKey,parameterIndex){let services=Metadata.get(Symbols.services,target,propertyKey)??[],types=Metadata.get("design:paramtypes",target,propertyKey)??[],name=isString2(service)?service:service?.name??types[parameterIndex].name;services.push({name,index:parameterIndex}),Metadata.define(Symbols.services,services,target,propertyKey)},Service.get=(service)=>Container.get(nameOf(service)),Service.resolve=(service)=>Container.resolve(nameOf(service),"Service.resolve"),Service.registerLazy=(name,resolver)=>{addGlobal(name,0,resolver)},Service.make=(service)=>Container.make(service),Service.bind=(service,name)=>{let s=Service.make(service);return Service.instance(name??service.name,s)};function factory(serviceOrName,nameOrFactory){let serviceName=isString2(serviceOrName)?serviceOrName:nameOrFactory??serviceOrName.name,create=()=>isClass(serviceOrName)?Service.make(serviceOrName):nameOrFactory();return addGlobal(serviceName,1,create),create()}Service.factory=factory,Service.instance=(service,instance)=>(addGlobal(nameOf(service),0,()=>instance,{instance}),instance),Service.remove=(service)=>{Container.removeGlobal(nameOf(service))},Service.exists=(service)=>Container.hasGlobal(nameOf(service)),Service.isConstructed=(name)=>Container.constructed().some((entry)=>entry.name===name),Service.clear=()=>{Container.clearGlobal()},Service.keys=(pattern)=>{let regexPattern="^"+pattern.replace(/[-[\]{}()+.,\\^$|#\s]/g,"\\$&").replace(/\*/g,".*").replace(/\?/g,".")+"$",regex=new RegExp(regexPattern);return Container.globalNames().filter((key)=>regex.test(key))};let addGlobal=(name,scope,create,prebuilt)=>{Container.add({name,scope,global:!0,owner:null,create,initialised:prebuilt!==void 0,...prebuilt?{instance:prebuilt.instance}:{}})}})(Service||={});var GLOBAL_OWNER="<global>",globalScope=new Map,moduleScopes=new Map,building=[],constructedEntries=[],overridden=new Set,visibility=null,moduleInitHook=null,skippedLookup=null,isPromise=(value)=>typeof value==="object"&&value!==null&&typeof value.then==="function",ownerId=(owner)=>owner?.id??GLOBAL_OWNER,inOwnerContext=(owner,fn)=>owner?ModuleContext.run(owner,fn):fn(),runOnInit=(entry,instance)=>{let onInit=instance?.onInit;if(typeof onInit!=="function")return;let result=inOwnerContext(entry.owner,()=>onInit.call(instance));return isPromise(result)?result.then(()=>{return}):void 0},construct=(entry)=>{let at=building.indexOf(entry);if(at!==-1)throw new CircularDependencyError([...building.slice(at),entry].map((e)=>e.name));building.push(entry);try{return inOwnerContext(entry.owner,entry.create)}finally{building.pop()}},instantiate=(entry)=>{if(entry.scope===1){let instance=construct(entry);return{instance,ready:runOnInit(entry,instance)}}if(entry.initFailure)throw entry.initFailure.error;if("instance"in entry)return{instance:entry.instance,ready:entry.initialised?void 0:entry.initPromise};let instance=construct(entry);entry.instance=instance,constructedEntries.push({entry,instance});let ready;try{ready=runOnInit(entry,instance)}catch(error){throw entry.initFailure={error},error}if(ready)entry.initPromise=ready.then(()=>{entry.initialised=!0},(error)=>{throw entry.initFailure={error},error}),entry.initPromise.catch(()=>{});else entry.initialised=!0;return{instance,ready:entry.initPromise}},needsOwnerInit=(entry)=>entry.owner!==null&&(entry.scope===1||!("instance"in entry)),entryValue=(entry)=>{if(needsOwnerInit(entry)&&moduleInitHook){let pending=moduleInitHook(entry.owner);if(isPromise(pending))throw pending.catch(()=>{}),new ServiceNotReadyError(entry.name,entry.owner?.id??null)}let{instance,ready}=instantiate(entry);if(ready)throw new ServiceNotReadyError(entry.name,entry.owner?.id??null);return instance},prepare=async(entry,path)=>{let at=path.indexOf(entry);if(at!==-1)throw new CircularDependencyError([...path.slice(at),entry].map((e)=>e.name));let trail=[...path,entry];if(needsOwnerInit(entry)&&moduleInitHook)await moduleInitHook(entry.owner);if(!entry.cls||"instance"in entry&&entry.scope!==1)return;let dependencies=Metadata.get(Symbols.services,entry.cls)??[],found=inOwnerContext(entry.owner,()=>dependencies.map((dependency)=>Container.lookup(dependency.name,ModuleContext.current())));for(let dependency of found){if(!dependency)continue;if(dependency.entry.scope===1)await prepare(dependency.entry,trail);else await entryValueAsync(dependency.entry,trail)}},entryValueAsync=async(entry,path=[])=>{await prepare(entry,path);let{instance,ready}=instantiate(entry);return await ready,instance},Container;((Container)=>{function register(owner,def,config){let isClass=typeof def==="function",options=isClass?Metadata.get(Symbols.serviceOptions,def)??{}:def,name=options.name??(isClass?def.name:"");if(overridden.has(name))return name;return add({name,scope:options.scope??0,global:options.global??owner===null,owner,cls:isClass?def:void 0,create:isClass?()=>make(def):()=>def.factory(config,(service)=>require2(service,def.name)),initialised:!1}),name}Container.register=register;function add(entry){if(entry.global||!entry.owner){let existing=globalScope.get(entry.name);if(existing)throw new DuplicateGlobalServiceError(entry.name,[ownerId(existing.owner),ownerId(entry.owner)]);globalScope.set(entry.name,entry);return}let scope=moduleScopes.get(entry.owner);if(!scope)scope=new Map,moduleScopes.set(entry.owner,scope);if(scope.has(entry.name))throw new ElysiumError(`Service "${entry.name}" is declared twice in module ${entry.owner.id}.`);scope.set(entry.name,entry)}Container.add=add;function setOverrides(names){overridden=new Set(names??[])}Container.setOverrides=setOverrides;function setVisibility(resolver){visibility=resolver}Container.setVisibility=setVisibility;function setModuleInitHook(hook){moduleInitHook=hook}Container.setModuleInitHook=setModuleInitHook;function setSkippedLookup(lookup){skippedLookup=lookup}Container.setSkippedLookup=setSkippedLookup;function lookup(name,from){let found=(entry)=>entry?{entry,owner:entry.owner}:null;if(from){let own=moduleScopes.get(from)?.get(name);if(own)return found(own);for(let imported of visibility?.visibleOwners(from)??[]){let exported=visibility.exportsOf(imported);if(exported===!0||exported.includes(name)){let entry=moduleScopes.get(imported)?.get(name);if(entry)return found(entry)}}}return found(globalScope.get(name))}Container.lookup=lookup;function get(name){let found=lookup(name,ModuleContext.current());return found?entryValue(found.entry):null}Container.get=get;function getGlobal(name){let entry=globalScope.get(name);return entry?entryValue(entry):null}Container.getGlobal=getGlobal;async function resolve(name,requester){let found=lookup(name,ModuleContext.current());if(!found)throw notFound(name,requester);return entryValueAsync(found.entry)}Container.resolve=resolve;function make(cls){let dependencies=Metadata.get(Symbols.services,cls)??[],params=[];for(let dependency of dependencies)params[dependency.index]=require2(dependency.name,cls.name);return new cls(...params)}Container.make=make;function require2(service,requester){let name=typeof service==="string"?service:service.name,found=lookup(name,ModuleContext.current());if(!found)throw notFound(name,requester);return entryValue(found.entry)}function notFound(name,requester){let skipped=skippedLookup?.(name);if(skipped)return new EnvironmentSkippedModuleError(name,requester,skipped.module,skipped.environment);let hidden=[...moduleScopes.values()].map((scope)=>scope.get(name)).find((entry)=>entry!==void 0);return hidden?new ServiceNotVisibleError(name,requester,ModuleContext.current()?.id??null,hidden.owner?.id??null):new ServiceNotFoundError(name)}Container.hasGlobal=(name)=>globalScope.has(name),Container.globalNames=()=>[...globalScope.keys()],Container.removeGlobal=(name)=>{globalScope.delete(name)},Container.clearGlobal=()=>{globalScope.clear()},Container.constructed=()=>constructedEntries.filter(({entry})=>!entry.initFailure).map(({entry,instance})=>({name:entry.name,instance,owner:entry.owner}));function reset(){globalScope.clear(),moduleScopes.clear(),constructedEntries.length=0}Container.reset=reset})(Container||={});var serviceName=(cls)=>Metadata.get(Symbols.serviceOptions,cls)?.name??cls.name,handlerFor=(node,cls,meta)=>{let method=meta.method;return async(data)=>{try{await ModuleContext.run(node,async()=>{if(meta.static)return cls[method](data);return(await Container.resolve(serviceName(cls),cls.name))[method](data)})}catch(e){EventBus.instance.emitError(e)}}};function wireEventListeners(node,owned,services){let plan=[...new Set(owned)].flatMap((cls)=>getEventListeners(cls).map((meta)=>{if(!meta.static&&!services.has(cls))throw new EventListenerNotSupportedError(cls.name,String(meta.method),node.id);return{meta,wrapper:handlerFor(node,cls,meta)}})),bus=EventBus.instance;return plan.map(({meta,wrapper})=>{if(meta.mode==="once")meta.prepend?bus.prependOnceListener(meta.event,wrapper):bus.once(meta.event,wrapper);else meta.prepend?bus.prependListener(meta.event,wrapper):bus.on(meta.event,wrapper);return{event:meta.event,wrapper}})}class ConsoleLogger{context;constructor(context={}){this.context=context}trace(msg,ctx){console.debug(`[TRACE] ${msg}`,{...this.context,...ctx})}debug(msg,ctx){console.debug(`[DEBUG] ${msg}`,{...this.context,...ctx})}info(msg,ctx){console.info(`[INFO] ${msg}`,{...this.context,...ctx})}warn(msg,ctx){console.warn(`[WARN] ${msg}`,{...this.context,...ctx})}error(msg,ctx){console.error(`[ERROR] ${msg}`,{...this.context,...ctx})}fatal(msg,ctx){console.error(`[FATAL] ${msg}`,{...this.context,...ctx})}child(ctx){return new ConsoleLogger({...this.context,...ctx})}}var LOGGER_SERVICE="logger",getLogger=(context)=>{try{let base=Container.getGlobal(LOGGER_SERVICE)??new ConsoleLogger;return context?base.child(context):base}catch{return new ConsoleLogger(context)}};function isConfiguredModule(value){return typeof value==="object"&&value!==null&&value[Symbols.configuredModule]===!0}function getModuleProps(cls){let props=Metadata.get(Symbols.module,cls);if(props===void 0)throw new InvalidModuleError(cls.name);return props}class Module{static register(props={}){return function(target){Metadata.define(Symbols.module,props,target)}}static configurable(options={}){class ConfigurableModule extends Module{static get config(){return`elysium.module.${this.name}.config`}static with(config){return{[Symbols.configuredModule]:!0,module:this,config}}static resolveConfig(input={}){let resolved=deepMerge({...input},{...options.defaults??{}}),missing=(options.required??[]).filter((key)=>resolved[key]===void 0);if(missing.length>0)throw new MissingModuleConfigError(this.name,missing.map(String));return resolved}get config(){return Metadata.get(Symbols.moduleConfig,this)}}return ConfigurableModule}onInit(){}onDestroy(){}onServe(_app){}}import{isEqual}from"radash";var APP_SITE="<app>",unwrap=(entry)=>isConfiguredModule(entry)?entry.module:entry,isActive=(props,environment)=>props.env===void 0||props.env.includes(environment);function discover(roots,environment){let records=new Map,skipped=[],done=new Set,stack=[],visit=(cls)=>{if(done.has(cls))return;let at=stack.indexOf(cls);if(at!==-1)throw new ModuleCycleError([...stack.slice(at),cls].map((c)=>c.name));let props=getModuleProps(cls);if(!isActive(props,environment)){if(!skipped.includes(cls))skipped.push(cls);return}stack.push(cls);let record={cls,props,imports:[],configs:[]};for(let entry of props.imports??[]){let target=unwrap(entry);if(visit(target),!records.has(target)&&!done.has(target))continue;if(!record.imports.includes(target))record.imports.push(target);if(isConfiguredModule(entry))record.configs.push({target,config:entry.config})}stack.pop(),done.add(cls),records.set(cls,record)};for(let root of roots)visit(unwrap(root));return{records,skipped}}function computeDepths(roots,records){let depths=new Map,frontier=roots.filter((r)=>records.has(r));frontier.forEach((r)=>depths.set(r,1));for(let depth=1;frontier.length>0;depth++){let next=[];for(let cls of frontier)for(let imported of records.get(cls).imports)if(!depths.has(imported))depths.set(imported,depth+1),next.push(imported);frontier=next}return depths}function commandsOf(props,environment){return(props.commands??[]).filter((c)=>!(environment==="production"&&c.dev===!0))}function assertUniqueNames(classes){let seen=new Map;for(let cls of classes){let other=seen.get(cls.name);if(other!==void 0&&other!==cls)throw new DuplicateModuleNameError(cls.name);seen.set(cls.name,cls)}}function assertUniqueCommands(nodes,environment){let seen=new Map;for(let node of nodes)for(let command of commandsOf(node.props,environment)){let name=getCommandName(command),owner=seen.get(name);if(owner!==void 0&&owner!==node.id)throw new DuplicateCommandError(name,[owner,node.id]);seen.set(name,node.id)}}var isModuleClass=(value)=>typeof value==="function"&&Metadata.get(Symbols.module,value)!==void 0,serviceName2=(cls)=>Metadata.get(Symbols.serviceOptions,cls)?.name??cls.name;function buildModuleGraph(rootModules,environment=currentEnvironment()){let{records,skipped}=discover(rootModules,environment);assertUniqueNames([...records.keys()]);let rootClasses=rootModules.map(unwrap),depths=computeDepths(rootClasses,records),sources=new Map,addSource=(target,source)=>sources.set(target,[...sources.get(target)??[],source]);for(let root of rootModules){let target=unwrap(root);if(isConfiguredModule(root)&&records.has(target))addSource(target,{site:APP_SITE,depth:1,config:root.config})}for(let record of records.values())for(let{target,config}of record.configs)addSource(target,{site:record.cls.name,depth:depths.get(record.cls)+1,config});let byClass=new Map;for(let record of records.values())byClass.set(record.cls,{id:record.cls.name,moduleClass:record.cls,props:record.props,imports:record.imports.map((i)=>byClass.get(i)),depth:depths.get(record.cls),configSources:sources.get(record.cls)??[]});let nodes=[...byClass.values()];return assertUniqueCommands(nodes,environment),{nodes,skipped,environment,node:(cls)=>byClass.get(cls),visibleOwners:(node)=>{let result=[],add=(owner)=>{if(owner===void 0||result.includes(owner))return;result.push(owner);let exported=owner.props.exports;if(Array.isArray(exported)){for(let entry of exported)if(isModuleClass(entry))add(byClass.get(entry))}};return node.imports.forEach(add),result},exportsOf:(node)=>{let exported=node.props.exports;if(exported===!0)return!0;return(exported??[]).filter((e)=>!isModuleClass(e)).map((e)=>typeof e==="string"?e:serviceName2(e))},collect:(key)=>nodes.flatMap((module)=>(key==="commands"?commandsOf(module.props,environment):module.props[key]??[]).map((item)=>({item,module})))}}function selectConfig(node){if(node.configSources.length===0)return;let best=Math.min(...node.configSources.map((s)=>s.depth)),tied=node.configSources.filter((s)=>s.depth===best),objects=tied.filter((s)=>typeof s.config!=="function");for(let other of objects.slice(1))if(!isEqual(objects[0].config,other.config))throw new ConflictingModuleConfigError(node.id,[objects[0].site,other.site]);return tied[0]}var KERNEL_SERVICE="elysium.kernel",isConfigurable=(cls)=>typeof cls.resolveConfig==="function",evaluate=(config)=>typeof config==="function"?config():config,scopeOf=(def)=>(typeof def==="function"?Metadata.get(Symbols.serviceOptions,def)?.scope:def.scope)??0,nameOf2=(def)=>typeof def==="function"?Metadata.get(Symbols.serviceOptions,def)?.name??def.name:def.name;class Kernel{moduleGraph;instances=new Map;registered=new Map;initialised=new Set;initialising=new Set;initOrder=[];inFlight=new Map;failures=new Map;ownClasses=new Map;wired=[];shutdownPromise;constructor(moduleGraph){this.moduleGraph=moduleGraph}get graph(){return this.moduleGraph}async register(){let{graph}=this;Service.instance(KERNEL_SERVICE,this),Container.setVisibility(graph),Container.setModuleInitHook((node)=>this.initialised.has(node)||this.initialising.has(node)||this.initSync(node)?void 0:this.initModule(node)),Container.setSkippedLookup(this.buildSkippedLookup());for(let node of graph.nodes){let config=await this.resolveNodeConfig(node),instance=ModuleContext.run(node,()=>Service.make(node.moduleClass));Metadata.define(Symbols.moduleConfig,config,instance),this.instances.set(node,instance);let registered=[];if(isConfigurable(node.moduleClass))Container.register(node,{name:node.moduleClass.config,factory:()=>config},config);let{services}=node.props,defs=typeof services==="function"?services(config):services??[],serviceClasses=defs.filter((d)=>typeof d==="function");this.ownClasses.set(node,{owned:[...serviceClasses,...node.props.controllers??[]],services:new Set(serviceClasses)});for(let def of defs)registered.push({name:Container.register(node,def,config),scope:scopeOf(def)});this.registered.set(node,registered)}}initModule(node){if(this.initialised.has(node))return Promise.resolve();let failure=this.failures.get(node);if(failure)return Promise.reject(failure.error);let pending=this.inFlight.get(node);if(!pending)pending=this.runInit(node),pending.catch(()=>{}),this.inFlight.set(node,pending);return pending}async bootEager(){for(let node of this.graph.nodes)await this.initModule(node);for(let node of this.graph.nodes)for(let{name,scope}of this.registered.get(node)??[]){if(scope===1)continue;await ModuleContext.run(node,()=>Service.resolve(name))}}async prepare(cls,owner){await this.initModule(owner);let dependencies=Metadata.get(Symbols.services,cls)??[];for(let{name}of dependencies)await ModuleContext.run(owner,()=>Container.resolve(name,cls.name));return ModuleContext.run(owner,()=>Service.make(cls))}moduleInstance(node){let instance=this.instances.get(node);if(!instance)throw new ElysiumError(`Module ${node.id} has not been registered.`);return instance}shutdown(){return this.shutdownPromise??=this.runShutdown(),this.shutdownPromise}async runInit(node){for(let imported of node.imports)await this.initModule(imported);await this.initOwn(node)}initOwn(node){let instance=this.moduleInstance(node),own=this.ownClasses.get(node);if(own)this.wired.push(...wireEventListeners(node,own.owned,own.services));this.initialising.add(node);let result;try{result=ModuleContext.run(node,()=>instance.onInit())}catch(error){throw this.initialising.delete(node),error}let done=()=>{this.initialising.delete(node),this.initialised.add(node),this.initOrder.push(node)};if(typeof result?.then==="function")return Promise.resolve(result).then(done,(error)=>{throw this.initialising.delete(node),error});done();return}initSync(node){if(this.initialised.has(node)||this.initialising.has(node))return!0;let failure=this.failures.get(node);if(failure)throw failure.error;if(this.inFlight.has(node))return!1;for(let imported of node.imports)if(!this.initSync(imported))return!1;let pending;try{pending=this.initOwn(node)}catch(error){throw this.failures.set(node,{error}),error}if(!pending)return!0;return pending.catch(()=>{}),this.inFlight.set(node,pending),!1}async runShutdown(){for(let{event,wrapper}of this.wired.splice(0))EventBus.instance.removeListener(event,wrapper);let targets=[...Container.constructed().map(({name,instance,owner})=>({label:`service ${name}`,instance,owner})).reverse(),...[...this.initOrder].reverse().map((node)=>({label:`module ${node.id}`,instance:this.instances.get(node),owner:node}))];for(let{label,instance,owner}of targets){let onDestroy=instance?.onDestroy;if(typeof onDestroy!=="function")continue;try{let call=()=>onDestroy.call(instance);await(owner?ModuleContext.run(owner,call):call())}catch(error){getLogger().error("Error while destroying",{target:label,error})}}}async resolveNodeConfig(node){let selected=selectConfig(node),input;if(selected){input=await evaluate(selected.config);let tied=node.configSources.filter((s)=>s.depth===selected.depth);if(tied.length>1&&tied.some((s)=>typeof s.config==="function")){for(let other of tied.filter((s)=>s!==selected))if(!isEqual2(input,await evaluate(other.config)))throw new ConflictingModuleConfigError(node.id,[selected.site,other.site])}}return isConfigurable(node.moduleClass)?node.moduleClass.resolveConfig(input):void 0}buildSkippedLookup(){let owners=new Map;for(let cls of this.graph.skipped){let{services}=getModuleProps(cls);if(!Array.isArray(services))continue;for(let def of services)owners.set(nameOf2(def),cls.name)}let{environment}=this.graph;return(name)=>{let module=owners.get(name);return module===void 0?null:{module,environment}}}}import{ElysiaCustomStatusResponse}from"elysia";var executeMiddlewareChain=(middlewares,context,method,node=null)=>{let chain=async()=>{for(let middleware of middlewares){let result=await middleware[method](context);if(result!==void 0)return result}};return Application.context.run(new Map([["tenant",context.tenant],["http:context",context]]),()=>node?ModuleContext.run(node,chain):chain())},makeMiddlewares=(middlewares,node)=>middlewares.map((middleware)=>node?ModuleContext.run(node,()=>Service.make(middleware)):Service.make(middleware)),applyMiddlewares=(middlewares,plugin,node)=>{let mi=makeMiddlewares(middlewares,node);if(node)plugin.onTransform(async(c)=>{let result=await executeMiddlewareChain(mi,c,"onRequest",node);if(result===void 0)return executeMiddlewareChain(mi,c,"onTransform",node);if(result instanceof ElysiaCustomStatusResponse)return result;return c.status(result instanceof Response?result.status:c.set.status??200,result)});else plugin.onRequest((c)=>executeMiddlewareChain(mi,c,"onRequest",node)),plugin.onTransform((c)=>executeMiddlewareChain(mi,c,"onTransform",node));plugin.onBeforeHandle((c)=>executeMiddlewareChain(mi,c,"onBeforeHandle",node)),plugin.onAfterHandle((c)=>executeMiddlewareChain(mi,c,"onAfterHandle",node)),plugin.onAfterResponse((c)=>executeMiddlewareChain(mi,c,"onAfterResponse",node))};class Middleware{static register(...middlewares){return function(target,propertyKey,descriptor){if(propertyKey===void 0&&descriptor===void 0){let m=Metadata.get(Symbols.middlewares,target)??[];m.push(...middlewares),Metadata.define(Symbols.middlewares,m,target)}else{let m=Metadata.get(Symbols.middlewares,target,propertyKey)??[];m.push(...middlewares),Metadata.define(Symbols.middlewares,m,target,propertyKey)}}}static guards(guards){class GuardedMiddleware extends this{}return Metadata.define(Symbols.middlewareGuards,guards,GuardedMiddleware),GuardedMiddleware}get logger(){return getLogger({middleware:this.constructor.name})}get guards(){return Metadata.get(Symbols.middlewareGuards,this.constructor)??[]}onRequest(_ctx){}onTransform(_ctx){}onAfterHandle(_ctx){}onBeforeHandle(_ctx){}onAfterResponse(_ctx){}}var resetContainer=()=>{Container.reset(),Container.setOverrides(null),Container.setVisibility(null),Container.setModuleInitHook(null),Container.setSkippedLookup(null)};async function createTestingApp(options){let{modules,overrides={},environment="test",boot="lazy"}=options;resetContainer();try{let graph=buildModuleGraph(modules,environment),kernel=new Kernel(graph);Container.setOverrides(Object.keys(overrides));for(let[name,value]of Object.entries(overrides))Service.instance(name,value);if(await kernel.register(),boot==="eager")await kernel.bootEager();return{kernel,get:async(name,from)=>{let resolve=()=>Service.resolve(name);if(!from)return resolve();let node=graph.node(from);if(!node)throw new ElysiumError(`Module ${from.name} is not part of the test graph.`);return ModuleContext.run(node,resolve)},shutdown:async()=>{await kernel.shutdown(),resetContainer()}}}catch(error){throw resetContainer(),error}}var fallbackContext=new AsyncLocalStorage2;class Application extends InteractsWithConsole{#elysia;#debug;#kernel;#onSignal;_appContextStorage=new AsyncLocalStorage2;static get instance(){return Service.get("elysium.app")}static get context(){return Application.instance?._appContextStorage??fallbackContext}static register(props={}){return function(target){let parentMetadata,proto=Object.getPrototypeOf(target);while(proto&&proto!==Object.prototype){if(parentMetadata=Metadata.getOwn(Symbols.app,proto),parentMetadata)break;proto=Object.getPrototypeOf(proto)}let ownMetadata=Metadata.getOwn(Symbols.app,target),modules=[...parentMetadata?.modules??[],...ownMetadata?.modules??[],...props.modules??[]],withoutModules=({modules:_modules,...rest})=>rest;props={...deepMerge(withoutModules(props),withoutModules(ownMetadata??{}),withoutModules(parentMetadata??{}),{openapi:!1}),modules},Metadata.define(Symbols.app,props,target)}}constructor(){super();let{env,debug}=Metadata.get(Symbols.app,this.constructor)??{};if(this.#debug=debug??!1,env){let rawEnv=Value.Convert(env,Value.Clone(Bun.env));if(this.#debug&&!Value.Check(env,rawEnv)){let error=Value.Errors(env,rawEnv).First();this.trace(error,"Invalid environment configuration")}initEnv(Value.Clean(env,rawEnv))}else initEnv(Bun.env);Service.remove("elysium.app"),Service.instance("elysium.app",this)}static start(){return new this().run()}static testing(options){return createTestingApp(options)}get kernel(){if(!this.#kernel)throw new ElysiumError("The application has not been started yet.");return this.#kernel}get elysia(){return this.#elysia}get isDebug(){return this.#debug}set isDebug(debug){this.#debug=debug}async onError(_error){return!0}async onStart(_elysia){}async onStop(_elysia){}async run(argv=process.argv.slice(2)){this.installSignalHandlers();try{let{modules}=this.props();return this.#kernel=new Kernel(buildModuleGraph(modules??[])),await this.dispatch(argv)}catch(error){return this.fail(error)}}props(){return Metadata.get(Symbols.app,this.constructor)??{}}async dispatch([action,...rest]){switch(action){case void 0:return this.commandDescribe(),this.exit(0);case"serve":return this.commandServe();case"list":return this.write(this.bold("Available commands:")),this.commandList(),this.exit(0);case"help":return this.commandHelp(rest[0]);default:return this.commandExec(action,rest)}}async fail(error){let message=error instanceof Error?error.message:String(error);if(this.error(message),!(error instanceof ElysiumError)&&this.isDebug)this.trace(error);return Event.emit("elysium:error",error),await this.#kernel?.shutdown(),this.exit(1)}exit(code){return this.removeSignalHandlers(),process.exit(code)}removeSignalHandlers(){if(!this.#onSignal)return;process.off("SIGINT",this.#onSignal),process.off("SIGTERM",this.#onSignal),this.#onSignal=void 0}installSignalHandlers(){this.removeSignalHandlers();let onSignal=async()=>{this.removeSignalHandlers();let code=0;try{await this.#elysia?.stop()}catch(error){code=1,this.error(error instanceof Error?error.message:String(error))}finally{try{await this.#kernel?.shutdown()}catch(error){code=1,this.error(error instanceof Error?error.message:String(error))}this.exit(code)}};this.#onSignal=onSignal,process.on("SIGINT",onSignal),process.on("SIGTERM",onSignal)}getCommands(){return this.kernel.graph.collect("commands")}findCommand(name){return this.getCommands().find(({item})=>getCommandName(item)===name)}async prepareCommand({item,module},forHelp=!1){let eager=item.boot&&!forHelp;if(await this.bootKernel(eager),!eager)return this.kernel.prepare(item,module);return ModuleContext.run(module,()=>Service.make(item))}async bootKernel(eager){if(await this.kernel.register(),eager)await this.kernel.bootEager();Event.emit("elysium:app:launched",this,this)}async commandHelp(name){let found=name?this.findCommand(name):void 0;if(!found)return this.error(name?`Command <${name}> not found.`:"No command provided. Usage: styx help <command>"),this.commandList(),this.exit(1);let command=await this.prepareCommand(found,!0);return this.write(await ModuleContext.run(found.module,()=>command.help())),await this.kernel.shutdown(),this.exit(0)}async commandExec(name,argv){let found=this.findCommand(name);if(!found)return this.error(`Command ${name} not found`),this.commandList(),this.exit(1);let command=await this.prepareCommand(found);return await ModuleContext.run(found.module,async()=>{if(await command.init(...argv))await command.run();else this.write(await command.help())}),await this.kernel.shutdown(),this.exit(0)}async commandServe(){let{server,openapi,plugins}=this.props();if(await this.bootKernel(!0),this.#elysia=new Elysia(server).onRequest((c)=>{if(this.isDebug)getLogger().debug("Incoming request",{method:c.request.method,url:c.request.url})}),Event.emit("elysium:server:before-init",this,this),this.#elysia.resolve({as:"global"},(ctx)=>({tenant:ctx.tenant??ctx.request.headers.get("x-tenant-id")??"public"})),openapi)this.#elysia.use(openapiPlugin(openapi));for(let plugin of plugins??[])this.#elysia.use(await plugin(this));this.setupServerHooks();let middlewares=Metadata.get(Symbols.middlewares,this.constructor)??[];applyMiddlewares(middlewares,this.#elysia,null),await this.serveModules(),Event.emit("elysium:server:after-init",this,this),this.#elysia.listen(server?.port??(parseInt(process.env.PORT,10)||3000))}async serveModules(){let{nodes}=this.kernel.graph;for(let node of nodes){let instance=this.kernel.moduleInstance(node);await ModuleContext.run(node,()=>instance.onServe(this.#elysia))}for(let node of nodes)if(node.props.controllers?.length)this.debug(`Mounting module ${node.id}...`),this.#elysia.use(await this.mountModule(node))}async mountModule(node){let app=new Elysia({name:node.id,prefix:node.props.prefix}),middlewares=Metadata.get(Symbols.middlewares,node.moduleClass)??[];applyMiddlewares(middlewares,app,node);for(let controller of node.props.controllers??[]){let plugin=Metadata.get(Symbols.elysiaPlugin,controller);if(!plugin)throw new InvalidControllerError(controller.name,node.id);app.use(await plugin(node))}return app}setupServerHooks(){this.#elysia.onError({as:"global"},(e)=>this.handleServerError(e)).onStart(async(elysia)=>{await this.onStart(elysia),Event.emit("elysium:server:start",elysia,this)}).onStop(async(elysia)=>{await this.onStop(elysia),Event.emit("elysium:server:stop",elysia,this),this.removeSignalHandlers(),await this.#kernel?.shutdown(),this._appContextStorage.disable()})}async handleServerError(e){if(!await this.onError(e))return;if(Event.emit("elysium:error",e),this.trace({name:"HttpError",message:"Unexpected error while handling HTTP request",stack:e.error.stack,...e.error}),typeof e.code==="number")return e.error.response;let data={};switch(e.code){case"VALIDATION":data={scope:e.error.type,code:e.error.message};break;case"INTERNAL_SERVER_ERROR":data={message:e.error.message};break}return{type:e.code,data}}commandDescribe(){this.section(`${this.bold("Usage:")} ${this.format("styx",5)} <command> [options]`),this.section(this.bold("Core Commands:")),this.newLine(),this.commandDescription("serve","","Starts the server."),this.commandDescription("help","<command>","Displays help for a command."),this.commandDescription("list","","List all available application commands."),this.section(this.bold("Application Commands:")),this.commandList()}commandList(){let groups=this.getCommands().reduce((acc,{item})=>{let groupKey=first(getCommandName(item).split(":"));return acc.set(groupKey,[...acc.get(groupKey)??[],item]),acc},new Map);for(let[group,groupCommands]of groups){this.section(this.format(group,4));for(let commandClass of groupCommands)this.commandDescription(getCommandName(commandClass),"",commandClass.description)}}commandDescription(command,args,description,width=InteractsWithConsole.SPACE_WIDTH){args=args.length===0?" ":` ${args} `,this.write(` ${this.format(command,6)}${args}${"\u2219".repeat(width-command.length-args.length)} ${description}`)}}import{Elysia as Elysia2,sse as esse,t}from"elysia";import{assign,isEmpty as isEmpty2,objectify}from"radash";async function*iterateInModule(node,generator){let finished=!1;try{while(!0){let step=await ModuleContext.run(node,()=>generator.next());if(step.done){finished=!0;return}yield step.value}}catch(error){throw finished=!0,error}finally{if(!finished)await ModuleContext.run(node,()=>generator.return(void 0))}}var cancellation=Error("The stream was closed before its end.");async function*iterateInScope(scope,start,signal){let cancelled=Promise.withResolvers();cancelled.promise.catch(()=>{});let cancel=()=>cancelled.reject(cancellation);if(signal?.aborted)cancel();signal?.addEventListener("abort",cancel,{once:!0});let pending=()=>{let deferred=Promise.withResolvers();return deferred.promise.catch(()=>{}),deferred},resume=Promise.withResolvers(),deliver=pending(),finished=scope(async()=>{let generator=start(),stepping=!1;try{while(await Promise.race([resume.promise,cancelled.promise])){resume=Promise.withResolvers(),stepping=!0;let step=await Promise.race([generator.next(),cancelled.promise]);stepping=!1;let current=deliver;if(deliver=pending(),current.resolve(step),step.done)return}}catch(error){if(error!==cancellation)throw error;if(stepping)generator.return(void 0).catch((error)=>Event.emit("elysium:error",error));else await generator.return(void 0);throw error}throw await generator.return(void 0),cancellation});finished.catch((error)=>deliver.reject(error));let completed=!1;try{while(!0){let current=deliver;resume.resolve(!0);let step=await current.promise.catch((error)=>{if(error===cancellation)return{done:!0,value:void 0};throw error});if(step.done){completed=!0;return}yield step.value}}finally{if(signal?.removeEventListener("abort",cancel),!completed)resume.resolve(!1);await finished.catch((error)=>{if(error!==cancellation)throw error})}}var DATABASE_TRANSACTION_SERVICE="db.transaction",getTransactionRunner=()=>{let runner=Service.get(DATABASE_TRANSACTION_SERVICE);if(!runner)throw Error(`Transactional handlers need the "${DATABASE_TRANSACTION_SERVICE}" service. Import a database module (such as DrizzleModule) in the application.`);return runner},registerHttpRequestHandler=(props)=>{let body=Metadata.get("http:body",props.target,props.propertyKey),params=Metadata.get("http:params",props.target,props.propertyKey)??[],query=Metadata.get("http:query",props.target,props.propertyKey),rawContext=Metadata.get("http:rawContext",props.target,props.propertyKey),customDecorators=Metadata.get("http:customDecorators",props.target,props.propertyKey)??[],middlewares=Metadata.get(Symbols.middlewares,props.target,props.propertyKey)??[],services=Metadata.get(Symbols.services,props.target,props.propertyKey)??[],{path,method,handler,target,response,operationId,description,transactional}=props,metadata=Metadata.get(Symbols.http,target.constructor)??[];metadata.push({path,method,body,params,query,customDecorators,rawContext,handler,middlewares,response,operationId,description,services,transactional}),Metadata.define(Symbols.http,metadata,target.constructor)},HttpControllerScope;((HttpControllerScope)=>{HttpControllerScope[HttpControllerScope.SERVER=0]="SERVER";HttpControllerScope[HttpControllerScope.REQUEST=1]="REQUEST"})(HttpControllerScope||={});var Http;((Http)=>(Http.controller=(props={path:"/"})=>function(target){async function handleHttp(node){await nextTick(),props=assign({path:"/",scope:0,tags:[]},props);let app=new Elysia2({prefix:props.path,name:`${node.id}:${target.name}`}),getController=(()=>{if(props.scope===0){let controller=ModuleContext.run(node,()=>Service.make(target));return()=>()=>controller}else if(props.scope===1)return()=>()=>ModuleContext.run(node,()=>Service.make(target));else throw Error(`Invalid scope for controller: ${target.name}`)})();app.resolve({as:"local"},()=>({controller:getController()}));let onRequest=Metadata.get("http:onRequest",target)??{};if(onRequest.handler)app.onRequest((c)=>{let controller=getController()();return ModuleContext.run(node,()=>onRequest.handler.call(controller,c))});let middlewares=Metadata.get(Symbols.middlewares,target)??[];applyMiddlewares(middlewares,app,node);let metadata=Metadata.get(Symbols.http,target)??[];for(let route of metadata){let getParameters=async(c)=>{let parameters=[];if(route.rawContext)parameters[route.rawContext.index]=c;if(route.body)parameters[route.body.index]=c.body;if(route.query)parameters[route.query.index]=c.query;if(!isEmpty2(route.params))for(let param of route.params)parameters[param.index]=c.params[param.slug];if(!isEmpty2(route.customDecorators))for(let customDecorator of route.customDecorators)parameters[customDecorator.index]=await ModuleContext.run(node,()=>customDecorator.handler(c));if(!isEmpty2(route.services))for(let service of route.services)parameters[service.index]=ModuleContext.run(node,()=>Service.get(service.name));return parameters},isGenerator=route.handler.constructor.name.includes("GeneratorFunction"),isSSE=route.method==="elysium:SSE",isTransactional=route.transactional??props.transactional??!1,isValidator=route.body&&route.body.schema===void 0&&route.body.validator!==void 0;route.method=isSSE?"GET":route.method;let getHandler=()=>{let inRequestScope=async(c,body)=>Application.context.run(new Map([["tenant",c.tenant],["http:context",c]]),()=>isTransactional?getTransactionRunner()(body):body()),callHandler=(c,args)=>ModuleContext.run(node,()=>route.handler.call(c.controller(),...args)),initTransactedHandler=(c)=>(...args)=>inRequestScope(c,async()=>callHandler(c,args));if(isGenerator)return async function*(c){let params=await getParameters(c);try{yield*iterateInScope((body)=>inRequestScope(c,body),()=>iterateInModule(node,callHandler(c,params)),c.request?.signal)}catch(error){yield error}};if(isSSE)return async function*(c){let handler=initTransactedHandler(c);c.set.headers["content-type"]="text/event-stream",c.set.headers["cache-control"]="no-cache",c.set.headers.connection="keep-alive";while(!0)yield esse(await handler(...await getParameters(c))),await Bun.sleep(10)};if(isValidator)return async function(c){if(await ModuleContext.run(node,()=>route.body.validator.validate(c)))return await initTransactedHandler(c)(...await getParameters(c));throw c.status(400,"Invalid request body")};return async function(c){return await initTransactedHandler(c)(...await getParameters(c))}},params=objectify(route.params??[],(p)=>p.slug,(p)=>p.schema),mi=makeMiddlewares(route.middlewares,node);app.route(route.method,route.path,getHandler(),{config:{},async transform(c){if(isValidator&&!await ModuleContext.run(node,()=>route.body.validator.authorize(c)))throw c.status(401,"Unauthorized");return executeMiddlewareChain(mi,c,"onTransform",node)},beforeHandle(c){return executeMiddlewareChain(mi,c,"onBeforeHandle",node)},afterHandle(c){return executeMiddlewareChain(mi,c,"onAfterHandle",node)},afterResponse(c){return executeMiddlewareChain(mi,c,"onAfterResponse",node)},detail:{tags:props.tags,description:route.description,operationId:route.operationId},body:isValidator?route.body?.validator?.schema:route.body?.schema,response:route.response,params:isEmpty2(params)?void 0:t.Object(params)})}return app}Metadata.define(Symbols.elysiaPlugin,handleHttp,target)},Http.sse=(props={})=>{let{path="/",...rest}=props;return Http.custom({method:"elysium:SSE",path,...rest})},Http.get=(props={})=>{let{path="/",...rest}=props;return Http.custom({method:"GET",path,...rest})},Http.post=(props={})=>{let{path="/",...rest}=props;return Http.custom({method:"POST",path,...rest})},Http.put=(props={})=>{let{path="/",...rest}=props;return Http.custom({method:"PUT",path,...rest})},Http.del=(props={})=>{let{path="/",...rest}=props;return Http.custom({method:"DELETE",path,...rest})},Http.patch=(props={})=>{let{path="/",...rest}=props;return Http.custom({method:"PATCH",path,...rest})},Http.head=(props={})=>{let{path="/",...rest}=props??{};return Http.custom({method:"HEAD",path,...rest})},Http.options=(props={})=>{let{path="/",...rest}=props;return Http.custom({method:"OPTIONS",path,...rest})},Http.trace=(props={})=>{let{path="/",...rest}=props;return Http.custom({method:"TRACE",path,...rest})},Http.custom=({path="/",...rest})=>function(target,propertyKey,descriptor){process.nextTick(()=>{registerHttpRequestHandler({path,handler:descriptor.value,target,propertyKey,...rest})})},Http.onRequest=()=>function(target,_,descriptor){let metadata=Metadata.get("http:onRequest",target.constructor)??{};metadata.handler=descriptor.value,Metadata.define("http:onRequest",metadata,target.constructor)},Http.context=()=>function(target,propertyKey,parameterIndex){Metadata.define("http:rawContext",{index:parameterIndex},target,propertyKey)},Http.param=(slug,schema)=>function(target,propertyKey,parameterIndex){let params=Metadata.get("http:params",target,propertyKey)??[];params.push({slug,schema:schema??t.String(),index:parameterIndex}),Metadata.define("http:params",params,target,propertyKey)},Http.query=(schema)=>function(target,propertyKey,parameterIndex){Metadata.define("http:query",{schema,index:parameterIndex},target,propertyKey)},Http.body=(schema)=>function(target,propertyKey,parameterIndex){if(typeof schema==="function")Metadata.define("http:body",{validator:schema,index:parameterIndex},target,propertyKey);else Metadata.define("http:body",{schema,index:parameterIndex},target,propertyKey)},Http.decorate=(handler)=>()=>function(target,propertyKey,parameterIndex){let decorators=Metadata.get("http:customDecorators",target,propertyKey)??[];decorators.push({handler,index:parameterIndex}),Metadata.define("http:customDecorators",decorators,target,propertyKey)}))(Http||={});class RequestLoggerMiddleware extends Middleware{async onRequest(ctx){ctx.__requestStartTime=Date.now(),this.logger.info("Incoming request",{method:ctx.request.method,path:new URL(ctx.request.url).pathname})}async onAfterResponse(ctx){let duration=Date.now()-(ctx.__requestStartTime??0);this.logger.info("Request completed",{method:ctx.request.method,path:new URL(ctx.request.url).pathname,duration:`${duration}ms`})}}var DEFAULT="default";function needsDefaultAlias(module,config){if(!Object.hasOwn(config.connections,config.default))throw new InvalidModuleConfigError(module,"default",`"${config.default}" is not one of the declared connections`);if(config.default===DEFAULT)return!1;if(Object.hasOwn(config.connections,DEFAULT))throw new InvalidModuleConfigError(module,"connections",`a connection named "default" conflicts with the default alias to "${config.default}"`);return!0}async function runAll(tasks){let errors=(await Promise.allSettled(tasks.map(async(task)=>task()))).flatMap((r)=>r.status==="rejected"?[r.reason]:[]);if(errors.length===1)throw errors[0];if(errors.length>1)throw AggregateError(errors,"Several connections failed to close.")}import{omit as omit2}from"radash";var Redis;((Redis)=>(Redis.getConnectionName=(name)=>`redis.connection.${name}`,Redis.getConnection=(name="default")=>{let client=Service.get(Redis.getConnectionName(name));if(client===null)throw new ServiceNotFoundError(Redis.getConnectionName(name));return client},Redis.connectionExists=(name)=>Service.exists(Redis.getConnectionName(name)),Redis.getDefaultConnection=()=>Redis.getConnection("default")))(Redis||={});class RedisModule extends Module.configurable({defaults:{default:"default"},required:["connections"]}){async onDestroy(){let names=Object.keys(this.config.connections).map(Redis.getConnectionName),clients=Container.constructed().filter(({name})=>names.includes(name));await runAll(clients.map(({instance})=>()=>instance.close()))}}RedisModule=__legacyDecorateClassTS([Module.register({services:(config)=>{let alias=needsDefaultAlias("RedisModule",config);return[...Object.entries(config.connections).map(([name,props])=>({name:Redis.getConnectionName(name),global:!0,factory:()=>new Bun.RedisClient(props.url,omit2(props,["url"]))})),...alias?[{name:Redis.getConnectionName("default"),global:!0,factory:(_config,get)=>get(Redis.getConnectionName(config.default))}]:[]]}})],RedisModule);import{Elysia as Elysia3}from"elysia";import{omit as omit3}from"radash";import Wampy from"wampy";var Wamp;((Wamp2)=>(Wamp2.getConnectionName=(name)=>`wamp.connection.${name}`,Wamp2.getConnection=(name="default")=>{let client=Service.get(Wamp2.getConnectionName(name));if(client===null)throw new ServiceNotFoundError(Wamp2.getConnectionName(name));return client},Wamp2.connectionExists=(name)=>Service.exists(Wamp2.getConnectionName(name)),Wamp2.getDefaultConnection=()=>Wamp2.getConnection("default"),Wamp2.createClient=(name,config)=>{let client=new Wampy(config.url,{ws:WebSocket,...omit3(config,["url","onOpen"]),onError(error){config.onError?.(error);let message=`An error occurred in WAMP connection ${name} (${config.url})`,reason=error;Event.emit("elysium:error",reason==null?Error(message):Error(`${message}: ${reason instanceof Error?reason.message:String(reason)}`,{cause:reason}))}}),maxRetries=config.maxRetries??5,retryInterval=config.retryInterval??5000,retryCount=0,closed=!1,timer,disconnect=client.disconnect.bind(client);client.disconnect=()=>(closed=!0,clearTimeout(timer),disconnect());let tryConnect=async()=>{if(closed)return;try{await client.connect()}catch(error){if(retryCount<maxRetries)getLogger().warn("Failed to connect to WAMP server, retrying",{connection:name,attempt:retryCount+1,maxRetries,error}),retryCount++,timer=setTimeout(tryConnect,retryInterval);else{let attempts=retryCount+1;getLogger().error("Failed to connect to WAMP server, giving up",{connection:name,attempts,maxRetries,error}),Event.emit("elysium:error",Error(`Failed to establish WAMP connection ${name} after ${attempts} attempts`))}return}if(closed)return;try{await config.onOpen?.()}catch(error){Event.emit("elysium:error",error)}};return tryConnect(),client},Wamp2.controller=(options)=>function(target){async function handleWamp(node){await nextTick();let controller=ModuleContext.run(node,()=>Service.make(target)),inModule=(handler)=>(...args)=>ModuleContext.run(node,()=>handler.apply(controller,args)),metadata=Metadata.get(Symbols.wamp,target)??{},connection=options.connection??"default",w=Wamp.getConnection(connection),app=new Elysia3({name:`${node.id}:${target.name}`});return app.onStart(async(_)=>{let registrations=metadata.registrations??[],subscriptions=metadata.subscriptions??[];for(let{topic,handler,options}of registrations)w.register(topic,inModule(handler),options).then((r)=>{getLogger().info("Registered WAMP RPC",{connection,topic,registrationId:r.registrationId})},(error)=>{getLogger().error("Failed to register WAMP RPC",{connection,topic,error}),Event.emit("elysium:error",Error(`Failed to register WAMP RPC ${topic} on connection ${connection}`,{cause:error}))});for(let{topic,handler,options}of subscriptions)w.subscribe(topic,inModule(handler),options).then((s)=>{getLogger().info("Subscribed to WAMP topic",{connection,topic,subscriptionId:s.subscriptionId})},(error)=>{getLogger().error("Failed to subscribe to WAMP topic",{connection,topic,error}),Event.emit("elysium:error",Error(`Failed to subscribe to WAMP topic ${topic} on connection ${connection}`,{cause:error}))})}),app}Metadata.define(Symbols.elysiaPlugin,handleWamp,target)},Wamp2.register=(topic,options)=>function(target,_propertyKey,descriptor){let metadata=Metadata.get(Symbols.wamp,target.constructor)??{};metadata.registrations??=[],metadata.registrations.push({topic,options,handler:descriptor.value}),Metadata.define(Symbols.wamp,metadata,target.constructor)},Wamp2.subscribe=(topic,options)=>function(target,_propertyKey,descriptor){let metadata=Metadata.get(Symbols.wamp,target.constructor)??{};metadata.subscriptions??=[],metadata.subscriptions.push({topic,options,handler:descriptor.value}),Metadata.define(Symbols.wamp,metadata,target.constructor)}))(Wamp||={});class WampModule extends Module.configurable({defaults:{default:"default"},required:["connections"]}){async onDestroy(){let names=Object.keys(this.config.connections).map(Wamp.getConnectionName),clients=Container.constructed().filter(({name})=>names.includes(name));await runAll(clients.map(({instance})=>()=>instance.disconnect()))}}WampModule=__legacyDecorateClassTS([Module.register({services:(config)=>{let alias=needsDefaultAlias("WampModule",config);return[...Object.entries(config.connections).map(([name,props])=>({name:Wamp.getConnectionName(name),global:!0,factory:()=>Wamp.createClient(name,props)})),...alias?[{name:Wamp.getConnectionName("default"),global:!0,factory:(_config,get)=>get(Wamp.getConnectionName(config.default))}]:[]]}})],WampModule);import{Elysia as Elysia4}from"elysia";var Websocket;((Websocket)=>(Websocket.controller=(props)=>function(target){async function handleWebsocket(node){await nextTick();let controller=ModuleContext.run(node,()=>Service.make(target)),metadata=Metadata.get(Symbols.websocket,target)??{},inModule=(handler)=>(...args)=>ModuleContext.run(node,()=>handler?.apply(controller,args)),open=metadata.open&&inModule(metadata.open),close=metadata.close&&inModule(metadata.close),message=metadata.message&&inModule(metadata.message),drain=metadata.drain&&inModule(metadata.drain),error=metadata.error?inModule(metadata.error):(e)=>Event.emit("elysium:error",e.error),app=new Elysia4({name:`${node.id}:${target.name}`}),middlewares=Metadata.get(Symbols.middlewares,target)??[];return applyMiddlewares(middlewares,app,node),app.ws(props.path,{open,close,message,drain,error,body:metadata.body,...props.options}),app}Metadata.define(Symbols.elysiaPlugin,handleWebsocket,target)},Websocket.onOpen=()=>function(target,_,descriptor){let metadata=Metadata.get(Symbols.websocket,target.constructor)??{};metadata.open=descriptor.value,Metadata.define(Symbols.websocket,metadata,target.constructor)},Websocket.onClose=()=>function(target,_,descriptor){let metadata=Metadata.get(Symbols.websocket,target.constructor)??{};metadata.close=descriptor.value,Metadata.define(Symbols.websocket,metadata,target.constructor)},Websocket.onMessage=(schema)=>function(target,_,descriptor){let metadata=Metadata.get(Symbols.websocket,target.constructor)??{};metadata.message=descriptor.value,metadata.body=schema,Metadata.define(Symbols.websocket,metadata,target.constructor)},Websocket.onDrain=()=>function(target,_,descriptor){let metadata=Metadata.get(Symbols.websocket,target.constructor)??{};metadata.drain=descriptor.value,Metadata.define(Symbols.websocket,metadata,target.constructor)},Websocket.onError=()=>function(target,_,descriptor){let metadata=Metadata.get(Symbols.websocket,target.constructor)??{};metadata.error=descriptor.value,Metadata.define(Symbols.websocket,metadata,target.constructor)}))(Websocket||={});export{Application,CircularDependencyError,Command,CommandArgumentType,ConflictingModuleConfigError,ConsoleFormat,ConsoleLogger,DATABASE_TRANSACTION_SERVICE,DuplicateCommandError,DuplicateGlobalServiceError,DuplicateModuleNameError,ElysiumError,Env,EnvironmentSkippedModuleError,Event,EventListenerNotSupportedError,Http,HttpControllerScope,InteractsWithConsole,InvalidModuleConfigError,InvalidModuleError,KERNEL_SERVICE,Kernel,LOGGER_SERVICE,Metadata,Middleware,MissingModuleConfigError,Module,ModuleContext,ModuleCycleError,Redis,RedisModule,RequestLoggerMiddleware,Service,ServiceNotFoundError,ServiceNotReadyError,ServiceNotVisibleError,ServiceScope,Wamp,WampModule,Websocket,getLogger,needsDefaultAlias,runAll,t2 as t};
package/dist/logger.d.ts CHANGED
@@ -33,3 +33,18 @@ export declare class ConsoleLogger implements LoggerInterface {
33
33
  fatal(msg: string, ctx?: Record<string, unknown>): void;
34
34
  child(ctx: Record<string, unknown>): LoggerInterface;
35
35
  }
36
+ /**
37
+ * The name of the global service holding the application logger.
38
+ * @author Axel Nana <axel.nana@workbud.com>
39
+ */
40
+ export declare const LOGGER_SERVICE = "logger";
41
+ /**
42
+ * Resolves the application logger at call time: the `logger` service when one is registered
43
+ * (e.g. by `@elysiumjs/hermes`), a {@link ConsoleLogger} otherwise or when it cannot be built
44
+ * or fails to create the child logger. Only the global scope is searched, so a module's own
45
+ * `logger` service never replaces the application logger.
46
+ * @author Axel Nana <axel.nana@workbud.com>
47
+ * @param context Optional context bound to the returned logger, as a child logger.
48
+ * @returns The application logger, or a child of it when `context` is given.
49
+ */
50
+ export declare const getLogger: (context?: Record<string, unknown>) => LoggerInterface;
@@ -47,8 +47,8 @@ export declare abstract class Middleware<TGuards extends string[] = []> {
47
47
  */
48
48
  static guards(guards: string[]): Class<Middleware>;
49
49
  /**
50
- * Structured application logger resolved from the service container.
51
- * Returns a child logger with the middleware class name in context.
50
+ * Structured application logger resolved from the service container, falling back to a
51
+ * `ConsoleLogger`. Returns a child logger with the middleware class name in context.
52
52
  * @author Axel Nana <axel.nana@workbud.com>
53
53
  */
54
54
  protected get logger(): LoggerInterface;
@@ -58,7 +58,10 @@ export declare abstract class Middleware<TGuards extends string[] = []> {
58
58
  get guards(): TGuards;
59
59
  /**
60
60
  * Called first before everything else. This middleware hook works only when used on
61
- * a controller, a module, or the application.
61
+ * a controller, a module, or the application. On the application it runs for every request,
62
+ * before routing. On a module or a controller it runs only for its routes, after the body is
63
+ * parsed and right before its own `onTransform`: reject early on the application instead to skip
64
+ * parsing. A returned value stops the request and is sent as the response.
62
65
  * @param _ctx The request context.
63
66
  */
64
67
  onRequest(_ctx: Context): any;
@@ -37,6 +37,11 @@ export type WSError = ErrorContext & {
37
37
  export declare namespace Websocket {
38
38
  /**
39
39
  * Marks a class as a websocket controller.
40
+ *
41
+ * Middlewares registered on the controller run on the upgrade request. `onRequest`, `onTransform`
42
+ * and `onBeforeHandle` can refuse the connection by returning a response. `onAfterHandle` and
43
+ * `onAfterResponse` run once the socket is upgraded: there is no response to read or replace.
44
+ *
40
45
  * @author Axel Nana <axel.nana@workbud.com>
41
46
  * @param props The websocket route properties.
42
47
  */
package/package.json CHANGED
@@ -1,15 +1,19 @@
1
1
  {
2
2
  "name": "@elysiumjs/core",
3
- "version": "0.8.0",
3
+ "version": "0.10.0",
4
4
  "license": "Apache-2.0",
5
5
  "description": "A super-charged web framework for Bun, built on top of ElysiaJS.",
6
6
  "private": false,
7
7
  "author": "Workbud Technologies Inc.",
8
8
  "repository": {
9
9
  "type": "git",
10
- "url": "https://github.com/workbud/elysium"
10
+ "url": "git+https://github.com/na2axl/elysiumjs.git",
11
+ "directory": "@elysiumjs/core"
12
+ },
13
+ "homepage": "https://github.com/na2axl/elysiumjs/tree/main/@elysiumjs/core#readme",
14
+ "bugs": {
15
+ "url": "https://github.com/na2axl/elysiumjs/issues"
11
16
  },
12
- "homepage": "https://github.com/workbud/elysium",
13
17
  "scripts": {
14
18
  "format": "prettier --write \"./src/**/*.ts\"",
15
19
  "clean": "rimraf dist",
@@ -17,7 +21,9 @@
17
21
  "build:ts": "tsc",
18
22
  "build": "bun run clean && bun run build:js && bun run build:ts",
19
23
  "typecheck": "tsc --noEmit",
20
- "prepublishOnly": "bun run build"
24
+ "prepublishOnly": "bun run build",
25
+ "test": "bun test",
26
+ "test:coverage": "bun test --coverage"
21
27
  },
22
28
  "keywords": [
23
29
  "elysium",
@@ -29,6 +35,10 @@
29
35
  "module": "dist/index.js",
30
36
  "type": "module",
31
37
  "types": "dist/index.d.ts",
38
+ "files": [
39
+ "dist",
40
+ "CHANGELOG.md"
41
+ ],
32
42
  "sideEffects": [
33
43
  "./src/index.ts"
34
44
  ],
package/tsconfig.json DELETED
@@ -1,12 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.base.json",
3
- "compilerOptions": {
4
- "outDir": "dist",
5
- "declaration": true,
6
- "emitDeclarationOnly": true,
7
- "declarationMap": false,
8
- "declarationDir": "dist",
9
- "rootDir": "src"
10
- },
11
- "include": ["src"]
12
- }