@elysiumjs/core 0.11.2 → 0.13.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 +12 -0
- package/README.md +20 -0
- package/dist/container.d.ts +32 -1
- package/dist/http.d.ts +7 -4
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -2
- package/dist/service.d.ts +56 -1
- package/dist/token.d.ts +37 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @elysiumjs/core
|
|
2
2
|
|
|
3
|
+
## 0.13.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#38](https://github.com/na2axl/elysiumjs/pull/38) [`c8d9f53`](https://github.com/na2axl/elysiumjs/commit/c8d9f534eef32621eae121f7008f97dc24808141) Thanks [@na2axl](https://github.com/na2axl)! - A validator's `validate` and `authorize` may return a `DomainError`, answered as is with its own status, code and extensions. `false` still answers a generic 400 from `validate` and a 401 from `authorize`.
|
|
8
|
+
|
|
9
|
+
## 0.12.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- [#32](https://github.com/na2axl/elysiumjs/pull/32) [`651a755`](https://github.com/na2axl/elysiumjs/commit/651a7557173ce2c6155bcfa8d90cf388ac419821) Thanks [@na2axl](https://github.com/na2axl)! - Add service tags (multi-binding). `Service.tag<T>(name)` creates a tag, `@Service.register({ tags })` (or `tags` on a factory definition) adds a service to it, and `@Service.injectAll(tag)`, `Service.getAll(tag)` and `Service.resolveAll(tag)` give every tagged service of the application in registration order. Tagged services follow the usual scope rules, and an overridden service keeps its tags.
|
|
14
|
+
|
|
3
15
|
## 0.11.2
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -137,6 +137,26 @@ A service has one of three scopes:
|
|
|
137
137
|
|
|
138
138
|
A request-scoped service resolves only inside a request: resolving it elsewhere throws `OutsideRequestScopeError`. Code outside a route, such as a job or a test, opens a scope with `Service.runInRequestScope(fn)`; every request-scoped service resolved inside `fn`, including after an `await`, is shared for that run. A singleton, or a controller with the default server scope, cannot depend on a request-scoped service, since it would keep the first request's instance: Elysium throws `ScopeMismatchError`. Use a request-scoped controller (`scope: HttpControllerScope.REQUEST`), or inject the service in the route handler's parameters.
|
|
139
139
|
|
|
140
|
+
### Tags
|
|
141
|
+
|
|
142
|
+
A tag groups services: each service registered with it is injected together with the others. Use it for extension points that modules contribute to, such as health checks or permission rules.
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
import { Service } from '@elysiumjs/core';
|
|
146
|
+
|
|
147
|
+
export const HealthChecks = Service.tag<HealthCheck>('health.checks');
|
|
148
|
+
|
|
149
|
+
@Service.register({ tags: [HealthChecks] })
|
|
150
|
+
export class DatabaseCheck implements HealthCheck {}
|
|
151
|
+
|
|
152
|
+
@Service.register()
|
|
153
|
+
export class Health {
|
|
154
|
+
constructor(@Service.injectAll(HealthChecks) private readonly checks: HealthCheck[]) {}
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
`@Service.injectAll(tag)` and `Service.getAll(tag)` give the tagged services in registration order, and an empty array when there is none; `Service.resolveAll(tag)` also waits for their `onInit`. A module receives the tagged services of every module of the application, including the ones it does not import, while getting one of them by its class still follows the module's imports and exports. Scopes apply to each tagged service as they do to `@Service.inject()`.
|
|
159
|
+
|
|
140
160
|
## Lazy boot
|
|
141
161
|
|
|
142
162
|
A CLI command boots lazily: running `app:cmd` initialises the command's own module and the modules it imports, not the whole graph. `help <cmd>` is always lazy, even for commands with `static boot = true`.
|
package/dist/container.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Class } from 'type-fest';
|
|
2
2
|
import type { ModuleNode } from './module-graph';
|
|
3
3
|
import type { ServiceDefinition } from './service';
|
|
4
|
-
import type { ServiceToken } from './token';
|
|
4
|
+
import type { Injection, ServiceTag, ServiceToken } from './token';
|
|
5
5
|
import { ServiceScope } from './service';
|
|
6
6
|
/**
|
|
7
7
|
* A service registered in the container.
|
|
@@ -32,6 +32,8 @@ export type ContainerEntry = {
|
|
|
32
32
|
};
|
|
33
33
|
/** The service class, when the entry was registered from one. Used to await its dependencies. */
|
|
34
34
|
cls?: Class<any>;
|
|
35
|
+
/** The names of the tags the service is registered with. */
|
|
36
|
+
tags?: readonly string[];
|
|
35
37
|
};
|
|
36
38
|
/**
|
|
37
39
|
* Supplies the visibility rules of the module graph to the container.
|
|
@@ -42,6 +44,8 @@ export type VisibilityResolver = {
|
|
|
42
44
|
visibleOwners(node: ModuleNode): readonly ModuleNode[];
|
|
43
45
|
/** The service tokens `owner` exports, or `true` when it exports all its services. */
|
|
44
46
|
exportsOf(owner: ModuleNode): readonly ServiceToken[] | true;
|
|
47
|
+
/** The active modules of the application, whose tagged services every module sees. */
|
|
48
|
+
readonly nodes?: readonly ModuleNode[];
|
|
45
49
|
};
|
|
46
50
|
/**
|
|
47
51
|
* The scoped service container: a global scope plus one scope per module.
|
|
@@ -96,6 +100,33 @@ export declare namespace Container {
|
|
|
96
100
|
entry: ContainerEntry;
|
|
97
101
|
owner: ModuleNode | null;
|
|
98
102
|
} | null;
|
|
103
|
+
/**
|
|
104
|
+
* Finds the services registered with a tag, in registration order: the global ones, the ones of
|
|
105
|
+
* `from`, and the ones of every active module of the application. A tag is an extension point
|
|
106
|
+
* open to the whole application, so a module sees the tagged services of modules it does not
|
|
107
|
+
* import; looking them up by token still follows the visibility rules.
|
|
108
|
+
*/
|
|
109
|
+
function lookupAll(tag: ServiceTag, from: ModuleNode | undefined): ContainerEntry[];
|
|
110
|
+
/**
|
|
111
|
+
* Gets every service registered with a tag, from the current module context.
|
|
112
|
+
* @returns The services, in registration order; empty when none has the tag.
|
|
113
|
+
* @throws {ServiceNotReadyError} If a service or its module is still initialising.
|
|
114
|
+
*/
|
|
115
|
+
function getAll(tag: ServiceTag): unknown[];
|
|
116
|
+
/**
|
|
117
|
+
* Like {@link getAll}, but awaits the `onInit` of each service and of its owning module.
|
|
118
|
+
*/
|
|
119
|
+
function resolveAll(tag: ServiceTag): Promise<unknown[]>;
|
|
120
|
+
/**
|
|
121
|
+
* Awaits a found entry like {@link resolve}: its module's initialisation, its dependencies and
|
|
122
|
+
* its `onInit`.
|
|
123
|
+
*/
|
|
124
|
+
function resolveEntry(entry: ContainerEntry): Promise<unknown>;
|
|
125
|
+
/**
|
|
126
|
+
* Awaits what a parameter receives from the container: a service, `null` when it is not
|
|
127
|
+
* visible, or every service of a tag.
|
|
128
|
+
*/
|
|
129
|
+
function resolveInjection(injection: Injection): Promise<unknown>;
|
|
99
130
|
/**
|
|
100
131
|
* Gets a service visible from the current module context.
|
|
101
132
|
* @returns The service, or `null` if none is visible.
|
package/dist/http.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ import type { FilterClass } from './filter';
|
|
|
6
6
|
import type { InterceptorClass } from './interceptor';
|
|
7
7
|
import type { Module } from './module';
|
|
8
8
|
import { Elysia } from 'elysia';
|
|
9
|
+
import { DomainError } from './problem';
|
|
9
10
|
/**
|
|
10
11
|
* A route is a string that starts with a forward slash.
|
|
11
12
|
* @author Axel Nana <axel.nana@workbud.com>
|
|
@@ -34,15 +35,17 @@ export type ValidatorClass<T extends TSchema = TSchema> = {
|
|
|
34
35
|
/**
|
|
35
36
|
* Whether the request should be authorized before validation. If the request is not authorized,
|
|
36
37
|
* the validation will be skipped.
|
|
38
|
+
* @returns `true` to go on, `false` to answer 401, or the `DomainError` to answer instead.
|
|
37
39
|
*/
|
|
38
|
-
authorize(ctx: Context<Static<T>>): Promise<boolean>;
|
|
40
|
+
authorize(ctx: Context<Static<T>>): Promise<boolean | DomainError>;
|
|
39
41
|
/**
|
|
40
42
|
* Processes custom validation logic. This method is called after the validation schema is checked
|
|
41
43
|
* against the input data. So it's safe to assume that the schema is valid.
|
|
42
|
-
* @param
|
|
43
|
-
* @returns
|
|
44
|
+
* @param ctx The request context, with the input data as its body.
|
|
45
|
+
* @returns `true` when the input data is valid, `false` to answer a generic 400, or the
|
|
46
|
+
* `DomainError` to answer instead, with its own status, code and extensions.
|
|
44
47
|
*/
|
|
45
|
-
validate(ctx: Context<Static<T>>): Promise<boolean>;
|
|
48
|
+
validate(ctx: Context<Static<T>>): Promise<boolean | DomainError>;
|
|
46
49
|
};
|
|
47
50
|
/**
|
|
48
51
|
* Metadata for the HTTP context.
|
package/dist/index.d.ts
CHANGED
|
@@ -22,6 +22,7 @@ export { type TestingApp, type TestingAppOptions, type TestingHttpApp, type Test
|
|
|
22
22
|
export { type HttpTestClient, type RequestHandler, type TestResponse, createHttpClient, TestRequest, UnexpectedStatusError } from './http-client';
|
|
23
23
|
export { type OutputLevel, type ServeHandle, type ShutdownSignal, Runtime, RuntimeNotConfiguredError, RuntimeNotSupportedError } from './runtime';
|
|
24
24
|
export { type ServiceDefinition, type ServiceFactoryDefinition, ServiceScope, Service } from './service';
|
|
25
|
+
export { type ServiceTag } from './token';
|
|
25
26
|
export { type WampRegistrationOptions, type WampSubscriptionOptions, type WampProps, type WampRegistrationHandlerArgs, type WampSubscriptionHandlerArgs, type WampRegistrationHandler, type WampSubscriptionHandler, type WampClientProps, type WampModuleConfig, Wamp, WampModule } from './wamp';
|
|
26
27
|
export { type WS, type WSError, Websocket } from './websocket';
|
|
27
28
|
/**
|
package/dist/index.js
CHANGED
|
@@ -17,5 +17,5 @@ var __legacyDecorateClassTS=function(decorators,target,key,desc){var c=arguments
|
|
|
17
17
|
`)}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(", ")}
|
|
18
18
|
`);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=["⠋","⠙","⠹","⠸","⠼","⠴","⠦","⠧","⠇","⠏"];#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)}}import{t}from"elysia";import Value from"typebox/value";var SECRET=Symbol.for("elysium:Secret"),REDACTED="[secret]";class Secret{#value;constructor(value){this.#value=value}reveal(){return this.#value}toString(){return REDACTED}toJSON(){return REDACTED}[Symbol.for("nodejs.util.inspect.custom")](){return REDACTED}static schema(schema){return{...schema??t.String(),[SECRET]:!0}}static isSchema(schema){return typeof schema==="object"&&schema!==null&&SECRET in schema}}class InvalidEnvironmentError extends ElysiumError{issues;constructor(issues){super(`Invalid environment configuration:
|
|
19
19
|
${issues.map((i)=>` - ${i.key}: ${i.message}`).join(`
|
|
20
|
-
`)}`);this.issues=issues}}class ConflictingEnvironmentSchemaError extends ElysiumError{key;owners;constructor(key,owners){super(`The environment variable "${key}" is declared differently by ${owners[0]} and ${owners[1]}. Declare it once, or with the same schema.`);this.key=key;this.owners=owners}}var mergeEnvironmentSchemas=(appSchema,graph)=>{let sources=[];if(appSchema)sources.push(["the application",appSchema]);for(let node of graph?.nodes??[])if(node.props.envSchema)sources.push([`module ${node.id}`,node.props.envSchema]);if(sources.length===0)return;let properties={},owners={};for(let[owner,schema]of sources){let props=schema.properties??{};for(let[key,property]of Object.entries(props)){if(key in properties&&JSON.stringify(properties[key])!==JSON.stringify(property))throw new ConflictingEnvironmentSchemaError(key,[owners[key],owner]);properties[key]=property,owners[key]=owner}}return t.Object(properties)},validateEnvironment=(schema,raw)=>{let value=Value.Convert(schema,Value.Default(schema,{...raw})),issues=[];for(let error of Value.Errors(schema,value)){let missing=error.params?.requiredProperties;if(Array.isArray(missing)){for(let key of missing)issues.push({key,message:"is required"});continue}let key=error.instancePath.replace(/^\//,"").split("/")[0]||"(root)";issues.push({key,message:error.message})}if(issues.length>0)throw new InvalidEnvironmentError(issues);let clean=Value.Clean(schema,value);for(let[key,property]of Object.entries(schema.properties))if(Secret.isSchema(property)&&clean[key]!==void 0)clean[key]=new Secret(clean[key]);return clean};import{AsyncLocalStorage as AsyncLocalStorage2}from"node:async_hooks";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)}};import{AsyncLocalStorage}from"node:async_hooks";var storage=new AsyncLocalStorage,ModuleContext;((ModuleContext)=>{function run(node,fn){return storage.run(node,fn)}ModuleContext.run=run;function outside(fn){return storage.exit(fn)}ModuleContext.outside=outside;function current(){return storage.getStore()}ModuleContext.current=current})(ModuleContext||={});import{isString}from"radash";var tokenOf=(service)=>typeof service==="string"?service:Metadata.get(Symbols.serviceOptions,service)?.name??service,tokenLabel=(token)=>typeof token==="string"?token:token.name||"<anonymous class>";var ServiceScope;((ServiceScope)=>{ServiceScope[ServiceScope.SINGLETON=0]="SINGLETON";ServiceScope[ServiceScope.FACTORY=1]="FACTORY";ServiceScope[ServiceScope.REQUEST=2]="REQUEST"})(ServiceScope||={});var isClass=(value)=>typeof value==="function"&&value.prototype&&value.prototype.constructor===value,nameOf=(service)=>tokenOf(service),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)??[],type=(Metadata.get("design:paramtypes",target,propertyKey)??[])[parameterIndex],token=service??(type&&type!==Object?type:void 0);if(token===void 0){let owner=typeof target==="function"?target:target.constructor;throw new ElysiumError(`Cannot tell which service to inject in parameter ${parameterIndex} of ${tokenLabel(owner)}${propertyKey?`.${String(propertyKey)}`:""}: pass its name or class to @Service.inject().`)}services.push({token,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=Container.makeSingleton(service);return Service.instance(name??service,s)};function factory(serviceOrName,nameOrFactory){let serviceName=isString(serviceOrName)?serviceOrName:nameOrFactory??tokenOf(serviceOrName),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.ownersOf=(service)=>Container.ownersOf(nameOf(service)),Service.isConstructed=(service)=>{let token=tokenOf(service);return Container.constructed().some((entry)=>entry.token===token)},Service.runInRequestScope=(fn)=>Container.runInRequestScope(fn),Service.disposeRequestScope=(key)=>Container.disposeRequestScope(key),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=(service,scope,create,prebuilt)=>{let token=tokenOf(service);Container.add({token,name:tokenLabel(token),scope,global:!0,owner:null,create,initialised:prebuilt!==void 0,...prebuilt?{instance:prebuilt.instance}:{}})}})(Service||={});var GLOBAL_OWNER="<global>",isDefined=(value)=>value!==void 0,globalScope=new Map,moduleScopes=new Map,requestStorage=new AsyncLocalStorage2,keyedRequestStores=new WeakMap,keyedDisposals=new WeakMap,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()}},destroyStore=async(store)=>{let entries=[...store.entries()].reverse();store.clear();for(let[entry,{instance}]of entries){let onDestroy=instance?.onDestroy;if(typeof onDestroy!=="function")continue;try{await inOwnerContext(entry.owner,()=>onDestroy.call(instance))}catch(error){getLogger().error("Error while destroying",{target:`service ${entry.name}`,error})}}},instantiate=(entry)=>{if(entry.scope===2){let captive=building.findLast((e)=>e.scope!==1);if(captive?.scope===0)throw new ScopeMismatchError(captive.name,entry.name);let store=requestStorage.getStore();if(!store)throw new OutsideRequestScopeError(entry.name);let existing=store.get(entry);if(existing)return existing;let instance=construct(entry),instantiation={instance,ready:void 0},ready=runOnInit(entry,instance);if(ready)instantiation.ready=ready.then(()=>{instantiation.ready=void 0},(error)=>{if(store.get(entry)===instantiation)store.delete(entry);throw error}),instantiation.ready.catch(()=>{});return store.set(entry,instantiation),instantiation}if(entry.scope===1){let instance=construct(entry),ready=runOnInit(entry,instance);return ready?.catch(()=>{}),{instance,ready}}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!==0||!("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,entry.token)}let{instance,ready}=instantiate(entry);if(ready)throw new ServiceNotReadyError(entry.name,entry.owner?.id??null,entry.token);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===0)return;let dependencies=Metadata.get(Symbols.services,entry.cls)??[],found=inOwnerContext(entry.owner,()=>dependencies.map((dependency)=>Container.lookup(dependency.token,ModuleContext.current())));for(let dependency of found){if(!dependency)continue;if(dependency.entry.scope===0)await entryValueAsync(dependency.entry,trail);else if(dependency.entry.scope===2&&!trail.some((e)=>e.scope===0)&&requestStorage.getStore())await entryValueAsync(dependency.entry,trail);else await prepare(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,token=options.name??(isClass?def:"");if(overridden.has(token))return token;return add({token,name:tokenLabel(token),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,tokenLabel(def.name))),initialised:!1}),token}Container.register=register;function add(entry){if(entry.global||!entry.owner){let existing=globalScope.get(entry.token);if(existing)throw new DuplicateGlobalServiceError(entry.name,[ownerId(existing.owner),ownerId(entry.owner)]);globalScope.set(entry.token,entry);return}let scope=moduleScopes.get(entry.owner);if(!scope)scope=new Map,moduleScopes.set(entry.owner,scope);if(scope.has(entry.token))throw new ElysiumError(`Service "${entry.name}" is declared twice in module ${entry.owner.id}.`);scope.set(entry.token,entry)}Container.add=add;function setOverrides(tokens){overridden=new Set([...tokens??[]].map(tokenOf))}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(service,from){let name=tokenOf(service),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.some((token)=>tokenOf(token)===name)){let entry=moduleScopes.get(imported)?.get(name);if(entry)return found(entry)}}}return found(globalScope.get(name))}Container.lookup=lookup;function get(service){let found=lookup(service,ModuleContext.current());return found?entryValue(found.entry):null}Container.get=get;function getGlobal(service){let entry=globalScope.get(tokenOf(service));return entry?entryValue(entry):null}Container.getGlobal=getGlobal;async function resolve(service,requester){let found=lookup(service,ModuleContext.current());if(!found)throw notFound(service,requester);return entryValueAsync(found.entry)}Container.resolve=resolve;function prepareClass(cls){return prepare({token:cls,name:cls.name,scope:1,global:!1,owner:ModuleContext.current()??null,create:()=>{return},initialised:!1,cls},[])}Container.prepareClass=prepareClass;async function resolveVisible(service){let found=lookup(service,ModuleContext.current());return found?entryValueAsync(found.entry):null}Container.resolveVisible=resolveVisible;function make(cls){let dependencies=Metadata.get(Symbols.services,cls)??[],params=[];for(let dependency of dependencies)params[dependency.index]=require2(dependency.token,cls.name);return new cls(...params)}Container.make=make;function makeSingleton(cls){let marker={token:cls,name:cls.name,scope:0};building.push(marker);try{return make(cls)}finally{building.pop()}}Container.makeSingleton=makeSingleton;function require2(service,requester){let found=lookup(service,ModuleContext.current());if(!found)throw notFound(service,requester);return entryValue(found.entry)}function notFound(service,requester){let token=tokenOf(service),name=tokenLabel(token),skipped=skippedLookup?.(token);if(skipped)return new EnvironmentSkippedModuleError(name,requester,skipped.module,skipped.environment);let hidden=[...moduleScopes.values()].map((scope)=>scope.get(token)).find((entry)=>entry!==void 0);return hidden?new ServiceNotVisibleError(name,requester,ModuleContext.current()?.id??null,hidden.owner?.id??null):new ServiceNotFoundError(name)}Container.ownersOf=(service)=>{let token=tokenOf(service);return[...moduleScopes].filter(([,scope])=>scope.has(token)).map(([node])=>node)},Container.hasGlobal=(service)=>globalScope.has(tokenOf(service)),Container.globalNames=()=>[...globalScope.keys()].filter((token)=>typeof token==="string"),Container.removeGlobal=(service)=>{globalScope.delete(tokenOf(service))},Container.runInRequestScope=(fn,key)=>{let store=key?keyedRequestStores.get(key):requestStorage.getStore(),owned=!key&&!store;if(!store){if(store=new Map,key)keyedRequestStores.set(key,store)}if(!owned)return requestStorage.run(store,fn);let created=store,result;try{result=requestStorage.run(created,fn)}catch(error){throw destroyStore(created),error}if(!isPromise(result))return destroyStore(created),result;return result.finally(()=>destroyStore(created))},Container.disposeRequestScope=(key)=>{let store=keyedRequestStores.get(key),running=keyedDisposals.get(key);if(!store)return running??Promise.resolve();keyedRequestStores.delete(key);let disposal=Promise.all([running,destroyStore(store)]).then(()=>{return});return keyedDisposals.set(key,disposal),disposal},Container.inRequestScope=()=>requestStorage.getStore()!==void 0,Container.clearGlobal=()=>{globalScope.clear()},Container.constructed=()=>constructedEntries.filter(({entry})=>!entry.initFailure).map(({entry,instance})=>({token:entry.token,name:entry.name,instance,owner:entry.owner}));function forgetFailures(nodes){let within=nodes?new Set(nodes):void 0,scopes=within?[globalScope,...[...within].map((node)=>moduleScopes.get(node)).filter(isDefined)]:[globalScope,...moduleScopes.values()],failed=new Set;for(let entries of scopes)for(let entry of entries.values()){if(!entry.initFailure)continue;if(within&&!(entry.owner&&within.has(entry.owner)))continue;failed.add(entry)}for(let entry of failed)delete entry.instance,delete entry.initPromise,delete entry.initFailure,entry.initialised=!1;let kept=constructedEntries.filter(({entry})=>!failed.has(entry));constructedEntries.splice(0,constructedEntries.length,...kept)}Container.forgetFailures=forgetFailures;function reset(){globalScope.clear(),moduleScopes.clear(),constructedEntries.length=0}Container.reset=reset})(Container||={});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||={});import{HTTPError,t as t2}from"elysia";import{status as httpStatus}from"http-status";var PROBLEM_CONTENT_TYPE="application/problem+json",statusTitle=(status)=>{let name=httpStatus[status];if(typeof name==="string")return name;return status>=500?"Server Error":status>=400?"Client Error":"Error"},ELYSIUM_PROBLEM_CODES={BAD_REQUEST:"BAD_REQUEST",VALIDATION_FAILED:"VALIDATION_FAILED",UNAUTHENTICATED:"UNAUTHENTICATED",FORBIDDEN:"FORBIDDEN",NOT_FOUND:"NOT_FOUND",CONFLICT:"CONFLICT",PAYLOAD_TOO_LARGE:"PAYLOAD_TOO_LARGE",RATE_LIMITED:"RATE_LIMITED",INTERNAL_ERROR:"INTERNAL_ERROR",CSRF_TOKEN_MISMATCH:"CSRF_TOKEN_MISMATCH",INVALID_SIGNATURE:"INVALID_SIGNATURE",PASSKEY_VERIFICATION_FAILED:"PASSKEY_VERIFICATION_FAILED",OAUTH_CALLBACK_FAILED:"OAUTH_CALLBACK_FAILED",IDEMPOTENCY_KEY_REQUIRED:"IDEMPOTENCY_KEY_REQUIRED",IDEMPOTENCY_KEY_REUSED:"IDEMPOTENCY_KEY_REUSED",FLOW_EXPIRED:"FLOW_EXPIRED",FLOW_COMPLETED:"FLOW_COMPLETED",FLOW_UNEXPECTED_STEP:"FLOW_UNEXPECTED_STEP",FLOW_STEP_IN_PROGRESS:"FLOW_STEP_IN_PROGRESS"},problemType=(code,base)=>{let slug=code.toLowerCase().replaceAll("_","-");return base?`${base.replace(/\/+$/,"")}/${slug}`:slug};class DomainError extends HTTPError{static typeBase;code;status;detail;title;errors;retryAfterSeconds;extensions;headers;constructor(code,status,detail,options={}){super(detail??code,options.cause instanceof Error?options.cause:void 0);this.name=new.target.name,this.code=code,this.status=status,this.detail=detail,this.title=options.title??statusTitle(status),this.errors=options.errors,this.retryAfterSeconds=options.retryAfterSeconds,this.extensions=options.extensions??{},this.headers=options.retryAfterSeconds===void 0?void 0:{"retry-after":String(options.retryAfterSeconds)}}get type(){return problemType(this.code,DomainError.typeBase)}static for(code,status){return class extends DomainError{constructor(detail,options){super(code,status,detail,options)}}}extension(){return{...this.extensions,title:this.title,...this.retryAfterSeconds===void 0?{}:{retryAfter:this.retryAfterSeconds},...this.errors?{errors:[...this.errors]}:{}}}}var isDomainError=(value)=>value instanceof DomainError;class BadRequestError extends DomainError.for("BAD_REQUEST",400){}class ValidationFailedError extends DomainError.for("VALIDATION_FAILED",422){constructor(errors,detail="The request is invalid."){super(detail,{errors})}}class UnauthenticatedError extends DomainError.for("UNAUTHENTICATED",401){constructor(detail="Authentication is required."){super(detail)}}class ForbiddenError extends DomainError.for("FORBIDDEN",403){}class NotFoundError extends DomainError.for("NOT_FOUND",404){}class ConflictError extends DomainError.for("CONFLICT",409){}var isProblemDetails=(value)=>typeof value==="object"&&value!==null&&!(value instanceof Response)&&typeof value.status==="number"&&typeof value.title==="string"&&typeof value.type==="string",unwrapParseError=(error)=>{let wrapped=error;return wrapped?.code==="parse"&&wrapped.cause instanceof DomainError?wrapped.cause:error},escapePointer=(segment)=>segment.replaceAll("~","~0").replaceAll("/","~1"),pointerOf=(part,path,child)=>{let segments=path&&path!=="root"?path.split(/[./]/).filter(Boolean):[];if(child)segments.push(child);return["",part,...segments.map(escapePointer)].join("/")},validationErrors=(part,errors)=>{let result=[];for(let raw of errors){let error=raw,path=error.path??error.instancePath?.replace(/^\//,""),params=error.params??{},code=error.keyword??Object.keys(params)[0],missing=params.requiredProperties;if(Array.isArray(missing)&&missing.length>0){for(let property of missing)result.push({pointer:pointerOf(part,path,property),message:"is required",code});continue}result.push({pointer:pointerOf(part,path),message:error.message??"is invalid",...code?{code}:{}})}return result},REQUEST_ID=/^[\w.:-]{1,128}$/,requestIds=new WeakMap,requestIdOf=(request,make)=>{let id=requestIds.get(request);if(id===void 0){let header=request.headers.get("x-request-id");id=make?.(request)??(header&&REQUEST_ID.test(header)?header:crypto.randomUUID()),requestIds.set(request,id)}return id},toProblem=(error,context)=>{let request={...context.requestId!==void 0?{requestId:context.requestId}:{},...context.path!==void 0?{instance:context.path}:{}},make=(code,status,members={})=>{let{type:_t,title,status:_s,code:_c,detail,instance:_i,requestId:_r,...rest}=members;return{type:problemType(code,context.typeBase),title:typeof title==="string"?title:statusTitle(status),status,code,...typeof detail==="string"?{detail}:{},...rest,...request}};if(error instanceof DomainError){let showDetail=error.detail!==void 0&&(error.status<500||context.development);return make(error.code,error.status,{...error.extension(),...showDetail?{detail:error.detail}:{}})}let elysia=error,code=typeof elysia?.code==="string"?elysia.code:void 0;if(code==="validation"&&elysia.status!==500)return make(ELYSIUM_PROBLEM_CODES.VALIDATION_FAILED,context.validationStatus??422,{detail:`The request ${elysia.type??"input"} is invalid.`,errors:validationErrors(elysia.type??"body",elysia.all??[])});if(code==="not-found")return make(ELYSIUM_PROBLEM_CODES.NOT_FOUND,404);if(code==="parse"||code==="invalid-cookie-signature")return make(ELYSIUM_PROBLEM_CODES.BAD_REQUEST,400);let members={};if(context.development&&error instanceof Error){if(members.detail=error.message,error.stack)members.stack=error.stack.split(`
|
|
21
|
-
`).map((line)=>line.trim())}return make(ELYSIUM_PROBLEM_CODES.INTERNAL_ERROR,500,members)},problemResponse=(problem,headers)=>new Response(JSON.stringify(problem),{status:problem.status,headers:{...headers,"content-type":PROBLEM_CONTENT_TYPE}}),ProblemDetailsSchema=t2.Object({type:t2.String(),title:t2.String(),status:t2.Integer(),code:t2.String(),detail:t2.Optional(t2.String()),instance:t2.Optional(t2.String()),requestId:t2.Optional(t2.String()),retryAfter:t2.Optional(t2.Integer()),errors:t2.Optional(t2.Array(t2.Object({pointer:t2.String(),message:t2.String(),code:t2.Optional(t2.String())})))},{additionalProperties:!0,$id:"ProblemDetails",description:"RFC 9457 problem details"});var CATCH=Symbol("elysium:Catch"),Catch=(...errors)=>(target)=>{Metadata.define(CATCH,errors,target)},routeFilterLevels=new WeakMap,FILTER_LEVELS=Symbol("elysium:FilterLevels"),catches=(filter,error)=>{let errors=Metadata.get(CATCH,filter)??[];return errors.length===0||errors.some((cls)=>error instanceof cls)},instances=new WeakMap,instanceOf=(level,cls)=>{let built=instances.get(level);if(!built)instances.set(level,built=new Map);let filter=built.get(cls);if(!filter)filter=level.node?ModuleContext.run(level.node,()=>Service.make(cls)):Service.make(cls),built.set(cls,filter);return filter},runFilters=async(levels,error,ctx)=>{for(let level of levels)for(let cls of level.filters){if(!catches(cls,error))continue;try{let run=()=>instanceOf(level,cls).catch(error,ctx),result=await Container.runInRequestScope(()=>level.node?ModuleContext.run(level.node,run):run(),ctx.request??ctx);if(result===void 0)continue;return{response:isProblemDetails(result)?problemResponse(result):result}}catch(filterError){getLogger().error("An exception filter failed; the error goes to the next filter",{filter:cls.name,error:filterError})}}return};import{isEqual as isEqual2}from"radash";import{EventEmitter}from"node:events";class LifecycleBus extends EventEmitter{static instance=new LifecycleBus;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 getLifecycleListeners(cls){return Metadata.getOwn(Symbols.lifecycleListeners,cls)??[]}var recordListener=(target,method,mode,props)=>{let isStatic=typeof target==="function",owner=isStatic?target:target.constructor;Metadata.define(Symbols.lifecycleListeners,[...getLifecycleListeners(owner),{event:props.event,method,mode,prepend:props.prepend??!1,static:isStatic}],owner)},Lifecycle;((Lifecycle)=>{Lifecycle.emit=(event,data,source=null)=>{LifecycleBus.instance.emitEvent(event,data,source)};function on(event,handler){if(typeof handler==="function"){LifecycleBus.instance.on(event,handler);return}return function(target,propertyKey){recordListener(target,propertyKey,"on",{...handler,event})}}Lifecycle.on=on;function once(event,handler){if(typeof handler==="function"){LifecycleBus.instance.once(event,handler);return}return function(target,propertyKey){recordListener(target,propertyKey,"once",{...handler,event})}}Lifecycle.once=once,Lifecycle.off=(event)=>{LifecycleBus.instance.removeAllListeners(event)},Lifecycle.clear=()=>{LifecycleBus.instance.removeAllListeners()},Lifecycle.listen=({mode,event,...options})=>{if(mode==="on")return on(event,options);else if(mode==="once")return once(event,options);throw new ElysiumError("Unknown mode provided to @listen. Use either 'on' or 'once'.")}})(Lifecycle||={});var 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(cls,cls.name))[method](data)})}catch(e){LifecycleBus.instance.emitError(e)}}};function wireLifecycleListeners(node,owned,services){let plan=[...new Set(owned)].flatMap((cls)=>getLifecycleListeners(cls).map((meta)=>{if(!meta.static&&!services.has(cls))throw new LifecycleListenerNotSupportedError(cls.name,String(meta.method),node.id);return{meta,wrapper:handlerFor(node,cls,meta)}})),bus=LifecycleBus.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;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)=>tokenOf(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,tokenOfDefinition=(def)=>typeof def==="function"?tokenOf(def):def.name;class Kernel{moduleGraph;instances=new Map;registered=new Map;initialised=new Set;initialising=new Set;initOrder=[];inFlight=new Map;failures=new Map;rejected=new Set;ownClasses=new Map;wired=[];shutdownPromise;constructor(moduleGraph){this.moduleGraph=moduleGraph}get graph(){return this.moduleGraph}serviceClasses(node){return[...this.ownClasses.get(node)?.services??[]]}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({token: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.rejected.add(node)),this.inFlight.set(node,pending);return pending}resetFailures(){this.failures.clear();for(let node of this.rejected)this.inFlight.delete(node);this.rejected.clear()}async bootEager(){for(let node of this.graph.nodes)await this.initModule(node);for(let node of this.graph.nodes)for(let{token,scope}of this.registered.get(node)??[]){if(scope!==0)continue;await ModuleContext.run(node,()=>Service.resolve(token))}}async prepare(cls,owner){await this.initModule(owner);let dependencies=Metadata.get(Symbols.services,cls)??[];for(let{token}of dependencies){if(ModuleContext.run(owner,()=>Container.lookup(token,owner))?.entry.scope===2)continue;await ModuleContext.run(owner,()=>Container.resolve(token,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),listeners=own?wireLifecycleListeners(node,own.owned,own.services):[];this.wired.push(...listeners);let fail=(error)=>{throw this.initialising.delete(node),this.unwire(listeners),error};this.initialising.add(node);let result;try{result=ModuleContext.run(node,()=>instance.onInit())}catch(error){fail(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,fail);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.rejected.add(node)),this.inFlight.set(node,pending),!1}unwire(listeners){let removed=new Set(listeners);for(let{event,wrapper}of listeners)LifecycleBus.instance.removeListener(event,wrapper);let kept=this.wired.filter((listener)=>!removed.has(listener));this.wired.splice(0,this.wired.length,...kept)}async runShutdown(){for(let{event,wrapper}of this.wired.splice(0))LifecycleBus.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(tokenOfDefinition(def),cls.name)}let{environment}=this.graph;return(token)=>{let module=owners.get(token);return module===void 0?null:{module,environment}}}}import{ElysiaStatus}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 Container.runInRequestScope(()=>Application.context.run(new Map([["tenant",context.tenant],["http:context",context]]),()=>node?ModuleContext.run(node,chain):chain()),context.request??context)},middlewareStops=new WeakSet,makeMiddlewares=(middlewares,node)=>middlewares.map((middleware)=>node?ModuleContext.run(node,()=>Service.make(middleware)):Service.make(middleware)),ERROR_HOOKS=Symbol("elysium:middleware-error-hooks"),enlist=(context,middlewares,node)=>{let list=context[ERROR_HOOKS]??=[];if(!list.some((entry)=>entry.middlewares===middlewares))list.push({middlewares,node})},runErrorMiddlewares=async(context)=>{let list=context?.[ERROR_HOOKS]??[];for(let{middlewares,node}of[...list].reverse()){let hooks=middlewares.filter((middleware)=>middleware.onError!==Middleware.prototype.onError);if(hooks.length===0)continue;await executeMiddlewareChain(hooks.map((middleware)=>({onError:async(c)=>{try{await middleware.onError(c)}catch(error){getLogger().error("An onError middleware failed",{error})}}})),context,"onError",node)}},applyMiddlewares=(middlewares,plugin,node)=>{let built,mi=()=>built??=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);let stop=result instanceof ElysiaStatus?result:c.status(result instanceof Response?result.status:c.set.status??200,result);throw middlewareStops.add(stop),stop});else plugin.onRequest((c)=>executeMiddlewareChain(mi(),c,"onRequest",node)),plugin.onTransform((c)=>executeMiddlewareChain(mi(),c,"onTransform",node));plugin.onBeforeHandle((c)=>(enlist(c,mi(),node),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){}onError(_ctx){}onAfterResponse(_ctx){}}import{ParseError}from"elysia";var DEFAULT_BODY_LIMIT=1048576;class PayloadTooLargeError extends DomainError.for("PAYLOAD_TOO_LARGE",413){limit;constructor(limit){super(`The request body is larger than ${limit} bytes.`);this.limit=limit}}class InvalidCorsOptionsError extends ElysiumError{constructor(){super('CORS cannot allow every origin ("*") with credentials. List the allowed origins instead.')}}var DEFAULT_METHODS=["GET","HEAD","PUT","PATCH","POST","DELETE"],originAllowed=(cors,origin,request)=>{let{origin:allowed}=cors;if(allowed==="*")return!0;if(typeof allowed==="string")return allowed===origin;if(Array.isArray(allowed))return allowed.includes(origin);if(allowed instanceof RegExp)return allowed.test(origin);return allowed(origin,request)},headerValue=(option,fallback,defaults)=>{if(typeof option==="string")return option;if(option===!0||option===void 0&&defaults)return fallback;return},clientAddress=(request,trustProxy)=>{let direct=Runtime.find()?.clientAddress?.(request);if(!trustProxy)return direct;let forwarded=(request.headers.get("x-forwarded-for")??"").split(",").map((hop)=>hop.trim()).filter(Boolean);if(forwarded.length===0)return direct;let chain=direct?[...forwarded,direct]:forwarded;if(trustProxy===!0)return chain[0];if(typeof trustProxy==="number")return chain[Math.max(0,chain.length-1-trustProxy)];let index=chain.length-1;while(index>0&&trustProxy.includes(chain[index]))index--;return chain[index]},isHttps=(request,trustProxy)=>{if(trustProxy){let proto=request.headers.get("x-forwarded-proto")?.split(",")[0]?.trim();if(proto)return proto==="https"}return new URL(request.url).protocol==="https:"},readLimited=async(body,limit)=>{let reader=body.getReader(),chunks=[],size=0;while(!0){let{done,value}=await reader.read();if(done)break;if(size+=value.byteLength,size>limit)throw await reader.cancel(),new PayloadTooLargeError(limit);chunks.push(value)}let bytes=new Uint8Array(new ArrayBuffer(size)),offset=0;for(let chunk of chunks)bytes.set(chunk,offset),offset+=chunk.byteLength;return bytes},formToObject=(form)=>{let result={};for(let key of new Set(form.keys())){let values=form.getAll(key);result[key]=values.length>1?values:values[0]}return result},parseLimited=async(request,contentType,limit)=>{let bytes=await readLimited(request.body,limit),text=()=>new TextDecoder().decode(bytes);if(contentType.startsWith("application/json")||contentType.endsWith("+json")){if(bytes.byteLength===0)return;try{return JSON.parse(text())}catch(error){throw new ParseError(error)}}if(contentType.startsWith("text/"))return text();if(contentType.startsWith("application/x-www-form-urlencoded"))return formToObject(new URLSearchParams(text()));return formToObject(await new Response(bytes,{headers:{"content-type":contentType}}).formData())},LIMITED_TYPES=/^(application\/(json|x-www-form-urlencoded)|text\/|multipart\/form-data)|\+json/,applyHttpSecurity=(app,options={})=>{let{cors,headers=!1,bodyLimit=DEFAULT_BODY_LIMIT,trustProxy=!1}=options;if(cors&&cors.origin==="*"&&cors.credentials)throw new InvalidCorsOptionsError;if(app.derive("global",({request})=>({ip:clientAddress(request,trustProxy)})),app.onRequest(({request,set})=>{if(headers){let all=headers===!0?{}:headers,defaults=headers===!0,values={"x-content-type-options":all.xContentTypeOptions===!1?void 0:all.xContentTypeOptions||defaults?"nosniff":void 0,"referrer-policy":headerValue(all.referrerPolicy,"no-referrer",defaults),"cross-origin-opener-policy":headerValue(all.crossOriginOpenerPolicy,"same-origin",defaults),"cross-origin-resource-policy":headerValue(all.crossOriginResourcePolicy,"same-origin",defaults),"x-frame-options":headerValue(all.xFrameOptions,"DENY",defaults),"content-security-policy":all.contentSecurityPolicy||void 0};if(isHttps(request,trustProxy))values["strict-transport-security"]=headerValue(all.strictTransportSecurity,"max-age=15552000; includeSubDomains",defaults);for(let[name,value]of Object.entries(values))if(value!==void 0)set.headers[name]=value}if(cors){let origin=request.headers.get("origin");if(set.headers.vary=set.headers.vary?`${set.headers.vary}, Origin`:"Origin",origin&&originAllowed(cors,origin,request)){if(set.headers["access-control-allow-origin"]=cors.origin==="*"&&!cors.credentials?"*":origin,cors.credentials)set.headers["access-control-allow-credentials"]="true";if(cors.exposedHeaders?.length)set.headers["access-control-expose-headers"]=cors.exposedHeaders.join(", ");let asked=request.headers.get("access-control-request-method");if(request.method==="OPTIONS"&&asked){let preflight=new Headers(set.headers);preflight.set("access-control-allow-methods",(cors.methods??DEFAULT_METHODS).join(", "));let allowedHeaders=cors.allowedHeaders?.join(", ")??request.headers.get("access-control-request-headers");if(allowedHeaders)preflight.set("access-control-allow-headers",allowedHeaders);if(cors.maxAge!==void 0)preflight.set("access-control-max-age",String(cors.maxAge));return new Response(null,{status:204,headers:preflight})}}}if(bodyLimit!==!1){if(Number(request.headers.get("content-length"))>bodyLimit)throw new PayloadTooLargeError(bodyLimit)}}),bodyLimit!==!1)app.onParse("global",(context)=>{let ctx=context,{request}=ctx,contentType=ctx.contentType??request.headers.get("content-type")??"";if(!request.body||request.headers.has("content-length"))return;if(!LIMITED_TYPES.test(contentType))return;return parseLimited(request,contentType,bodyLimit)})};class UnexpectedStatusError extends ElysiumError{expected;actual;body;constructor(expected,actual,body){super(`Expected status ${expected}, got ${actual}: ${body||"(empty body)"}`);this.expected=expected;this.actual=actual;this.body=body;this.name="UnexpectedStatusError"}}var toTestResponse=(response)=>Object.assign(response,{expectStatus:async(status)=>{if(response.status!==status)throw new UnexpectedStatusError(status,response.status,await response.clone().text());return response}});class TestRequest{#handle;#method;#url;#headers=new Headers;#body;constructor(handle,method,path,baseUrl){this.#handle=handle,this.#method=method,this.#url=new URL(path,baseUrl)}header(name,value){return this.#headers.set(name,value),this}headers(headers){for(let[name,value]of Object.entries(headers))this.#headers.set(name,value);return this}query(params){for(let[name,value]of Object.entries(params))for(let item of Array.isArray(value)?value:[value])this.#url.searchParams.append(name,String(item));return this}json(body){if(this.#body=JSON.stringify(body),!this.#headers.has("content-type"))this.#headers.set("content-type","application/json");return this}form(fields){return this.#body=new URLSearchParams(fields),this}body(body){return this.#body=body,this}toRequest(){let init={method:this.#method,headers:this.#headers,body:this.#body};if(this.#body instanceof ReadableStream)init.duplex="half";return new Request(this.#url,init)}async send(){return toTestResponse(await this.#handle(this.toRequest()))}then(onfulfilled,onrejected){return this.send().then(onfulfilled,onrejected)}}var createHttpClient=(handle,baseUrl="http://localhost")=>{let request=(method,path)=>new TestRequest(handle,method,path,baseUrl);return{get:(path)=>request("GET",path),post:(path)=>request("POST",path),put:(path)=>request("PUT",path),patch:(path)=>request("PATCH",path),delete:(path)=>request("DELETE",path),head:(path)=>request("HEAD",path),options:(path)=>request("OPTIONS",path),request,send:async(req)=>toTestResponse(await handle(req))}};var resetContainer=()=>{Container.reset(),Container.setOverrides(null),Container.setVisibility(null),Container.setModuleInitHook(null),Container.setSkippedLookup(null)},applyOverrides=(overrides)=>{let entries=overrides instanceof Map?[...overrides]:Object.entries(overrides);Container.setOverrides(entries.map(([token])=>token));for(let[token,value]of entries)Service.instance(token,value)};async function createTestingHttpApp(options){let{app:App,overrides={}}=options;resetContainer();try{let app=new App;applyOverrides(overrides);let elysia=await app.build(),graph=app.kernel.graph;return{app,kernel:app.kernel,http:createHttpClient((request)=>elysia.handle(request)),get:async(name,from)=>{await app.boot();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 app graph.`);return ModuleContext.run(node,resolve)},shutdown:async()=>{await app.kernel.shutdown(),resetContainer()}}}catch(error){throw resetContainer(),error}}async function createTestingApp(options){let{modules,overrides={},environment="test",boot="lazy"}=options;resetContainer();try{let graph=buildModuleGraph(modules,environment),kernel=new Kernel(graph);if(applyOverrides(overrides),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 AsyncLocalStorage3;class Application extends InteractsWithConsole{#elysia;#debug;#routeFilters=new Map;#appFilters={filters:[],node:null};#kernel;#booting;#removeShutdownHandler;#serving;_appContextStorage=new AsyncLocalStorage3;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??[]],pick=(key)=>props[key]??ownMetadata?.[key]??parentMetadata?.[key],runtime=pick("runtime"),kept={env:pick("env"),filters:pick("filters"),interceptors:pick("interceptors"),rateLimit:pick("rateLimit")},withoutModules=({modules:_modules,runtime:_runtime,env:_env,filters:_filters,interceptors:_interceptors,rateLimit:_rateLimit,...rest})=>rest;props={...deepMerge(withoutModules(props),withoutModules(ownMetadata??{}),withoutModules(parentMetadata??{}),{openapi:!1}),modules,...runtime?{runtime}:{},...Object.fromEntries(Object.entries(kept).filter(([,value])=>value!==void 0))},Metadata.define(Symbols.app,props,target)}}constructor(){super();let{debug,runtime}=Metadata.get(Symbols.app,this.constructor)??{};if(this.#debug=debug??!1,runtime)Runtime.use(runtime);initEnv(Runtime.get().env()),Service.remove("elysium.app"),Service.instance("elysium.app",this)}static start(){return new this().run()}static testing(options){return"app"in options?createTestingHttpApp(options):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=Runtime.get().argv()){this.installSignalHandlers();try{let{modules}=this.props();return this.#kernel=new Kernel(buildModuleGraph(modules??[])),this.configureEnvironment(),await this.dispatch(argv)}catch(error){return this.fail(error)}}props(){return Metadata.get(Symbols.app,this.constructor)??{}}static moduleGraph(environment){let{modules}=Metadata.get(Symbols.app,this)??{};return buildModuleGraph(modules??[],environment)}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 Lifecycle.emit("elysium:error",error),await this.#kernel?.shutdown(),this.exit(1)}exit(code){return this.removeSignalHandlers(),Runtime.get().exit(code)}removeSignalHandlers(){this.#removeShutdownHandler?.(),this.#removeShutdownHandler=void 0}installSignalHandlers(){this.removeSignalHandlers();let onSignal=async()=>{this.removeSignalHandlers();let code=0;try{let serving=this.#serving;this.#serving=void 0,await serving?.close(),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.#removeShutdownHandler=Runtime.get().onShutdown(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.boot();Lifecycle.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 build(){let{modules}=this.props();return this.#kernel=new Kernel(buildModuleGraph(modules??[])),this.configureEnvironment(),await this.bootKernel(!1),await this.buildServer(!0),this.#elysia}configureEnvironment(){let schema=mergeEnvironmentSchemas(this.props().env,this.kernel.graph);if(!schema)return;Env.clear(),initEnv(validateEnvironment(schema,Runtime.get().env()))}boot(){if(this.#booting)return this.#booting;let attempt=this.kernel.bootEager().catch((error)=>{if(this.#booting===attempt)this.#booting=void 0,this.kernel.resetFailures(),Container.forgetFailures(this.kernel.graph.nodes);throw error});return this.#booting=attempt,attempt}async commandServe(){let{server}=this.props();await this.bootKernel(!0),await this.buildServer(!1);let runtime=Runtime.get();this.#serving=runtime.serve(this.#elysia,server?.port??(parseInt(runtime.env().PORT??"",10)||3000))??void 0}async buildServer(bootOnFirstRequest){let{server,openapi,plugins}=this.props();if(this.#elysia=new Elysia(server),bootOnFirstRequest){let booted=!1;this.#elysia.onRequest(()=>{if(booted)return;return this.boot().then(()=>{booted=!0})})}if(this.#elysia.onRequest((c)=>{if(this.isDebug)getLogger().debug("Incoming request",{method:c.request.method,url:c.request.url})}),Lifecycle.emit("elysium:server:before-init",this,this),this.#elysia.derive("global",(ctx)=>({tenant:ctx.tenant??ctx.request.headers.get("x-tenant-id")??"public"})),applyHttpSecurity(this.#elysia,this.props().http),DomainError.typeBase=this.props().problems?.typeBase,openapi){let workers=Runtime.find()?.name==="workers";this.#elysia.use(openapiPlugin(workers?{embedSpec:!0,...openapi}: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(),this.indexRouteFilters(),Lifecycle.emit("elysium:server:after-init",this,this)}indexRouteFilters(){this.#appFilters={filters:this.props().filters??[],node:null},this.#routeFilters.clear();for(let route of this.#elysia.routes){let levels=routeFilterLevels.get(route.handler);if(levels?.length)this.#routeFilters.set(`${route.method} ${route.path}`,levels)}}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("global",(e)=>this.handleServerError(e)).onAfterResponse("global",(c)=>Container.disposeRequestScope(c.request??c)).onStart(async(elysia)=>{await this.onStart(elysia),Lifecycle.emit("elysium:server:start",elysia,this)}).onStop(async(elysia)=>{await this.onStop(elysia),Lifecycle.emit("elysium:server:stop",elysia,this),this.removeSignalHandlers(),await this.#kernel?.shutdown(),this._appContextStorage.disable()})}async handleServerError(e){let error=unwrapParseError(e.error);if(middlewareStops.has(error))return;if(await runErrorMiddlewares(e),error instanceof ElysiaStatus2){if(Number(error.status)>=500&&!await this.reportError(e))return;return error}let route=e.route,levels=[...e[FILTER_LEVELS]??(route?this.#routeFilters.get(`${e.request.method} ${route}`):void 0)??[],this.#appFilters],filtered=await runFilters(levels,error,e);if(filtered)return filtered.response;let{problems}=this.props(),{requestId,...options}=problems??{},problem=toProblem(error,{...options,path:e.request?new URL(e.request.url).pathname:void 0,requestId:e.request?requestIdOf(e.request,requestId):void 0,development:currentEnvironment()==="development"});if(problem.status>=500&&!await this.reportError(e))return;let headers=Object.fromEntries(Object.entries({...e.set?.headers,...error.headers}).map(([name,value])=>[name,String(value)]));return problemResponse(problem,headers)}async reportError(e){if(!await this.onError(e))return!1;return Lifecycle.emit("elysium:error",e),this.trace({name:"HttpError",message:"Unexpected error while handling HTTP request",stack:e.error?.stack,...e.error}),!0}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}${"∙".repeat(width-command.length-args.length)} ${description}`)}}import{Elysia as Elysia2,sse as esse,t as t3}from"elysia";import{assign,isEmpty as isEmpty2,objectify}from"radash";var singletons=new WeakMap,build=(level,cls)=>{let make=()=>level.node?ModuleContext.run(level.node,()=>Service.make(cls)):Service.make(cls),scope=Metadata.get(Symbols.serviceOptions,cls)?.scope;if(scope===2||scope===1)return make();let built=singletons.get(level);if(!built)singletons.set(level,built=new Map);let instance=built.get(cls);if(!instance)built.set(cls,instance=make());return instance},runInterceptors=(levels,ctx,handler)=>{let chain=levels.flatMap((level)=>level.interceptors.map((cls)=>({level,cls}))),step=(index)=>{if(index===chain.length)return handler();let{level,cls}=chain[index],called=!1,next=()=>{if(called)return Promise.reject(new InterceptorNextCalledTwiceError(cls.name));return called=!0,step(index+1)},run=()=>build(level,cls).intercept(ctx,next);return level.node?ModuleContext.run(level.node,run):run()};return step(0)};class TimingInterceptor{async intercept(ctx,next){let start=performance.now();try{return await next()}finally{let entry=`handler;dur=${(performance.now()-start).toFixed(1)}`,current=ctx.set.headers["server-timing"];ctx.set.headers["server-timing"]=current?`${current}, ${entry}`:entry}}}class TransformInterceptor{static with(transform){return class{async intercept(ctx,next){return transform(await next(),ctx)}}}}class MemoryRateLimitStore{#windows=new Map;#nextSweep=0;async hit(key,windowMs){let now=Date.now();this.#sweep(now);let current=this.#windows.get(key);if(current&¤t.resetAt>now)return current.count++,{...current};let opened={count:1,resetAt:now+windowMs};return this.#windows.set(key,opened),{...opened}}#sweep(now){if(now<this.#nextSweep)return;this.#nextSweep=now+1000;for(let[key,window]of this.#windows)if(window.resetAt<=now)this.#windows.delete(key)}}class TooManyRequestsError extends DomainError.for("RATE_LIMITED",429){retryAfter;constructor(retryAfter){super(`Too many requests. Try again in ${retryAfter} seconds.`,{retryAfterSeconds:retryAfter});this.retryAfter=retryAfter}}var RATE_LIMIT=Symbol.for("elysium:RateLimit"),RateLimit=(limit)=>(target,propertyKey,_descriptor)=>{Metadata.define(RATE_LIMIT,limit,target,propertyKey)},rateLimitOf=(controller,method)=>Metadata.get(RATE_LIMIT,controller.prototype,method)??Metadata.get(RATE_LIMIT,controller),defaultStore=new MemoryRateLimitStore,enforceRateLimit=async(limit,appStore,context)=>{let name=limit.name??"default",key=limit.key?await limit.key(context):`${context.ip??"unknown"}:${context.route}`,hit=await(limit.store??appStore??defaultStore).hit(`rl:${name}:${key}`,limit.window),headers=context.set.headers;if(headers["ratelimit-limit"]=String(limit.max),headers["ratelimit-policy"]=`${limit.max};w=${Math.ceil(limit.window/1000)}`,"limited"in hit){if(!hit.limited)return;let retryAfter=Math.ceil(limit.window/1000);throw headers["ratelimit-remaining"]="0",headers["retry-after"]=String(retryAfter),new TooManyRequestsError(retryAfter)}let reset=Math.max(0,Math.ceil((hit.resetAt-Date.now())/1000));if(headers["ratelimit-remaining"]=String(Math.max(0,limit.max-hit.count)),headers["ratelimit-reset"]=String(reset),hit.count>limit.max)throw headers["retry-after"]=String(reset),new TooManyRequestsError(reset)};import{ElysiaStatus as ElysiaStatus3}from"elysia";var HIDDEN=Symbol.for("elysium:Hidden"),cloneSchema=(schema,extra={},drop=[])=>{let descriptors=Object.getOwnPropertyDescriptors(schema);for(let key of drop)delete descriptors[key];let clone=Object.defineProperties({},descriptors);for(let key of Reflect.ownKeys(extra))Object.defineProperty(clone,key,{value:extra[key],enumerable:typeof key==="string",writable:!0,configurable:!0});return clone},isObjectSchema=(schema)=>schema.type==="object"&&typeof schema.properties==="object",isPlainObject=(value)=>{if(typeof value!=="object"||value===null)return!1;let proto=Object.getPrototypeOf(value);return proto===Object.prototype||proto===null},isStatusMap=(response)=>{let keys=Object.keys(response);return keys.length>0&&keys.every((key)=>/^\d{3}$/.test(key))},Resource;((Resource)=>(Resource.hidden=(schema)=>cloneSchema(schema,{[HIDDEN]:!0}),Resource.isHidden=(schema)=>typeof schema==="object"&&schema!==null&&(HIDDEN in schema),Resource.project=(schema)=>projectNode(schema),Resource.projectResponse=(response)=>{if(!isStatusMap(response))return Resource.project(response);let projected={};for(let[status,schema]of Object.entries(response))projected[Number(status)]=Resource.project(schema);return projected},Resource.successSchema=(response)=>{if(!isStatusMap(response))return response;if(response[200])return response[200];let status=Object.keys(response).map(Number).sort((a,b)=>a-b).find((code)=>code>=200&&code<300);return status===void 0?void 0:response[status]},Resource.serialize=(schema,value,options={})=>{let node=schema,fields=options.fields?.filter(Boolean);if(!fields?.length)return serializeNode(node,value);let target=fieldsTarget(node);if(!target)return serializeNode(node,value);let allowed=Object.keys(target.properties).filter((key)=>!Resource.isHidden(target.properties[key])),unknown=fields.filter((field)=>!allowed.includes(field));if(unknown.length>0)throw new BadRequestError("The requested fields are not part of this resource.",{errors:unknown.map((field)=>({pointer:"/query/fields",message:`unknown field "${field}"`,code:"fields"}))});let selected=new Set(fields);return serializeNode(replaceTarget(node,target,((object)=>cloneSchema(object,{properties:Object.fromEntries(Object.entries(object.properties).filter(([key])=>selected.has(key))),required:(object.required??[]).filter((key)=>selected.has(key))}))(target)),value)}))(Resource||={});var fieldsTarget=(schema)=>{if(schema.type==="array"&&schema.items&&isObjectSchema(schema.items))return schema.items;if(!isObjectSchema(schema))return;let data=schema.properties.data;if(data?.type==="array"&&data.items&&isObjectSchema(data.items))return data.items;return schema},replaceTarget=(schema,target,next)=>{if(schema===target)return next;if(schema.items===target)return cloneSchema(schema,{items:next});let data=schema.properties.data;return cloneSchema(schema,{properties:{...schema.properties,data:cloneSchema(data,{items:next})}})},projectNode=(schema)=>{let node="~codec"in schema?cloneSchema(schema,{},["~codec"]):schema;if(isObjectSchema(node)){let properties={};for(let[key,property]of Object.entries(node.properties))if(!Resource.isHidden(property))properties[key]=projectNode(property);node=cloneSchema(node,{properties,...node.required?{required:node.required.filter((key)=>(key in properties))}:{}})}if(node.items)node=cloneSchema(node,{items:projectNode(node.items)});if(node.anyOf)node=cloneSchema(node,{anyOf:node.anyOf.map(projectNode)});if(node.oneOf)node=cloneSchema(node,{oneOf:node.oneOf.map(projectNode)});return node},branchFor=(branches,value)=>{if(value===null)return branches.find((branch)=>branch.type==="null");if(Array.isArray(value))return branches.find((branch)=>branch.type==="array");if(typeof value==="object")return branches.find(isObjectSchema);return branches.find((branch)=>branch.type!=="null")},serializeNode=(schema,value)=>{if(value instanceof Date)return schema?.format==="date"?value.toISOString().slice(0,10):value.toISOString();if(typeof value==="object"&&value!==null&&!Array.isArray(value)&&!isPlainObject(value)&&typeof value.toJSON==="function")return serializeNode(schema,value.toJSON());if(!schema)return value;let union=schema.anyOf??schema.oneOf;if(union)return serializeNode(branchFor(union,value),value);if(Array.isArray(value))return schema.type==="array"?value.map((item)=>serializeNode(schema.items,item)):value;if(isObjectSchema(schema)&&typeof value==="object"&&value!==null){let source=value,result={};for(let[key,property]of Object.entries(schema.properties)){if(Resource.isHidden(property)||source[key]===void 0)continue;result[key]=serializeNode(property,source[key])}if(schema.additionalProperties!==void 0&&schema.additionalProperties!==!1){let extra=typeof schema.additionalProperties==="object"?schema.additionalProperties:void 0;for(let[key,item]of Object.entries(source))if(!(key in schema.properties)&&item!==void 0)result[key]=serializeNode(extra,item)}return result}return value},isSerializable=(value)=>!(value instanceof Response)&&!(value instanceof ElysiaStatus3)&&!(value instanceof ReadableStream)&&!(value instanceof Blob)&&!(typeof value==="object"&&value!==null&&(Symbol.asyncIterator in value)),readFields=(query)=>{let raw=query?.fields;if(raw===void 0||raw===null||raw==="")return;return(Array.isArray(raw)?raw:[raw]).flatMap((value)=>String(value).split(",")).map((field)=>field.trim())};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)=>Lifecycle.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,serialize,filters,interceptors}=props,metadata=Metadata.get(Symbols.http,target.constructor)??[];metadata.push({name:String(props.propertyKey),path,method,body,params,query,customDecorators,rawContext,handler,middlewares,response,operationId,description,services,transactional,serialize,filters,interceptors}),Metadata.define(Symbols.http,metadata,target.constructor)},HttpControllerScope;((HttpControllerScope)=>{HttpControllerScope[HttpControllerScope.SERVER=0]="SERVER";HttpControllerScope[HttpControllerScope.REQUEST=1]="REQUEST"})(HttpControllerScope||={});var moduleInterceptorLevels=new WeakMap,moduleInterceptorLevel=(node)=>{let level=moduleInterceptorLevels.get(node);if(!level)moduleInterceptorLevels.set(node,level={interceptors:node.props.interceptors??[],node});return level},appInterceptorLevels=new WeakMap,appInterceptorLevel=()=>{let app=Service.get("elysium.app");if(!app)return{interceptors:[],node:null};let level=appInterceptorLevels.get(app);if(!level){let interceptors=Metadata.get(Symbols.app,app.constructor)?.interceptors??[];appInterceptorLevels.set(app,level={interceptors,node:null})}return level},appRateLimit=()=>{let app=Service.get("elysium.app");return app?Metadata.get(Symbols.app,app.constructor)?.rateLimit:void 0},appDefaultRateLimit=()=>{let options=appRateLimit();return options?.window!==void 0&&options.max!==void 0?options:void 0},joinPath=(base,path)=>`/${[base,path].map((part)=>part.replace(/^\/+|\/+$/g,"")).filter(Boolean).join("/")}`,moduleFilterLevels=new WeakMap,moduleFilterLevel=(node)=>{let level=moduleFilterLevels.get(node);if(!level)moduleFilterLevels.set(node,level={filters:node.props.filters??[],node});return level},Http;((Http)=>(Http.describe=(controller)=>{let props=Metadata.get("http:controller",controller);if(!props)return;let metadata=Metadata.get(Symbols.http,controller)??[];return{path:props.path??"/",routes:metadata.map((route)=>{let sse=route.method==="elysium:SSE";return{name:route.name,method:sse?"GET":route.method.toUpperCase(),path:route.path,sse,body:route.body?.index,query:route.query?.index,params:route.params.map(({slug,index})=>({slug,index}))}})}},Http.controller=(path="/",options={})=>{let props={...options,path};return 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}`}),controllerFilters={filters:props.filters??[],node},moduleFilters=moduleFilterLevel(node),outer,outerInterceptors=()=>outer??=[appInterceptorLevel(),moduleInterceptorLevel(node),{interceptors:props.interceptors??[],node}].filter((level)=>level.interceptors.length>0),getController=(()=>{if(props.scope===0){let controller;return()=>()=>controller??=ModuleContext.run(node,()=>Container.makeSingleton(target))}else if(props.scope===1)return()=>()=>ModuleContext.run(node,()=>Service.make(target));else throw Error(`Invalid scope for controller: ${target.name}`)})();app.derive("local",()=>({controller:getController()}));let prepareController=props.scope===1?()=>ModuleContext.run(node,()=>Container.prepareClass(target)):null,onRequest=Metadata.get("http:onRequest",target)??{};if(onRequest.handler){let callOnRequest=(c)=>{let controller=getController()();return ModuleContext.run(node,()=>onRequest.handler.call(controller,c))};app.onRequest((c)=>Container.runInRequestScope(()=>prepareController?prepareController().then(()=>callOnRequest(c)):callOnRequest(c),c.request))}let middlewares=Metadata.get(Symbols.middlewares,target)??[];applyMiddlewares(middlewares,app,node);let metadata=Metadata.get(Symbols.http,target)??[];for(let route of metadata){let scopeKey=(c)=>c.request??c,getParameters=(c)=>Container.runInRequestScope(()=>readParameters(c),scopeKey(c)),readParameters=async(c)=>{let parameters=[];if(prepareController)await prepareController();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]=await ModuleContext.run(node,()=>Container.resolveVisible(service.token));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)=>Container.runInRequestScope(()=>Application.context.run(new Map([["tenant",c.tenant],["http:context",c]]),()=>isTransactional?getTransactionRunner()(body):body()),scopeKey(c)),callHandler=(c,args)=>ModuleContext.run(node,()=>route.handler.call(c.controller(),...args)),callForValue=async(c,args)=>{let result=await callHandler(c,args);if(result instanceof DomainError)throw result;return result},initTransactedHandler=(c)=>(...args)=>inRequestScope(c,()=>callForValue(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 Runtime.get().sleep(10)};let routeInterceptors={interceptors:route.interceptors??[],node},initInterceptedHandler=(c)=>(...args)=>inRequestScope(c,()=>runInterceptors([...outerInterceptors(),routeInterceptors],c,()=>callForValue(c,args))),successSchema=route.response&&route.serialize!==!1?Resource.successSchema(route.response):void 0,toBody=(c,result)=>{if(!successSchema||!isSerializable(result))return result;let fields=readFields(c.query),body=Resource.serialize(successSchema,result,{fields});if(!fields)return body;let status=typeof c.set.status==="number"?c.set.status:200;return Response.json(body,{status})};if(isValidator)return async function(c){if(await ModuleContext.run(node,()=>route.body.validator.validate(c))){let handler=initInterceptedHandler(c);return toBody(c,await handler(...await getParameters(c)))}throw new BadRequestError("The request body is invalid.")};return async function(c){let handler=initInterceptedHandler(c);return toBody(c,await handler(...await getParameters(c)))}},params=objectify(route.params??[],(p)=>p.slug,(p)=>p.schema),mi=makeMiddlewares(route.middlewares,node),routeName=`${route.method.toUpperCase()} ${joinPath(props.path,route.path)}`,limit,routeRateLimit=()=>limit??=rateLimitOf(target,route.name)??appDefaultRateLimit()??!1,handler=getHandler(),filterLevels=[{filters:route.filters??[],node},controllerFilters,moduleFilters].filter((level)=>level.filters.length>0);routeFilterLevels.set(handler,filterLevels),app.route.call(app,route.method,route.path,{async transform(c){if(c[FILTER_LEVELS]=filterLevels,isValidator&&!await ModuleContext.run(node,()=>route.body.validator.authorize(c)))throw c.status(401,"Unauthorized");return Container.runInRequestScope(()=>executeMiddlewareChain(mi,c,"onTransform",node),scopeKey(c))},async beforeHandle(c){enlist(c,mi,node);let limit=routeRateLimit();if(limit)await enforceRateLimit(limit,appRateLimit()?.store,{request:c.request,ip:c.ip,route:routeName,context:c,set:c.set});return Container.runInRequestScope(()=>executeMiddlewareChain(mi,c,"onBeforeHandle",node),scopeKey(c))},afterHandle(c){return Container.runInRequestScope(()=>executeMiddlewareChain(mi,c,"onAfterHandle",node),scopeKey(c))},afterResponse(c){return Container.runInRequestScope(()=>executeMiddlewareChain(mi,c,"onAfterResponse",node),scopeKey(c))},detail:{tags:props.tags,description:route.description,operationId:route.operationId},body:isValidator?route.body?.validator?.schema:route.body?.schema,response:route.response?Resource.projectResponse(route.response):void 0,params:isEmpty2(params)?void 0:t3.Object(params)},handler)}return app}Metadata.define("http:controller",props,target),Metadata.define(Symbols.elysiaPlugin,handleHttp,target)}},Http.sse=(path="/",options={})=>Http.custom("elysium:SSE",path,options),Http.get=(path="/",options={})=>Http.custom("GET",path,options),Http.post=(path="/",options={})=>Http.custom("POST",path,options),Http.put=(path="/",options={})=>Http.custom("PUT",path,options),Http.del=(path="/",options={})=>Http.custom("DELETE",path,options),Http.patch=(path="/",options={})=>Http.custom("PATCH",path,options),Http.head=(path="/",options={})=>Http.custom("HEAD",path,options),Http.options=(path="/",options={})=>Http.custom("OPTIONS",path,options),Http.trace=(path="/",options={})=>Http.custom("TRACE",path,options),Http.custom=(method,path="/",options={})=>function(target,propertyKey,descriptor){queueMicrotask(()=>{registerHttpRequestHandler({method,path,handler:descriptor.value,target,propertyKey,...options})})},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??t3.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||={});var userIdOf=(user)=>{if(typeof user==="string"||typeof user==="number")return String(user);if(typeof user!=="object"||user===null)return;let{id,sub}=user,value=id??sub;return value===void 0||value===null?void 0:String(value)},currentUserId=()=>{let http=Application.context.getStore()?.get("http:context");return userIdOf(http?.currentUser)};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{Elysia as Elysia3}from"elysia";import{omit as omit2}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,...omit2(config,["url","onOpen"]),onError(error){config.onError?.(error);let message=`An error occurred in WAMP connection ${name} (${config.url})`,reason=error;Lifecycle.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}),Lifecycle.emit("elysium:error",Error(`Failed to establish WAMP connection ${name} after ${attempts} attempts`))}return}if(closed)return;try{await config.onOpen?.()}catch(error){Lifecycle.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}),Lifecycle.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}),Lifecycle.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";import{websocket}from"elysia/websocket";var Websocket;((Websocket)=>(Websocket.controller=(path,options={})=>function(target){async function handleWebsocket(node){await nextTick();let controller,getController=()=>controller??=ModuleContext.run(node,()=>Service.make(target)),metadata=Metadata.get(Symbols.websocket,target)??{},inModule=(handler)=>(...args)=>ModuleContext.run(node,()=>handler?.apply(getController(),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)=>Lifecycle.emit("elysium:error",e.error),app=new Elysia4({name:`${node.id}:${target.name}`}).use(websocket()),middlewares=Metadata.get(Symbols.middlewares,target)??[];return applyMiddlewares(middlewares,app,node),app.ws(path,{open,close,message,drain,error,body:metadata.body,...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,BadRequestError,Catch,CircularDependencyError,Command,CommandArgumentType,ConflictError,ConflictingEnvironmentSchemaError,ConflictingModuleConfigError,ConsoleFormat,ConsoleLogger,DATABASE_TRANSACTION_SERVICE,DEFAULT_BODY_LIMIT,DomainError,DuplicateCommandError,DuplicateGlobalServiceError,DuplicateModuleNameError,ELYSIUM_PROBLEM_CODES,ElysiumError,Env,EnvironmentSkippedModuleError,ForbiddenError,Http,HttpControllerScope,InteractsWithConsole,InterceptorNextCalledTwiceError,InvalidCorsOptionsError,InvalidEnvironmentError,InvalidModuleConfigError,InvalidModuleError,KERNEL_SERVICE,Kernel,LOGGER_SERVICE,Lifecycle,LifecycleListenerNotSupportedError,MemoryRateLimitStore,Metadata,Middleware,MissingModuleConfigError,Module,ModuleContext,ModuleCycleError,NotFoundError,OutsideRequestScopeError,PROBLEM_CONTENT_TYPE,PayloadTooLargeError,ProblemDetailsSchema,RateLimit,RequestLoggerMiddleware,Resource,Runtime,RuntimeNotConfiguredError,RuntimeNotSupportedError,ScopeMismatchError,Secret,Service,ServiceNotFoundError,ServiceNotReadyError,ServiceNotVisibleError,ServiceScope,TestRequest,TimingInterceptor,TooManyRequestsError,TransformInterceptor,UnauthenticatedError,UnexpectedStatusError,ValidationFailedError,Wamp,WampModule,Websocket,clientAddress,createHttpClient,currentEnvironment,currentUserId,getLogger,isDomainError,isProblemDetails,mergeEnvironmentSchemas,needsDefaultAlias,problemResponse,problemType,requestIdOf,resetContainer,runAll,statusTitle,t4 as t,toProblem,userIdOf,validateEnvironment,validationErrors};
|
|
20
|
+
`)}`);this.issues=issues}}class ConflictingEnvironmentSchemaError extends ElysiumError{key;owners;constructor(key,owners){super(`The environment variable "${key}" is declared differently by ${owners[0]} and ${owners[1]}. Declare it once, or with the same schema.`);this.key=key;this.owners=owners}}var mergeEnvironmentSchemas=(appSchema,graph)=>{let sources=[];if(appSchema)sources.push(["the application",appSchema]);for(let node of graph?.nodes??[])if(node.props.envSchema)sources.push([`module ${node.id}`,node.props.envSchema]);if(sources.length===0)return;let properties={},owners={};for(let[owner,schema]of sources){let props=schema.properties??{};for(let[key,property]of Object.entries(props)){if(key in properties&&JSON.stringify(properties[key])!==JSON.stringify(property))throw new ConflictingEnvironmentSchemaError(key,[owners[key],owner]);properties[key]=property,owners[key]=owner}}return t.Object(properties)},validateEnvironment=(schema,raw)=>{let value=Value.Convert(schema,Value.Default(schema,{...raw})),issues=[];for(let error of Value.Errors(schema,value)){let missing=error.params?.requiredProperties;if(Array.isArray(missing)){for(let key of missing)issues.push({key,message:"is required"});continue}let key=error.instancePath.replace(/^\//,"").split("/")[0]||"(root)";issues.push({key,message:error.message})}if(issues.length>0)throw new InvalidEnvironmentError(issues);let clean=Value.Clean(schema,value);for(let[key,property]of Object.entries(schema.properties))if(Secret.isSchema(property)&&clean[key]!==void 0)clean[key]=new Secret(clean[key]);return clean};import{AsyncLocalStorage as AsyncLocalStorage2}from"node:async_hooks";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)}};import{AsyncLocalStorage}from"node:async_hooks";var storage=new AsyncLocalStorage,ModuleContext;((ModuleContext)=>{function run(node,fn){return storage.run(node,fn)}ModuleContext.run=run;function outside(fn){return storage.exit(fn)}ModuleContext.outside=outside;function current(){return storage.getStore()}ModuleContext.current=current})(ModuleContext||={});import{isString}from"radash";var tokenOf=(service)=>typeof service==="string"?service:Metadata.get(Symbols.serviceOptions,service)?.name??service,tokenLabel=(token)=>typeof token==="string"?token:token.name||"<anonymous class>",serviceTag=(name)=>Object.freeze({tag:name}),isServiceTag=(value)=>typeof value==="object"&&value!==null&&typeof value.tag==="string";var ServiceScope;((ServiceScope)=>{ServiceScope[ServiceScope.SINGLETON=0]="SINGLETON";ServiceScope[ServiceScope.FACTORY=1]="FACTORY";ServiceScope[ServiceScope.REQUEST=2]="REQUEST"})(ServiceScope||={});var isClass=(value)=>typeof value==="function"&&value.prototype&&value.prototype.constructor===value,nameOf=(service)=>tokenOf(service),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)??[],type=(Metadata.get("design:paramtypes",target,propertyKey)??[])[parameterIndex],token=service??(type&&type!==Object?type:void 0);if(token===void 0){let owner=typeof target==="function"?target:target.constructor;throw new ElysiumError(`Cannot tell which service to inject in parameter ${parameterIndex} of ${tokenLabel(owner)}${propertyKey?`.${String(propertyKey)}`:""}: pass its name or class to @Service.inject().`)}services.push({token,index:parameterIndex}),Metadata.define(Symbols.services,services,target,propertyKey)},Service.tag=(name)=>serviceTag(name),Service.injectAll=(tag)=>{if(!isServiceTag(tag))throw new ElysiumError("@Service.injectAll() expects a tag from Service.tag().");return function(target,propertyKey,parameterIndex){let services=Metadata.get(Symbols.services,target,propertyKey)??[];services.push({tag,index:parameterIndex}),Metadata.define(Symbols.services,services,target,propertyKey)}},Service.getAll=(tag)=>Container.getAll(tag),Service.resolveAll=(tag)=>Container.resolveAll(tag),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=Container.makeSingleton(service);return Service.instance(name??service,s)};function factory(serviceOrName,nameOrFactory){let serviceName=isString(serviceOrName)?serviceOrName:nameOrFactory??tokenOf(serviceOrName),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.ownersOf=(service)=>Container.ownersOf(nameOf(service)),Service.isConstructed=(service)=>{let token=tokenOf(service);return Container.constructed().some((entry)=>entry.token===token)},Service.runInRequestScope=(fn)=>Container.runInRequestScope(fn),Service.disposeRequestScope=(key)=>Container.disposeRequestScope(key),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=(service,scope,create,prebuilt)=>{let token=tokenOf(service);Container.add({token,name:tokenLabel(token),scope,global:!0,owner:null,create,initialised:prebuilt!==void 0,...prebuilt?{instance:prebuilt.instance}:{}})}})(Service||={});var GLOBAL_OWNER="<global>",isDefined=(value)=>value!==void 0,tagNames=(tags)=>[...new Set((tags??[]).map((tag)=>tag.tag))],indexTags=(entry,tags)=>{for(let tag of tags){let entries=tagged.get(tag)??[];if(!entries.includes(entry))entries.push(entry);tagged.set(tag,entries)}},unindexTags=(drop)=>{for(let[tag,entries]of tagged){let kept=entries.filter((entry)=>!drop(entry));if(kept.length>0)tagged.set(tag,kept);else tagged.delete(tag)}},globalScope=new Map,moduleScopes=new Map,tagged=new Map,requestStorage=new AsyncLocalStorage2,keyedRequestStores=new WeakMap,keyedDisposals=new WeakMap,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()}},destroyStore=async(store)=>{let entries=[...store.entries()].reverse();store.clear();for(let[entry,{instance}]of entries){let onDestroy=instance?.onDestroy;if(typeof onDestroy!=="function")continue;try{await inOwnerContext(entry.owner,()=>onDestroy.call(instance))}catch(error){getLogger().error("Error while destroying",{target:`service ${entry.name}`,error})}}},instantiate=(entry)=>{if(entry.scope===2){let captive=building.findLast((e)=>e.scope!==1);if(captive?.scope===0)throw new ScopeMismatchError(captive.name,entry.name);let store=requestStorage.getStore();if(!store)throw new OutsideRequestScopeError(entry.name);let existing=store.get(entry);if(existing)return existing;let instance=construct(entry),instantiation={instance,ready:void 0},ready=runOnInit(entry,instance);if(ready)instantiation.ready=ready.then(()=>{instantiation.ready=void 0},(error)=>{if(store.get(entry)===instantiation)store.delete(entry);throw error}),instantiation.ready.catch(()=>{});return store.set(entry,instantiation),instantiation}if(entry.scope===1){let instance=construct(entry),ready=runOnInit(entry,instance);return ready?.catch(()=>{}),{instance,ready}}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!==0||!("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,entry.token)}let{instance,ready}=instantiate(entry);if(ready)throw new ServiceNotReadyError(entry.name,entry.owner?.id??null,entry.token);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===0)return;let dependencies=Metadata.get(Symbols.services,entry.cls)??[],found=inOwnerContext(entry.owner,()=>{let from=ModuleContext.current();return dependencies.flatMap((dependency)=>("tag"in dependency)?Container.lookupAll(dependency.tag,from):[Container.lookup(dependency.token,from)?.entry].filter(isDefined))});for(let dependency of found)if(dependency.scope===0)await entryValueAsync(dependency,trail);else if(dependency.scope===2&&!trail.some((e)=>e.scope===0)&&requestStorage.getStore())await entryValueAsync(dependency,trail);else await prepare(dependency,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,token=options.name??(isClass?def:""),tags=tagNames(options.tags);if(overridden.has(token)){let override=globalScope.get(token);if(override)indexTags(override,tags);return token}return add({token,name:tokenLabel(token),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,tokenLabel(def.name))),initialised:!1,tags}),token}Container.register=register;function add(entry){if(entry.global||!entry.owner){let existing=globalScope.get(entry.token);if(existing)throw new DuplicateGlobalServiceError(entry.name,[ownerId(existing.owner),ownerId(entry.owner)]);globalScope.set(entry.token,entry),indexTags(entry,entry.tags??[]);return}let scope=moduleScopes.get(entry.owner);if(!scope)scope=new Map,moduleScopes.set(entry.owner,scope);if(scope.has(entry.token))throw new ElysiumError(`Service "${entry.name}" is declared twice in module ${entry.owner.id}.`);scope.set(entry.token,entry),indexTags(entry,entry.tags??[])}Container.add=add;function setOverrides(tokens){overridden=new Set([...tokens??[]].map(tokenOf))}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(service,from){let name=tokenOf(service),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.some((token)=>tokenOf(token)===name)){let entry=moduleScopes.get(imported)?.get(name);if(entry)return found(entry)}}}return found(globalScope.get(name))}Container.lookup=lookup;function lookupAll(tag,from){let nodes=new Set(visibility?.nodes??[]);return(tagged.get(tag.tag)??[]).filter((entry)=>entry.global||!entry.owner||entry.owner===from||nodes.has(entry.owner))}Container.lookupAll=lookupAll;function getAll(tag){return lookupAll(tag,ModuleContext.current()).map(entryValue)}Container.getAll=getAll;async function resolveAll(tag){let instances=[];for(let entry of lookupAll(tag,ModuleContext.current()))instances.push(await entryValueAsync(entry));return instances}Container.resolveAll=resolveAll;function resolveEntry(entry){return entryValueAsync(entry)}Container.resolveEntry=resolveEntry;function resolveInjection(injection){return"tag"in injection?resolveAll(injection.tag):resolveVisible(injection.token)}Container.resolveInjection=resolveInjection;function get(service){let found=lookup(service,ModuleContext.current());return found?entryValue(found.entry):null}Container.get=get;function getGlobal(service){let entry=globalScope.get(tokenOf(service));return entry?entryValue(entry):null}Container.getGlobal=getGlobal;async function resolve(service,requester){let found=lookup(service,ModuleContext.current());if(!found)throw notFound(service,requester);return entryValueAsync(found.entry)}Container.resolve=resolve;function prepareClass(cls){return prepare({token:cls,name:cls.name,scope:1,global:!1,owner:ModuleContext.current()??null,create:()=>{return},initialised:!1,cls},[])}Container.prepareClass=prepareClass;async function resolveVisible(service){let found=lookup(service,ModuleContext.current());return found?entryValueAsync(found.entry):null}Container.resolveVisible=resolveVisible;function make(cls){let dependencies=Metadata.get(Symbols.services,cls)??[],params=[];for(let dependency of dependencies)params[dependency.index]="tag"in dependency?getAll(dependency.tag):require2(dependency.token,cls.name);return new cls(...params)}Container.make=make;function makeSingleton(cls){let marker={token:cls,name:cls.name,scope:0};building.push(marker);try{return make(cls)}finally{building.pop()}}Container.makeSingleton=makeSingleton;function require2(service,requester){let found=lookup(service,ModuleContext.current());if(!found)throw notFound(service,requester);return entryValue(found.entry)}function notFound(service,requester){let token=tokenOf(service),name=tokenLabel(token),skipped=skippedLookup?.(token);if(skipped)return new EnvironmentSkippedModuleError(name,requester,skipped.module,skipped.environment);let hidden=[...moduleScopes.values()].map((scope)=>scope.get(token)).find((entry)=>entry!==void 0);return hidden?new ServiceNotVisibleError(name,requester,ModuleContext.current()?.id??null,hidden.owner?.id??null):new ServiceNotFoundError(name)}Container.ownersOf=(service)=>{let token=tokenOf(service);return[...moduleScopes].filter(([,scope])=>scope.has(token)).map(([node])=>node)},Container.hasGlobal=(service)=>globalScope.has(tokenOf(service)),Container.globalNames=()=>[...globalScope.keys()].filter((token)=>typeof token==="string"),Container.removeGlobal=(service)=>{let entry=globalScope.get(tokenOf(service));if(globalScope.delete(tokenOf(service)),entry)unindexTags((e)=>e===entry)},Container.runInRequestScope=(fn,key)=>{let store=key?keyedRequestStores.get(key):requestStorage.getStore(),owned=!key&&!store;if(!store){if(store=new Map,key)keyedRequestStores.set(key,store)}if(!owned)return requestStorage.run(store,fn);let created=store,result;try{result=requestStorage.run(created,fn)}catch(error){throw destroyStore(created),error}if(!isPromise(result))return destroyStore(created),result;return result.finally(()=>destroyStore(created))},Container.disposeRequestScope=(key)=>{let store=keyedRequestStores.get(key),running=keyedDisposals.get(key);if(!store)return running??Promise.resolve();keyedRequestStores.delete(key);let disposal=Promise.all([running,destroyStore(store)]).then(()=>{return});return keyedDisposals.set(key,disposal),disposal},Container.inRequestScope=()=>requestStorage.getStore()!==void 0,Container.clearGlobal=()=>{let entries=new Set(globalScope.values());globalScope.clear(),unindexTags((entry)=>entries.has(entry))},Container.constructed=()=>constructedEntries.filter(({entry})=>!entry.initFailure).map(({entry,instance})=>({token:entry.token,name:entry.name,instance,owner:entry.owner}));function forgetFailures(nodes){let within=nodes?new Set(nodes):void 0,scopes=within?[globalScope,...[...within].map((node)=>moduleScopes.get(node)).filter(isDefined)]:[globalScope,...moduleScopes.values()],failed=new Set;for(let entries of scopes)for(let entry of entries.values()){if(!entry.initFailure)continue;if(within&&!(entry.owner&&within.has(entry.owner)))continue;failed.add(entry)}for(let entry of failed)delete entry.instance,delete entry.initPromise,delete entry.initFailure,entry.initialised=!1;let kept=constructedEntries.filter(({entry})=>!failed.has(entry));constructedEntries.splice(0,constructedEntries.length,...kept)}Container.forgetFailures=forgetFailures;function reset(){globalScope.clear(),moduleScopes.clear(),tagged.clear(),constructedEntries.length=0}Container.reset=reset})(Container||={});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||={});import{HTTPError,t as t2}from"elysia";import{status as httpStatus}from"http-status";var PROBLEM_CONTENT_TYPE="application/problem+json",statusTitle=(status)=>{let name=httpStatus[status];if(typeof name==="string")return name;return status>=500?"Server Error":status>=400?"Client Error":"Error"},ELYSIUM_PROBLEM_CODES={BAD_REQUEST:"BAD_REQUEST",VALIDATION_FAILED:"VALIDATION_FAILED",UNAUTHENTICATED:"UNAUTHENTICATED",FORBIDDEN:"FORBIDDEN",NOT_FOUND:"NOT_FOUND",CONFLICT:"CONFLICT",PAYLOAD_TOO_LARGE:"PAYLOAD_TOO_LARGE",RATE_LIMITED:"RATE_LIMITED",INTERNAL_ERROR:"INTERNAL_ERROR",CSRF_TOKEN_MISMATCH:"CSRF_TOKEN_MISMATCH",INVALID_SIGNATURE:"INVALID_SIGNATURE",PASSKEY_VERIFICATION_FAILED:"PASSKEY_VERIFICATION_FAILED",OAUTH_CALLBACK_FAILED:"OAUTH_CALLBACK_FAILED",IDEMPOTENCY_KEY_REQUIRED:"IDEMPOTENCY_KEY_REQUIRED",IDEMPOTENCY_KEY_REUSED:"IDEMPOTENCY_KEY_REUSED",FLOW_EXPIRED:"FLOW_EXPIRED",FLOW_COMPLETED:"FLOW_COMPLETED",FLOW_UNEXPECTED_STEP:"FLOW_UNEXPECTED_STEP",FLOW_STEP_IN_PROGRESS:"FLOW_STEP_IN_PROGRESS"},problemType=(code,base)=>{let slug=code.toLowerCase().replaceAll("_","-");return base?`${base.replace(/\/+$/,"")}/${slug}`:slug};class DomainError extends HTTPError{static typeBase;code;status;detail;title;errors;retryAfterSeconds;extensions;headers;constructor(code,status,detail,options={}){super(detail??code,options.cause instanceof Error?options.cause:void 0);this.name=new.target.name,this.code=code,this.status=status,this.detail=detail,this.title=options.title??statusTitle(status),this.errors=options.errors,this.retryAfterSeconds=options.retryAfterSeconds,this.extensions=options.extensions??{},this.headers=options.retryAfterSeconds===void 0?void 0:{"retry-after":String(options.retryAfterSeconds)}}get type(){return problemType(this.code,DomainError.typeBase)}static for(code,status){return class extends DomainError{constructor(detail,options){super(code,status,detail,options)}}}extension(){return{...this.extensions,title:this.title,...this.retryAfterSeconds===void 0?{}:{retryAfter:this.retryAfterSeconds},...this.errors?{errors:[...this.errors]}:{}}}}var isDomainError=(value)=>value instanceof DomainError;class BadRequestError extends DomainError.for("BAD_REQUEST",400){}class ValidationFailedError extends DomainError.for("VALIDATION_FAILED",422){constructor(errors,detail="The request is invalid."){super(detail,{errors})}}class UnauthenticatedError extends DomainError.for("UNAUTHENTICATED",401){constructor(detail="Authentication is required."){super(detail)}}class ForbiddenError extends DomainError.for("FORBIDDEN",403){}class NotFoundError extends DomainError.for("NOT_FOUND",404){}class ConflictError extends DomainError.for("CONFLICT",409){}var isProblemDetails=(value)=>typeof value==="object"&&value!==null&&!(value instanceof Response)&&typeof value.status==="number"&&typeof value.title==="string"&&typeof value.type==="string",unwrapParseError=(error)=>{let wrapped=error;return wrapped?.code==="parse"&&wrapped.cause instanceof DomainError?wrapped.cause:error},escapePointer=(segment)=>segment.replaceAll("~","~0").replaceAll("/","~1"),pointerOf=(part,path,child)=>{let segments=path&&path!=="root"?path.split(/[./]/).filter(Boolean):[];if(child)segments.push(child);return["",part,...segments.map(escapePointer)].join("/")},validationErrors=(part,errors)=>{let result=[];for(let raw of errors){let error=raw,path=error.path??error.instancePath?.replace(/^\//,""),params=error.params??{},code=error.keyword??Object.keys(params)[0],missing=params.requiredProperties;if(Array.isArray(missing)&&missing.length>0){for(let property of missing)result.push({pointer:pointerOf(part,path,property),message:"is required",code});continue}result.push({pointer:pointerOf(part,path),message:error.message??"is invalid",...code?{code}:{}})}return result},REQUEST_ID=/^[\w.:-]{1,128}$/,requestIds=new WeakMap,requestIdOf=(request,make)=>{let id=requestIds.get(request);if(id===void 0){let header=request.headers.get("x-request-id");id=make?.(request)??(header&&REQUEST_ID.test(header)?header:crypto.randomUUID()),requestIds.set(request,id)}return id},toProblem=(error,context)=>{let request={...context.requestId!==void 0?{requestId:context.requestId}:{},...context.path!==void 0?{instance:context.path}:{}},make=(code,status,members={})=>{let{type:_t,title,status:_s,code:_c,detail,instance:_i,requestId:_r,...rest}=members;return{type:problemType(code,context.typeBase),title:typeof title==="string"?title:statusTitle(status),status,code,...typeof detail==="string"?{detail}:{},...rest,...request}};if(error instanceof DomainError){let showDetail=error.detail!==void 0&&(error.status<500||context.development);return make(error.code,error.status,{...error.extension(),...showDetail?{detail:error.detail}:{}})}let elysia=error,code=typeof elysia?.code==="string"?elysia.code:void 0;if(code==="validation"&&elysia.status!==500)return make(ELYSIUM_PROBLEM_CODES.VALIDATION_FAILED,context.validationStatus??422,{detail:`The request ${elysia.type??"input"} is invalid.`,errors:validationErrors(elysia.type??"body",elysia.all??[])});if(code==="not-found")return make(ELYSIUM_PROBLEM_CODES.NOT_FOUND,404);if(code==="parse"||code==="invalid-cookie-signature")return make(ELYSIUM_PROBLEM_CODES.BAD_REQUEST,400);let members={};if(context.development&&error instanceof Error){if(members.detail=error.message,error.stack)members.stack=error.stack.split(`
|
|
21
|
+
`).map((line)=>line.trim())}return make(ELYSIUM_PROBLEM_CODES.INTERNAL_ERROR,500,members)},problemResponse=(problem,headers)=>new Response(JSON.stringify(problem),{status:problem.status,headers:{...headers,"content-type":PROBLEM_CONTENT_TYPE}}),ProblemDetailsSchema=t2.Object({type:t2.String(),title:t2.String(),status:t2.Integer(),code:t2.String(),detail:t2.Optional(t2.String()),instance:t2.Optional(t2.String()),requestId:t2.Optional(t2.String()),retryAfter:t2.Optional(t2.Integer()),errors:t2.Optional(t2.Array(t2.Object({pointer:t2.String(),message:t2.String(),code:t2.Optional(t2.String())})))},{additionalProperties:!0,$id:"ProblemDetails",description:"RFC 9457 problem details"});var CATCH=Symbol("elysium:Catch"),Catch=(...errors)=>(target)=>{Metadata.define(CATCH,errors,target)},routeFilterLevels=new WeakMap,FILTER_LEVELS=Symbol("elysium:FilterLevels"),catches=(filter,error)=>{let errors=Metadata.get(CATCH,filter)??[];return errors.length===0||errors.some((cls)=>error instanceof cls)},instances=new WeakMap,instanceOf=(level,cls)=>{let built=instances.get(level);if(!built)instances.set(level,built=new Map);let filter=built.get(cls);if(!filter)filter=level.node?ModuleContext.run(level.node,()=>Service.make(cls)):Service.make(cls),built.set(cls,filter);return filter},runFilters=async(levels,error,ctx)=>{for(let level of levels)for(let cls of level.filters){if(!catches(cls,error))continue;try{let run=()=>instanceOf(level,cls).catch(error,ctx),result=await Container.runInRequestScope(()=>level.node?ModuleContext.run(level.node,run):run(),ctx.request??ctx);if(result===void 0)continue;return{response:isProblemDetails(result)?problemResponse(result):result}}catch(filterError){getLogger().error("An exception filter failed; the error goes to the next filter",{filter:cls.name,error:filterError})}}return};import{isEqual as isEqual2}from"radash";import{EventEmitter}from"node:events";class LifecycleBus extends EventEmitter{static instance=new LifecycleBus;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 getLifecycleListeners(cls){return Metadata.getOwn(Symbols.lifecycleListeners,cls)??[]}var recordListener=(target,method,mode,props)=>{let isStatic=typeof target==="function",owner=isStatic?target:target.constructor;Metadata.define(Symbols.lifecycleListeners,[...getLifecycleListeners(owner),{event:props.event,method,mode,prepend:props.prepend??!1,static:isStatic}],owner)},Lifecycle;((Lifecycle)=>{Lifecycle.emit=(event,data,source=null)=>{LifecycleBus.instance.emitEvent(event,data,source)};function on(event,handler){if(typeof handler==="function"){LifecycleBus.instance.on(event,handler);return}return function(target,propertyKey){recordListener(target,propertyKey,"on",{...handler,event})}}Lifecycle.on=on;function once(event,handler){if(typeof handler==="function"){LifecycleBus.instance.once(event,handler);return}return function(target,propertyKey){recordListener(target,propertyKey,"once",{...handler,event})}}Lifecycle.once=once,Lifecycle.off=(event)=>{LifecycleBus.instance.removeAllListeners(event)},Lifecycle.clear=()=>{LifecycleBus.instance.removeAllListeners()},Lifecycle.listen=({mode,event,...options})=>{if(mode==="on")return on(event,options);else if(mode==="once")return once(event,options);throw new ElysiumError("Unknown mode provided to @listen. Use either 'on' or 'once'.")}})(Lifecycle||={});var 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(cls,cls.name))[method](data)})}catch(e){LifecycleBus.instance.emitError(e)}}};function wireLifecycleListeners(node,owned,services){let plan=[...new Set(owned)].flatMap((cls)=>getLifecycleListeners(cls).map((meta)=>{if(!meta.static&&!services.has(cls))throw new LifecycleListenerNotSupportedError(cls.name,String(meta.method),node.id);return{meta,wrapper:handlerFor(node,cls,meta)}})),bus=LifecycleBus.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;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)=>tokenOf(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,tokenOfDefinition=(def)=>typeof def==="function"?tokenOf(def):def.name;class Kernel{moduleGraph;instances=new Map;registered=new Map;initialised=new Set;initialising=new Set;initOrder=[];inFlight=new Map;failures=new Map;rejected=new Set;ownClasses=new Map;wired=[];shutdownPromise;constructor(moduleGraph){this.moduleGraph=moduleGraph}get graph(){return this.moduleGraph}serviceClasses(node){return[...this.ownClasses.get(node)?.services??[]]}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({token: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.rejected.add(node)),this.inFlight.set(node,pending);return pending}resetFailures(){this.failures.clear();for(let node of this.rejected)this.inFlight.delete(node);this.rejected.clear()}async bootEager(){for(let node of this.graph.nodes)await this.initModule(node);for(let node of this.graph.nodes)for(let{token,scope}of this.registered.get(node)??[]){if(scope!==0)continue;await ModuleContext.run(node,()=>Service.resolve(token))}}async prepare(cls,owner){await this.initModule(owner);let dependencies=Metadata.get(Symbols.services,cls)??[];for(let dependency of dependencies){if("tag"in dependency){for(let entry of Container.lookupAll(dependency.tag,owner)){if(entry.scope===2)continue;await ModuleContext.run(owner,()=>Container.resolveEntry(entry))}continue}let{token}=dependency;if(ModuleContext.run(owner,()=>Container.lookup(token,owner))?.entry.scope===2)continue;await ModuleContext.run(owner,()=>Container.resolve(token,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),listeners=own?wireLifecycleListeners(node,own.owned,own.services):[];this.wired.push(...listeners);let fail=(error)=>{throw this.initialising.delete(node),this.unwire(listeners),error};this.initialising.add(node);let result;try{result=ModuleContext.run(node,()=>instance.onInit())}catch(error){fail(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,fail);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.rejected.add(node)),this.inFlight.set(node,pending),!1}unwire(listeners){let removed=new Set(listeners);for(let{event,wrapper}of listeners)LifecycleBus.instance.removeListener(event,wrapper);let kept=this.wired.filter((listener)=>!removed.has(listener));this.wired.splice(0,this.wired.length,...kept)}async runShutdown(){for(let{event,wrapper}of this.wired.splice(0))LifecycleBus.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(tokenOfDefinition(def),cls.name)}let{environment}=this.graph;return(token)=>{let module=owners.get(token);return module===void 0?null:{module,environment}}}}import{ElysiaStatus}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 Container.runInRequestScope(()=>Application.context.run(new Map([["tenant",context.tenant],["http:context",context]]),()=>node?ModuleContext.run(node,chain):chain()),context.request??context)},middlewareStops=new WeakSet,makeMiddlewares=(middlewares,node)=>middlewares.map((middleware)=>node?ModuleContext.run(node,()=>Service.make(middleware)):Service.make(middleware)),ERROR_HOOKS=Symbol("elysium:middleware-error-hooks"),enlist=(context,middlewares,node)=>{let list=context[ERROR_HOOKS]??=[];if(!list.some((entry)=>entry.middlewares===middlewares))list.push({middlewares,node})},runErrorMiddlewares=async(context)=>{let list=context?.[ERROR_HOOKS]??[];for(let{middlewares,node}of[...list].reverse()){let hooks=middlewares.filter((middleware)=>middleware.onError!==Middleware.prototype.onError);if(hooks.length===0)continue;await executeMiddlewareChain(hooks.map((middleware)=>({onError:async(c)=>{try{await middleware.onError(c)}catch(error){getLogger().error("An onError middleware failed",{error})}}})),context,"onError",node)}},applyMiddlewares=(middlewares,plugin,node)=>{let built,mi=()=>built??=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);let stop=result instanceof ElysiaStatus?result:c.status(result instanceof Response?result.status:c.set.status??200,result);throw middlewareStops.add(stop),stop});else plugin.onRequest((c)=>executeMiddlewareChain(mi(),c,"onRequest",node)),plugin.onTransform((c)=>executeMiddlewareChain(mi(),c,"onTransform",node));plugin.onBeforeHandle((c)=>(enlist(c,mi(),node),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){}onError(_ctx){}onAfterResponse(_ctx){}}import{ParseError}from"elysia";var DEFAULT_BODY_LIMIT=1048576;class PayloadTooLargeError extends DomainError.for("PAYLOAD_TOO_LARGE",413){limit;constructor(limit){super(`The request body is larger than ${limit} bytes.`);this.limit=limit}}class InvalidCorsOptionsError extends ElysiumError{constructor(){super('CORS cannot allow every origin ("*") with credentials. List the allowed origins instead.')}}var DEFAULT_METHODS=["GET","HEAD","PUT","PATCH","POST","DELETE"],originAllowed=(cors,origin,request)=>{let{origin:allowed}=cors;if(allowed==="*")return!0;if(typeof allowed==="string")return allowed===origin;if(Array.isArray(allowed))return allowed.includes(origin);if(allowed instanceof RegExp)return allowed.test(origin);return allowed(origin,request)},headerValue=(option,fallback,defaults)=>{if(typeof option==="string")return option;if(option===!0||option===void 0&&defaults)return fallback;return},clientAddress=(request,trustProxy)=>{let direct=Runtime.find()?.clientAddress?.(request);if(!trustProxy)return direct;let forwarded=(request.headers.get("x-forwarded-for")??"").split(",").map((hop)=>hop.trim()).filter(Boolean);if(forwarded.length===0)return direct;let chain=direct?[...forwarded,direct]:forwarded;if(trustProxy===!0)return chain[0];if(typeof trustProxy==="number")return chain[Math.max(0,chain.length-1-trustProxy)];let index=chain.length-1;while(index>0&&trustProxy.includes(chain[index]))index--;return chain[index]},isHttps=(request,trustProxy)=>{if(trustProxy){let proto=request.headers.get("x-forwarded-proto")?.split(",")[0]?.trim();if(proto)return proto==="https"}return new URL(request.url).protocol==="https:"},readLimited=async(body,limit)=>{let reader=body.getReader(),chunks=[],size=0;while(!0){let{done,value}=await reader.read();if(done)break;if(size+=value.byteLength,size>limit)throw await reader.cancel(),new PayloadTooLargeError(limit);chunks.push(value)}let bytes=new Uint8Array(new ArrayBuffer(size)),offset=0;for(let chunk of chunks)bytes.set(chunk,offset),offset+=chunk.byteLength;return bytes},formToObject=(form)=>{let result={};for(let key of new Set(form.keys())){let values=form.getAll(key);result[key]=values.length>1?values:values[0]}return result},parseLimited=async(request,contentType,limit)=>{let bytes=await readLimited(request.body,limit),text=()=>new TextDecoder().decode(bytes);if(contentType.startsWith("application/json")||contentType.endsWith("+json")){if(bytes.byteLength===0)return;try{return JSON.parse(text())}catch(error){throw new ParseError(error)}}if(contentType.startsWith("text/"))return text();if(contentType.startsWith("application/x-www-form-urlencoded"))return formToObject(new URLSearchParams(text()));return formToObject(await new Response(bytes,{headers:{"content-type":contentType}}).formData())},LIMITED_TYPES=/^(application\/(json|x-www-form-urlencoded)|text\/|multipart\/form-data)|\+json/,applyHttpSecurity=(app,options={})=>{let{cors,headers=!1,bodyLimit=DEFAULT_BODY_LIMIT,trustProxy=!1}=options;if(cors&&cors.origin==="*"&&cors.credentials)throw new InvalidCorsOptionsError;if(app.derive("global",({request})=>({ip:clientAddress(request,trustProxy)})),app.onRequest(({request,set})=>{if(headers){let all=headers===!0?{}:headers,defaults=headers===!0,values={"x-content-type-options":all.xContentTypeOptions===!1?void 0:all.xContentTypeOptions||defaults?"nosniff":void 0,"referrer-policy":headerValue(all.referrerPolicy,"no-referrer",defaults),"cross-origin-opener-policy":headerValue(all.crossOriginOpenerPolicy,"same-origin",defaults),"cross-origin-resource-policy":headerValue(all.crossOriginResourcePolicy,"same-origin",defaults),"x-frame-options":headerValue(all.xFrameOptions,"DENY",defaults),"content-security-policy":all.contentSecurityPolicy||void 0};if(isHttps(request,trustProxy))values["strict-transport-security"]=headerValue(all.strictTransportSecurity,"max-age=15552000; includeSubDomains",defaults);for(let[name,value]of Object.entries(values))if(value!==void 0)set.headers[name]=value}if(cors){let origin=request.headers.get("origin");if(set.headers.vary=set.headers.vary?`${set.headers.vary}, Origin`:"Origin",origin&&originAllowed(cors,origin,request)){if(set.headers["access-control-allow-origin"]=cors.origin==="*"&&!cors.credentials?"*":origin,cors.credentials)set.headers["access-control-allow-credentials"]="true";if(cors.exposedHeaders?.length)set.headers["access-control-expose-headers"]=cors.exposedHeaders.join(", ");let asked=request.headers.get("access-control-request-method");if(request.method==="OPTIONS"&&asked){let preflight=new Headers(set.headers);preflight.set("access-control-allow-methods",(cors.methods??DEFAULT_METHODS).join(", "));let allowedHeaders=cors.allowedHeaders?.join(", ")??request.headers.get("access-control-request-headers");if(allowedHeaders)preflight.set("access-control-allow-headers",allowedHeaders);if(cors.maxAge!==void 0)preflight.set("access-control-max-age",String(cors.maxAge));return new Response(null,{status:204,headers:preflight})}}}if(bodyLimit!==!1){if(Number(request.headers.get("content-length"))>bodyLimit)throw new PayloadTooLargeError(bodyLimit)}}),bodyLimit!==!1)app.onParse("global",(context)=>{let ctx=context,{request}=ctx,contentType=ctx.contentType??request.headers.get("content-type")??"";if(!request.body||request.headers.has("content-length"))return;if(!LIMITED_TYPES.test(contentType))return;return parseLimited(request,contentType,bodyLimit)})};class UnexpectedStatusError extends ElysiumError{expected;actual;body;constructor(expected,actual,body){super(`Expected status ${expected}, got ${actual}: ${body||"(empty body)"}`);this.expected=expected;this.actual=actual;this.body=body;this.name="UnexpectedStatusError"}}var toTestResponse=(response)=>Object.assign(response,{expectStatus:async(status)=>{if(response.status!==status)throw new UnexpectedStatusError(status,response.status,await response.clone().text());return response}});class TestRequest{#handle;#method;#url;#headers=new Headers;#body;constructor(handle,method,path,baseUrl){this.#handle=handle,this.#method=method,this.#url=new URL(path,baseUrl)}header(name,value){return this.#headers.set(name,value),this}headers(headers){for(let[name,value]of Object.entries(headers))this.#headers.set(name,value);return this}query(params){for(let[name,value]of Object.entries(params))for(let item of Array.isArray(value)?value:[value])this.#url.searchParams.append(name,String(item));return this}json(body){if(this.#body=JSON.stringify(body),!this.#headers.has("content-type"))this.#headers.set("content-type","application/json");return this}form(fields){return this.#body=new URLSearchParams(fields),this}body(body){return this.#body=body,this}toRequest(){let init={method:this.#method,headers:this.#headers,body:this.#body};if(this.#body instanceof ReadableStream)init.duplex="half";return new Request(this.#url,init)}async send(){return toTestResponse(await this.#handle(this.toRequest()))}then(onfulfilled,onrejected){return this.send().then(onfulfilled,onrejected)}}var createHttpClient=(handle,baseUrl="http://localhost")=>{let request=(method,path)=>new TestRequest(handle,method,path,baseUrl);return{get:(path)=>request("GET",path),post:(path)=>request("POST",path),put:(path)=>request("PUT",path),patch:(path)=>request("PATCH",path),delete:(path)=>request("DELETE",path),head:(path)=>request("HEAD",path),options:(path)=>request("OPTIONS",path),request,send:async(req)=>toTestResponse(await handle(req))}};var resetContainer=()=>{Container.reset(),Container.setOverrides(null),Container.setVisibility(null),Container.setModuleInitHook(null),Container.setSkippedLookup(null)},applyOverrides=(overrides)=>{let entries=overrides instanceof Map?[...overrides]:Object.entries(overrides);Container.setOverrides(entries.map(([token])=>token));for(let[token,value]of entries)Service.instance(token,value)};async function createTestingHttpApp(options){let{app:App,overrides={}}=options;resetContainer();try{let app=new App;applyOverrides(overrides);let elysia=await app.build(),graph=app.kernel.graph;return{app,kernel:app.kernel,http:createHttpClient((request)=>elysia.handle(request)),get:async(name,from)=>{await app.boot();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 app graph.`);return ModuleContext.run(node,resolve)},shutdown:async()=>{await app.kernel.shutdown(),resetContainer()}}}catch(error){throw resetContainer(),error}}async function createTestingApp(options){let{modules,overrides={},environment="test",boot="lazy"}=options;resetContainer();try{let graph=buildModuleGraph(modules,environment),kernel=new Kernel(graph);if(applyOverrides(overrides),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 AsyncLocalStorage3;class Application extends InteractsWithConsole{#elysia;#debug;#routeFilters=new Map;#appFilters={filters:[],node:null};#kernel;#booting;#removeShutdownHandler;#serving;_appContextStorage=new AsyncLocalStorage3;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??[]],pick=(key)=>props[key]??ownMetadata?.[key]??parentMetadata?.[key],runtime=pick("runtime"),kept={env:pick("env"),filters:pick("filters"),interceptors:pick("interceptors"),rateLimit:pick("rateLimit")},withoutModules=({modules:_modules,runtime:_runtime,env:_env,filters:_filters,interceptors:_interceptors,rateLimit:_rateLimit,...rest})=>rest;props={...deepMerge(withoutModules(props),withoutModules(ownMetadata??{}),withoutModules(parentMetadata??{}),{openapi:!1}),modules,...runtime?{runtime}:{},...Object.fromEntries(Object.entries(kept).filter(([,value])=>value!==void 0))},Metadata.define(Symbols.app,props,target)}}constructor(){super();let{debug,runtime}=Metadata.get(Symbols.app,this.constructor)??{};if(this.#debug=debug??!1,runtime)Runtime.use(runtime);initEnv(Runtime.get().env()),Service.remove("elysium.app"),Service.instance("elysium.app",this)}static start(){return new this().run()}static testing(options){return"app"in options?createTestingHttpApp(options):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=Runtime.get().argv()){this.installSignalHandlers();try{let{modules}=this.props();return this.#kernel=new Kernel(buildModuleGraph(modules??[])),this.configureEnvironment(),await this.dispatch(argv)}catch(error){return this.fail(error)}}props(){return Metadata.get(Symbols.app,this.constructor)??{}}static moduleGraph(environment){let{modules}=Metadata.get(Symbols.app,this)??{};return buildModuleGraph(modules??[],environment)}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 Lifecycle.emit("elysium:error",error),await this.#kernel?.shutdown(),this.exit(1)}exit(code){return this.removeSignalHandlers(),Runtime.get().exit(code)}removeSignalHandlers(){this.#removeShutdownHandler?.(),this.#removeShutdownHandler=void 0}installSignalHandlers(){this.removeSignalHandlers();let onSignal=async()=>{this.removeSignalHandlers();let code=0;try{let serving=this.#serving;this.#serving=void 0,await serving?.close(),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.#removeShutdownHandler=Runtime.get().onShutdown(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.boot();Lifecycle.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 build(){let{modules}=this.props();return this.#kernel=new Kernel(buildModuleGraph(modules??[])),this.configureEnvironment(),await this.bootKernel(!1),await this.buildServer(!0),this.#elysia}configureEnvironment(){let schema=mergeEnvironmentSchemas(this.props().env,this.kernel.graph);if(!schema)return;Env.clear(),initEnv(validateEnvironment(schema,Runtime.get().env()))}boot(){if(this.#booting)return this.#booting;let attempt=this.kernel.bootEager().catch((error)=>{if(this.#booting===attempt)this.#booting=void 0,this.kernel.resetFailures(),Container.forgetFailures(this.kernel.graph.nodes);throw error});return this.#booting=attempt,attempt}async commandServe(){let{server}=this.props();await this.bootKernel(!0),await this.buildServer(!1);let runtime=Runtime.get();this.#serving=runtime.serve(this.#elysia,server?.port??(parseInt(runtime.env().PORT??"",10)||3000))??void 0}async buildServer(bootOnFirstRequest){let{server,openapi,plugins}=this.props();if(this.#elysia=new Elysia(server),bootOnFirstRequest){let booted=!1;this.#elysia.onRequest(()=>{if(booted)return;return this.boot().then(()=>{booted=!0})})}if(this.#elysia.onRequest((c)=>{if(this.isDebug)getLogger().debug("Incoming request",{method:c.request.method,url:c.request.url})}),Lifecycle.emit("elysium:server:before-init",this,this),this.#elysia.derive("global",(ctx)=>({tenant:ctx.tenant??ctx.request.headers.get("x-tenant-id")??"public"})),applyHttpSecurity(this.#elysia,this.props().http),DomainError.typeBase=this.props().problems?.typeBase,openapi){let workers=Runtime.find()?.name==="workers";this.#elysia.use(openapiPlugin(workers?{embedSpec:!0,...openapi}: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(),this.indexRouteFilters(),Lifecycle.emit("elysium:server:after-init",this,this)}indexRouteFilters(){this.#appFilters={filters:this.props().filters??[],node:null},this.#routeFilters.clear();for(let route of this.#elysia.routes){let levels=routeFilterLevels.get(route.handler);if(levels?.length)this.#routeFilters.set(`${route.method} ${route.path}`,levels)}}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("global",(e)=>this.handleServerError(e)).onAfterResponse("global",(c)=>Container.disposeRequestScope(c.request??c)).onStart(async(elysia)=>{await this.onStart(elysia),Lifecycle.emit("elysium:server:start",elysia,this)}).onStop(async(elysia)=>{await this.onStop(elysia),Lifecycle.emit("elysium:server:stop",elysia,this),this.removeSignalHandlers(),await this.#kernel?.shutdown(),this._appContextStorage.disable()})}async handleServerError(e){let error=unwrapParseError(e.error);if(middlewareStops.has(error))return;if(await runErrorMiddlewares(e),error instanceof ElysiaStatus2){if(Number(error.status)>=500&&!await this.reportError(e))return;return error}let route=e.route,levels=[...e[FILTER_LEVELS]??(route?this.#routeFilters.get(`${e.request.method} ${route}`):void 0)??[],this.#appFilters],filtered=await runFilters(levels,error,e);if(filtered)return filtered.response;let{problems}=this.props(),{requestId,...options}=problems??{},problem=toProblem(error,{...options,path:e.request?new URL(e.request.url).pathname:void 0,requestId:e.request?requestIdOf(e.request,requestId):void 0,development:currentEnvironment()==="development"});if(problem.status>=500&&!await this.reportError(e))return;let headers=Object.fromEntries(Object.entries({...e.set?.headers,...error.headers}).map(([name,value])=>[name,String(value)]));return problemResponse(problem,headers)}async reportError(e){if(!await this.onError(e))return!1;return Lifecycle.emit("elysium:error",e),this.trace({name:"HttpError",message:"Unexpected error while handling HTTP request",stack:e.error?.stack,...e.error}),!0}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}${"∙".repeat(width-command.length-args.length)} ${description}`)}}import{Elysia as Elysia2,sse as esse,t as t3}from"elysia";import{assign,isEmpty as isEmpty2,objectify}from"radash";var singletons=new WeakMap,build=(level,cls)=>{let make=()=>level.node?ModuleContext.run(level.node,()=>Service.make(cls)):Service.make(cls),scope=Metadata.get(Symbols.serviceOptions,cls)?.scope;if(scope===2||scope===1)return make();let built=singletons.get(level);if(!built)singletons.set(level,built=new Map);let instance=built.get(cls);if(!instance)built.set(cls,instance=make());return instance},runInterceptors=(levels,ctx,handler)=>{let chain=levels.flatMap((level)=>level.interceptors.map((cls)=>({level,cls}))),step=(index)=>{if(index===chain.length)return handler();let{level,cls}=chain[index],called=!1,next=()=>{if(called)return Promise.reject(new InterceptorNextCalledTwiceError(cls.name));return called=!0,step(index+1)},run=()=>build(level,cls).intercept(ctx,next);return level.node?ModuleContext.run(level.node,run):run()};return step(0)};class TimingInterceptor{async intercept(ctx,next){let start=performance.now();try{return await next()}finally{let entry=`handler;dur=${(performance.now()-start).toFixed(1)}`,current=ctx.set.headers["server-timing"];ctx.set.headers["server-timing"]=current?`${current}, ${entry}`:entry}}}class TransformInterceptor{static with(transform){return class{async intercept(ctx,next){return transform(await next(),ctx)}}}}class MemoryRateLimitStore{#windows=new Map;#nextSweep=0;async hit(key,windowMs){let now=Date.now();this.#sweep(now);let current=this.#windows.get(key);if(current&¤t.resetAt>now)return current.count++,{...current};let opened={count:1,resetAt:now+windowMs};return this.#windows.set(key,opened),{...opened}}#sweep(now){if(now<this.#nextSweep)return;this.#nextSweep=now+1000;for(let[key,window]of this.#windows)if(window.resetAt<=now)this.#windows.delete(key)}}class TooManyRequestsError extends DomainError.for("RATE_LIMITED",429){retryAfter;constructor(retryAfter){super(`Too many requests. Try again in ${retryAfter} seconds.`,{retryAfterSeconds:retryAfter});this.retryAfter=retryAfter}}var RATE_LIMIT=Symbol.for("elysium:RateLimit"),RateLimit=(limit)=>(target,propertyKey,_descriptor)=>{Metadata.define(RATE_LIMIT,limit,target,propertyKey)},rateLimitOf=(controller,method)=>Metadata.get(RATE_LIMIT,controller.prototype,method)??Metadata.get(RATE_LIMIT,controller),defaultStore=new MemoryRateLimitStore,enforceRateLimit=async(limit,appStore,context)=>{let name=limit.name??"default",key=limit.key?await limit.key(context):`${context.ip??"unknown"}:${context.route}`,hit=await(limit.store??appStore??defaultStore).hit(`rl:${name}:${key}`,limit.window),headers=context.set.headers;if(headers["ratelimit-limit"]=String(limit.max),headers["ratelimit-policy"]=`${limit.max};w=${Math.ceil(limit.window/1000)}`,"limited"in hit){if(!hit.limited)return;let retryAfter=Math.ceil(limit.window/1000);throw headers["ratelimit-remaining"]="0",headers["retry-after"]=String(retryAfter),new TooManyRequestsError(retryAfter)}let reset=Math.max(0,Math.ceil((hit.resetAt-Date.now())/1000));if(headers["ratelimit-remaining"]=String(Math.max(0,limit.max-hit.count)),headers["ratelimit-reset"]=String(reset),hit.count>limit.max)throw headers["retry-after"]=String(reset),new TooManyRequestsError(reset)};import{ElysiaStatus as ElysiaStatus3}from"elysia";var HIDDEN=Symbol.for("elysium:Hidden"),cloneSchema=(schema,extra={},drop=[])=>{let descriptors=Object.getOwnPropertyDescriptors(schema);for(let key of drop)delete descriptors[key];let clone=Object.defineProperties({},descriptors);for(let key of Reflect.ownKeys(extra))Object.defineProperty(clone,key,{value:extra[key],enumerable:typeof key==="string",writable:!0,configurable:!0});return clone},isObjectSchema=(schema)=>schema.type==="object"&&typeof schema.properties==="object",isPlainObject=(value)=>{if(typeof value!=="object"||value===null)return!1;let proto=Object.getPrototypeOf(value);return proto===Object.prototype||proto===null},isStatusMap=(response)=>{let keys=Object.keys(response);return keys.length>0&&keys.every((key)=>/^\d{3}$/.test(key))},Resource;((Resource)=>(Resource.hidden=(schema)=>cloneSchema(schema,{[HIDDEN]:!0}),Resource.isHidden=(schema)=>typeof schema==="object"&&schema!==null&&(HIDDEN in schema),Resource.project=(schema)=>projectNode(schema),Resource.projectResponse=(response)=>{if(!isStatusMap(response))return Resource.project(response);let projected={};for(let[status,schema]of Object.entries(response))projected[Number(status)]=Resource.project(schema);return projected},Resource.successSchema=(response)=>{if(!isStatusMap(response))return response;if(response[200])return response[200];let status=Object.keys(response).map(Number).sort((a,b)=>a-b).find((code)=>code>=200&&code<300);return status===void 0?void 0:response[status]},Resource.serialize=(schema,value,options={})=>{let node=schema,fields=options.fields?.filter(Boolean);if(!fields?.length)return serializeNode(node,value);let target=fieldsTarget(node);if(!target)return serializeNode(node,value);let allowed=Object.keys(target.properties).filter((key)=>!Resource.isHidden(target.properties[key])),unknown=fields.filter((field)=>!allowed.includes(field));if(unknown.length>0)throw new BadRequestError("The requested fields are not part of this resource.",{errors:unknown.map((field)=>({pointer:"/query/fields",message:`unknown field "${field}"`,code:"fields"}))});let selected=new Set(fields);return serializeNode(replaceTarget(node,target,((object)=>cloneSchema(object,{properties:Object.fromEntries(Object.entries(object.properties).filter(([key])=>selected.has(key))),required:(object.required??[]).filter((key)=>selected.has(key))}))(target)),value)}))(Resource||={});var fieldsTarget=(schema)=>{if(schema.type==="array"&&schema.items&&isObjectSchema(schema.items))return schema.items;if(!isObjectSchema(schema))return;let data=schema.properties.data;if(data?.type==="array"&&data.items&&isObjectSchema(data.items))return data.items;return schema},replaceTarget=(schema,target,next)=>{if(schema===target)return next;if(schema.items===target)return cloneSchema(schema,{items:next});let data=schema.properties.data;return cloneSchema(schema,{properties:{...schema.properties,data:cloneSchema(data,{items:next})}})},projectNode=(schema)=>{let node="~codec"in schema?cloneSchema(schema,{},["~codec"]):schema;if(isObjectSchema(node)){let properties={};for(let[key,property]of Object.entries(node.properties))if(!Resource.isHidden(property))properties[key]=projectNode(property);node=cloneSchema(node,{properties,...node.required?{required:node.required.filter((key)=>(key in properties))}:{}})}if(node.items)node=cloneSchema(node,{items:projectNode(node.items)});if(node.anyOf)node=cloneSchema(node,{anyOf:node.anyOf.map(projectNode)});if(node.oneOf)node=cloneSchema(node,{oneOf:node.oneOf.map(projectNode)});return node},branchFor=(branches,value)=>{if(value===null)return branches.find((branch)=>branch.type==="null");if(Array.isArray(value))return branches.find((branch)=>branch.type==="array");if(typeof value==="object")return branches.find(isObjectSchema);return branches.find((branch)=>branch.type!=="null")},serializeNode=(schema,value)=>{if(value instanceof Date)return schema?.format==="date"?value.toISOString().slice(0,10):value.toISOString();if(typeof value==="object"&&value!==null&&!Array.isArray(value)&&!isPlainObject(value)&&typeof value.toJSON==="function")return serializeNode(schema,value.toJSON());if(!schema)return value;let union=schema.anyOf??schema.oneOf;if(union)return serializeNode(branchFor(union,value),value);if(Array.isArray(value))return schema.type==="array"?value.map((item)=>serializeNode(schema.items,item)):value;if(isObjectSchema(schema)&&typeof value==="object"&&value!==null){let source=value,result={};for(let[key,property]of Object.entries(schema.properties)){if(Resource.isHidden(property)||source[key]===void 0)continue;result[key]=serializeNode(property,source[key])}if(schema.additionalProperties!==void 0&&schema.additionalProperties!==!1){let extra=typeof schema.additionalProperties==="object"?schema.additionalProperties:void 0;for(let[key,item]of Object.entries(source))if(!(key in schema.properties)&&item!==void 0)result[key]=serializeNode(extra,item)}return result}return value},isSerializable=(value)=>!(value instanceof Response)&&!(value instanceof ElysiaStatus3)&&!(value instanceof ReadableStream)&&!(value instanceof Blob)&&!(typeof value==="object"&&value!==null&&(Symbol.asyncIterator in value)),readFields=(query)=>{let raw=query?.fields;if(raw===void 0||raw===null||raw==="")return;return(Array.isArray(raw)?raw:[raw]).flatMap((value)=>String(value).split(",")).map((field)=>field.trim())};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)=>Lifecycle.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,serialize,filters,interceptors}=props,metadata=Metadata.get(Symbols.http,target.constructor)??[];metadata.push({name:String(props.propertyKey),path,method,body,params,query,customDecorators,rawContext,handler,middlewares,response,operationId,description,services,transactional,serialize,filters,interceptors}),Metadata.define(Symbols.http,metadata,target.constructor)},HttpControllerScope;((HttpControllerScope)=>{HttpControllerScope[HttpControllerScope.SERVER=0]="SERVER";HttpControllerScope[HttpControllerScope.REQUEST=1]="REQUEST"})(HttpControllerScope||={});var moduleInterceptorLevels=new WeakMap,moduleInterceptorLevel=(node)=>{let level=moduleInterceptorLevels.get(node);if(!level)moduleInterceptorLevels.set(node,level={interceptors:node.props.interceptors??[],node});return level},appInterceptorLevels=new WeakMap,appInterceptorLevel=()=>{let app=Service.get("elysium.app");if(!app)return{interceptors:[],node:null};let level=appInterceptorLevels.get(app);if(!level){let interceptors=Metadata.get(Symbols.app,app.constructor)?.interceptors??[];appInterceptorLevels.set(app,level={interceptors,node:null})}return level},appRateLimit=()=>{let app=Service.get("elysium.app");return app?Metadata.get(Symbols.app,app.constructor)?.rateLimit:void 0},appDefaultRateLimit=()=>{let options=appRateLimit();return options?.window!==void 0&&options.max!==void 0?options:void 0},joinPath=(base,path)=>`/${[base,path].map((part)=>part.replace(/^\/+|\/+$/g,"")).filter(Boolean).join("/")}`,moduleFilterLevels=new WeakMap,moduleFilterLevel=(node)=>{let level=moduleFilterLevels.get(node);if(!level)moduleFilterLevels.set(node,level={filters:node.props.filters??[],node});return level},Http;((Http)=>(Http.describe=(controller)=>{let props=Metadata.get("http:controller",controller);if(!props)return;let metadata=Metadata.get(Symbols.http,controller)??[];return{path:props.path??"/",routes:metadata.map((route)=>{let sse=route.method==="elysium:SSE";return{name:route.name,method:sse?"GET":route.method.toUpperCase(),path:route.path,sse,body:route.body?.index,query:route.query?.index,params:route.params.map(({slug,index})=>({slug,index}))}})}},Http.controller=(path="/",options={})=>{let props={...options,path};return 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}`}),controllerFilters={filters:props.filters??[],node},moduleFilters=moduleFilterLevel(node),outer,outerInterceptors=()=>outer??=[appInterceptorLevel(),moduleInterceptorLevel(node),{interceptors:props.interceptors??[],node}].filter((level)=>level.interceptors.length>0),getController=(()=>{if(props.scope===0){let controller;return()=>()=>controller??=ModuleContext.run(node,()=>Container.makeSingleton(target))}else if(props.scope===1)return()=>()=>ModuleContext.run(node,()=>Service.make(target));else throw Error(`Invalid scope for controller: ${target.name}`)})();app.derive("local",()=>({controller:getController()}));let prepareController=props.scope===1?()=>ModuleContext.run(node,()=>Container.prepareClass(target)):null,onRequest=Metadata.get("http:onRequest",target)??{};if(onRequest.handler){let callOnRequest=(c)=>{let controller=getController()();return ModuleContext.run(node,()=>onRequest.handler.call(controller,c))};app.onRequest((c)=>Container.runInRequestScope(()=>prepareController?prepareController().then(()=>callOnRequest(c)):callOnRequest(c),c.request))}let middlewares=Metadata.get(Symbols.middlewares,target)??[];applyMiddlewares(middlewares,app,node);let metadata=Metadata.get(Symbols.http,target)??[];for(let route of metadata){let scopeKey=(c)=>c.request??c,getParameters=(c)=>Container.runInRequestScope(()=>readParameters(c),scopeKey(c)),readParameters=async(c)=>{let parameters=[];if(prepareController)await prepareController();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]=await ModuleContext.run(node,()=>Container.resolveInjection(service));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)=>Container.runInRequestScope(()=>Application.context.run(new Map([["tenant",c.tenant],["http:context",c]]),()=>isTransactional?getTransactionRunner()(body):body()),scopeKey(c)),callHandler=(c,args)=>ModuleContext.run(node,()=>route.handler.call(c.controller(),...args)),callForValue=async(c,args)=>{let result=await callHandler(c,args);if(result instanceof DomainError)throw result;return result},initTransactedHandler=(c)=>(...args)=>inRequestScope(c,()=>callForValue(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 Runtime.get().sleep(10)};let routeInterceptors={interceptors:route.interceptors??[],node},initInterceptedHandler=(c)=>(...args)=>inRequestScope(c,()=>runInterceptors([...outerInterceptors(),routeInterceptors],c,()=>callForValue(c,args))),successSchema=route.response&&route.serialize!==!1?Resource.successSchema(route.response):void 0,toBody=(c,result)=>{if(!successSchema||!isSerializable(result))return result;let fields=readFields(c.query),body=Resource.serialize(successSchema,result,{fields});if(!fields)return body;let status=typeof c.set.status==="number"?c.set.status:200;return Response.json(body,{status})};if(isValidator)return async function(c){let valid=await ModuleContext.run(node,()=>route.body.validator.validate(c));if(valid instanceof DomainError)throw valid;if(!valid)throw new BadRequestError("The request body is invalid.");let handler=initInterceptedHandler(c);return toBody(c,await handler(...await getParameters(c)))};return async function(c){let handler=initInterceptedHandler(c);return toBody(c,await handler(...await getParameters(c)))}},params=objectify(route.params??[],(p)=>p.slug,(p)=>p.schema),mi=makeMiddlewares(route.middlewares,node),routeName=`${route.method.toUpperCase()} ${joinPath(props.path,route.path)}`,limit,routeRateLimit=()=>limit??=rateLimitOf(target,route.name)??appDefaultRateLimit()??!1,handler=getHandler(),filterLevels=[{filters:route.filters??[],node},controllerFilters,moduleFilters].filter((level)=>level.filters.length>0);routeFilterLevels.set(handler,filterLevels),app.route.call(app,route.method,route.path,{async transform(c){if(c[FILTER_LEVELS]=filterLevels,isValidator){let authorized=await ModuleContext.run(node,()=>route.body.validator.authorize(c));if(authorized instanceof DomainError)throw authorized;if(!authorized)throw c.status(401,"Unauthorized")}return Container.runInRequestScope(()=>executeMiddlewareChain(mi,c,"onTransform",node),scopeKey(c))},async beforeHandle(c){enlist(c,mi,node);let limit=routeRateLimit();if(limit)await enforceRateLimit(limit,appRateLimit()?.store,{request:c.request,ip:c.ip,route:routeName,context:c,set:c.set});return Container.runInRequestScope(()=>executeMiddlewareChain(mi,c,"onBeforeHandle",node),scopeKey(c))},afterHandle(c){return Container.runInRequestScope(()=>executeMiddlewareChain(mi,c,"onAfterHandle",node),scopeKey(c))},afterResponse(c){return Container.runInRequestScope(()=>executeMiddlewareChain(mi,c,"onAfterResponse",node),scopeKey(c))},detail:{tags:props.tags,description:route.description,operationId:route.operationId},body:isValidator?route.body?.validator?.schema:route.body?.schema,response:route.response?Resource.projectResponse(route.response):void 0,params:isEmpty2(params)?void 0:t3.Object(params)},handler)}return app}Metadata.define("http:controller",props,target),Metadata.define(Symbols.elysiaPlugin,handleHttp,target)}},Http.sse=(path="/",options={})=>Http.custom("elysium:SSE",path,options),Http.get=(path="/",options={})=>Http.custom("GET",path,options),Http.post=(path="/",options={})=>Http.custom("POST",path,options),Http.put=(path="/",options={})=>Http.custom("PUT",path,options),Http.del=(path="/",options={})=>Http.custom("DELETE",path,options),Http.patch=(path="/",options={})=>Http.custom("PATCH",path,options),Http.head=(path="/",options={})=>Http.custom("HEAD",path,options),Http.options=(path="/",options={})=>Http.custom("OPTIONS",path,options),Http.trace=(path="/",options={})=>Http.custom("TRACE",path,options),Http.custom=(method,path="/",options={})=>function(target,propertyKey,descriptor){queueMicrotask(()=>{registerHttpRequestHandler({method,path,handler:descriptor.value,target,propertyKey,...options})})},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??t3.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||={});var userIdOf=(user)=>{if(typeof user==="string"||typeof user==="number")return String(user);if(typeof user!=="object"||user===null)return;let{id,sub}=user,value=id??sub;return value===void 0||value===null?void 0:String(value)},currentUserId=()=>{let http=Application.context.getStore()?.get("http:context");return userIdOf(http?.currentUser)};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{Elysia as Elysia3}from"elysia";import{omit as omit2}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,...omit2(config,["url","onOpen"]),onError(error){config.onError?.(error);let message=`An error occurred in WAMP connection ${name} (${config.url})`,reason=error;Lifecycle.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}),Lifecycle.emit("elysium:error",Error(`Failed to establish WAMP connection ${name} after ${attempts} attempts`))}return}if(closed)return;try{await config.onOpen?.()}catch(error){Lifecycle.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}),Lifecycle.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}),Lifecycle.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";import{websocket}from"elysia/websocket";var Websocket;((Websocket)=>(Websocket.controller=(path,options={})=>function(target){async function handleWebsocket(node){await nextTick();let controller,getController=()=>controller??=ModuleContext.run(node,()=>Service.make(target)),metadata=Metadata.get(Symbols.websocket,target)??{},inModule=(handler)=>(...args)=>ModuleContext.run(node,()=>handler?.apply(getController(),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)=>Lifecycle.emit("elysium:error",e.error),app=new Elysia4({name:`${node.id}:${target.name}`}).use(websocket()),middlewares=Metadata.get(Symbols.middlewares,target)??[];return applyMiddlewares(middlewares,app,node),app.ws(path,{open,close,message,drain,error,body:metadata.body,...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,BadRequestError,Catch,CircularDependencyError,Command,CommandArgumentType,ConflictError,ConflictingEnvironmentSchemaError,ConflictingModuleConfigError,ConsoleFormat,ConsoleLogger,DATABASE_TRANSACTION_SERVICE,DEFAULT_BODY_LIMIT,DomainError,DuplicateCommandError,DuplicateGlobalServiceError,DuplicateModuleNameError,ELYSIUM_PROBLEM_CODES,ElysiumError,Env,EnvironmentSkippedModuleError,ForbiddenError,Http,HttpControllerScope,InteractsWithConsole,InterceptorNextCalledTwiceError,InvalidCorsOptionsError,InvalidEnvironmentError,InvalidModuleConfigError,InvalidModuleError,KERNEL_SERVICE,Kernel,LOGGER_SERVICE,Lifecycle,LifecycleListenerNotSupportedError,MemoryRateLimitStore,Metadata,Middleware,MissingModuleConfigError,Module,ModuleContext,ModuleCycleError,NotFoundError,OutsideRequestScopeError,PROBLEM_CONTENT_TYPE,PayloadTooLargeError,ProblemDetailsSchema,RateLimit,RequestLoggerMiddleware,Resource,Runtime,RuntimeNotConfiguredError,RuntimeNotSupportedError,ScopeMismatchError,Secret,Service,ServiceNotFoundError,ServiceNotReadyError,ServiceNotVisibleError,ServiceScope,TestRequest,TimingInterceptor,TooManyRequestsError,TransformInterceptor,UnauthenticatedError,UnexpectedStatusError,ValidationFailedError,Wamp,WampModule,Websocket,clientAddress,createHttpClient,currentEnvironment,currentUserId,getLogger,isDomainError,isProblemDetails,mergeEnvironmentSchemas,needsDefaultAlias,problemResponse,problemType,requestIdOf,resetContainer,runAll,statusTitle,t4 as t,toProblem,userIdOf,validateEnvironment,validationErrors};
|
package/dist/service.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Class } from 'type-fest';
|
|
2
2
|
import type { ModuleNode } from './module-graph';
|
|
3
|
-
import type { ServiceToken } from './token';
|
|
3
|
+
import type { ServiceTag, ServiceToken } from './token';
|
|
4
4
|
/**
|
|
5
5
|
* The scope of a service.
|
|
6
6
|
* @author Axel Nana <axel.nana@workbud.com>
|
|
@@ -63,6 +63,11 @@ export type ServiceProps = Partial<{
|
|
|
63
63
|
* Whether the service is visible from every module without being exported.
|
|
64
64
|
*/
|
|
65
65
|
global: boolean;
|
|
66
|
+
/**
|
|
67
|
+
* The tags the service is registered with: it is part of what `@Service.injectAll(tag)` and
|
|
68
|
+
* `Service.getAll(tag)` return for each of them.
|
|
69
|
+
*/
|
|
70
|
+
tags: readonly ServiceTag<any>[];
|
|
66
71
|
}>;
|
|
67
72
|
/**
|
|
68
73
|
* Describes a service built by a factory function instead of a class.
|
|
@@ -77,6 +82,8 @@ export type ServiceFactoryDefinition = {
|
|
|
77
82
|
scope?: ServiceScope;
|
|
78
83
|
/** Whether the service is visible from every module. */
|
|
79
84
|
global?: boolean;
|
|
85
|
+
/** The tags the service is registered with. */
|
|
86
|
+
tags?: readonly ServiceTag<any>[];
|
|
80
87
|
};
|
|
81
88
|
/**
|
|
82
89
|
* Anything a module can list in its `services`.
|
|
@@ -99,6 +106,54 @@ export declare namespace Service {
|
|
|
99
106
|
* @throws {ElysiumError} When no service is given and the parameter type is unknown.
|
|
100
107
|
*/
|
|
101
108
|
const inject: (service?: ServiceToken) => ParameterDecorator;
|
|
109
|
+
/**
|
|
110
|
+
* Creates a tag, the token of a group of services. Services join it with
|
|
111
|
+
* `@Service.register({ tags: [tag] })` (or `tags` on a factory definition), and a consumer
|
|
112
|
+
* receives them all with `@Service.injectAll(tag)` or `Service.getAll(tag)`.
|
|
113
|
+
*
|
|
114
|
+
* A tag is an extension point open to the whole application: a module receives the tagged
|
|
115
|
+
* services of every active module, including the ones it does not import.
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* ```ts
|
|
119
|
+
* export const HealthChecks = Service.tag<HealthCheck>('health.checks');
|
|
120
|
+
*
|
|
121
|
+
* @Service.register({ tags: [HealthChecks] })
|
|
122
|
+
* class DatabaseCheck implements HealthCheck {}
|
|
123
|
+
*
|
|
124
|
+
* @Service.register()
|
|
125
|
+
* class Health {
|
|
126
|
+
* constructor(@Service.injectAll(HealthChecks) private readonly checks: HealthCheck[]) {}
|
|
127
|
+
* }
|
|
128
|
+
* ```
|
|
129
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
130
|
+
* @template T The type every tagged service implements.
|
|
131
|
+
* @param name The name of the tag, unique in the application. Two tags with the same name are
|
|
132
|
+
* the same tag.
|
|
133
|
+
*/
|
|
134
|
+
const tag: <T>(name: string) => ServiceTag<T>;
|
|
135
|
+
/**
|
|
136
|
+
* Sets a parameter to every service registered with a tag, in registration order (an empty
|
|
137
|
+
* array when there is none). The same scope rules as `inject` apply to each of them: a
|
|
138
|
+
* singleton that receives a request-scoped service throws `ScopeMismatchError`.
|
|
139
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
140
|
+
* @param tag The tag, from `Service.tag()`.
|
|
141
|
+
*/
|
|
142
|
+
const injectAll: (tag: ServiceTag<any>) => ParameterDecorator;
|
|
143
|
+
/**
|
|
144
|
+
* Retrieves every service registered with a tag, in registration order.
|
|
145
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
146
|
+
* @param tag The tag, from `Service.tag()`.
|
|
147
|
+
* @returns The services; empty when none has the tag.
|
|
148
|
+
* @throws {ServiceNotReadyError} If a service, or its module, is still initialising.
|
|
149
|
+
*/
|
|
150
|
+
const getAll: <T>(tag: ServiceTag<T>) => T[];
|
|
151
|
+
/**
|
|
152
|
+
* Like `getAll`, but waits for the `onInit` of each service and of its owning module.
|
|
153
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
154
|
+
* @param tag The tag, from `Service.tag()`.
|
|
155
|
+
*/
|
|
156
|
+
const resolveAll: <T>(tag: ServiceTag<T>) => Promise<T[]>;
|
|
102
157
|
/**
|
|
103
158
|
* Retrieves a service visible from the current module context (global services only
|
|
104
159
|
* when there is none). Lazy services are built on first access.
|
package/dist/token.d.ts
CHANGED
|
@@ -20,3 +20,40 @@ export declare const tokenOf: (service: ServiceToken) => ServiceToken;
|
|
|
20
20
|
* @param token The service token.
|
|
21
21
|
*/
|
|
22
22
|
export declare const tokenLabel: (token: ServiceToken) => string;
|
|
23
|
+
/**
|
|
24
|
+
* A tag that groups services: every service registered with it is injected together with
|
|
25
|
+
* `@Service.injectAll(tag)`, or retrieved with `Service.getAll(tag)`. Tags are matched by name.
|
|
26
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
27
|
+
* @template T The type every tagged service implements.
|
|
28
|
+
*/
|
|
29
|
+
export type ServiceTag<T = unknown> = {
|
|
30
|
+
/** The name of the tag. */
|
|
31
|
+
readonly tag: string;
|
|
32
|
+
/** Carries the type of the tagged services; never set at runtime. */
|
|
33
|
+
readonly __services?: T;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Creates a tag.
|
|
37
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
38
|
+
* @template T The type every tagged service implements.
|
|
39
|
+
* @param name The name of the tag, unique in the application.
|
|
40
|
+
*/
|
|
41
|
+
export declare const serviceTag: <T>(name: string) => ServiceTag<T>;
|
|
42
|
+
/**
|
|
43
|
+
* Checks whether a value is a service tag.
|
|
44
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
45
|
+
* @param value The value to check.
|
|
46
|
+
*/
|
|
47
|
+
export declare const isServiceTag: (value: unknown) => value is ServiceTag;
|
|
48
|
+
/**
|
|
49
|
+
* What a constructor or handler parameter receives from the container: one service by its token,
|
|
50
|
+
* or every service of a tag.
|
|
51
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
52
|
+
*/
|
|
53
|
+
export type Injection = {
|
|
54
|
+
readonly token: ServiceToken;
|
|
55
|
+
readonly index: number;
|
|
56
|
+
} | {
|
|
57
|
+
readonly tag: ServiceTag;
|
|
58
|
+
readonly index: number;
|
|
59
|
+
};
|