@elysiumjs/core 0.7.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +115 -0
- package/README.md +37 -22
- package/dist/app.d.ts +72 -54
- package/dist/command.d.ts +20 -4
- package/dist/connections.d.ts +17 -0
- package/dist/container.d.ts +134 -0
- package/dist/errors.d.ts +159 -0
- package/dist/event-wiring.d.ts +23 -0
- package/dist/event.d.ts +39 -1
- package/dist/http.d.ts +9 -3
- package/dist/index.d.ts +13 -8
- package/dist/index.js +14 -14
- package/dist/kernel.d.ts +71 -0
- package/dist/logger.d.ts +15 -0
- package/dist/metadata.d.ts +46 -0
- package/dist/middleware.d.ts +18 -5
- package/dist/module-context.d.ts +20 -0
- package/dist/module-graph.d.ts +68 -0
- package/dist/module.d.ts +83 -25
- package/dist/redis.d.ts +56 -19
- package/dist/service.d.ts +60 -16
- package/dist/testing.d.ts +39 -0
- package/dist/utils.d.ts +24 -7
- package/dist/wamp.d.ts +72 -92
- package/dist/websocket.d.ts +5 -0
- package/package.json +18 -9
- package/dist/job.d.ts +0 -105
- package/dist/worker.d.ts +0 -219
- package/tsconfig.json +0 -12
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,120 @@
|
|
|
1
1
|
# @elysiumjs/core
|
|
2
2
|
|
|
3
|
+
## 0.10.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`68a0cdd`](https://github.com/na2axl/elysiumjs/commit/68a0cdd8b2e4a003d6e0af2d78985623cae40428) Thanks [@na2axl](https://github.com/na2axl)! - Route the framework's own diagnostics through the logger service.
|
|
8
|
+
|
|
9
|
+
Breaking changes:
|
|
10
|
+
|
|
11
|
+
- `@Event.listen()` with a mode other than `on` or `once` now throws an `ElysiumError` when the class is defined, instead of logging to the console and ignoring the listener.
|
|
12
|
+
|
|
13
|
+
Fixes and additions:
|
|
14
|
+
|
|
15
|
+
- `getLogger(context?)` and `LOGGER_SERVICE` (`'logger'`) are exported by `@elysiumjs/core`. `getLogger()` returns the `logger` service when one is registered (such as the one `HermesModule` provides), and a `ConsoleLogger` otherwise or when the service cannot be built or fails to create a child logger. It is resolved at call time from the global scope only, so a module's own `logger` service does not replace it, and returns a child logger bound to `context` when one is given.
|
|
16
|
+
- `Middleware.logger` (used by `RequestLoggerMiddleware`) falls back to a `ConsoleLogger` when no `logger` service is registered. It threw before.
|
|
17
|
+
- Core diagnostics go through the logger with structured context instead of `console.*`: the debug-mode request log (`debug`, with `method` and `url`), `onDestroy` failures on shutdown (`error`, with `target` and `error`), WAMP connection attempts (`warn` while retrying, `error` when giving up, with `connection`, `attempt`/`attempts`, `maxRetries` and `error`), and WAMP registrations and subscriptions (`info`, with `connection`, `topic` and the registration or subscription id).
|
|
18
|
+
- In debug mode, incoming requests are logged through the logger at `debug` level instead of printed with `console.log`. With `HermesModule`, they show only when its `level` is `debug` or `trace`.
|
|
19
|
+
- A failed WAMP registration or subscription is logged at `error` level and emitted as `elysium:error` with an error naming the topic and the connection, instead of becoming an unhandled rejection.
|
|
20
|
+
- The `elysium:error` emitted for a WAMP client error names the connection and its URL and includes the client's message (or the value it reported, when it is not an `Error`), with that value as `cause` when there is one.
|
|
21
|
+
- `HermesLogger` serialises an `error` field of the log context like pino's `err` (type, message and stack), so logged errors keep their stack trace. Elysium logs errors under `error`.
|
|
22
|
+
- `@Logged` resolves its fallback logger with `getLogger()`, and logs a thrown error whole under `error` (it logged only its message), so its stack trace is kept.
|
|
23
|
+
- The `elysium:error` emitted when a WAMP connection gives up states the number of attempts actually made (the first one plus the retries), as the logged `attempts` does. It stated `maxRetries` before.
|
|
24
|
+
|
|
25
|
+
- [`76ba0ff`](https://github.com/na2axl/elysiumjs/commit/76ba0ff18f3c196cebc2189426328f67cb699f46) Thanks [@na2axl](https://github.com/na2axl)! - `onRequest` middlewares registered on a module or a controller now run only for its routes. Elysia runs `onRequest` hooks on every request of the app, so such a middleware could reject requests meant for other modules.
|
|
26
|
+
|
|
27
|
+
They now run in the transform step of their own routes, so after routing and body parsing, and each one runs right before its own `onTransform`: module `onRequest`, module `onTransform`, then controller `onRequest`, controller `onTransform`. Any value they return still stops the request and is sent as the response. Application middlewares still run `onRequest` before routing, for every request.
|
|
28
|
+
|
|
29
|
+
- [`bacce71`](https://github.com/na2axl/elysiumjs/commit/bacce7146f667a1a46bf474dc0344fb0111ae12a) Thanks [@na2axl](https://github.com/na2axl)! - WebSocket controllers now run the middlewares registered on them with `@Middleware.register()`, on the upgrade request. `onRequest`, `onTransform` and `onBeforeHandle` can refuse the connection by returning a response. `onAfterHandle` and `onAfterResponse` run after the upgrade, with no response to read.
|
|
30
|
+
|
|
31
|
+
### Patch Changes
|
|
32
|
+
|
|
33
|
+
- [`2c49acd`](https://github.com/na2axl/elysiumjs/commit/2c49acd3b552aa43765d70339fa216961499e1b5) Thanks [@na2axl](https://github.com/na2axl)! - The tenant set by a middleware, such as a `TenantMiddleware` reading its own header, is no longer replaced by the `x-tenant-id` header (or `public`) before the handler runs.
|
|
34
|
+
|
|
35
|
+
- [`4ebf029`](https://github.com/na2axl/elysiumjs/commit/4ebf029611e6d753a4f1bfdbe247a75d954d9ded) Thanks [@na2axl](https://github.com/na2axl)! - WAMP clients use Bun's own `WebSocket` again. The subclass that faked the `protocol` field is gone, since Bun now fills it (oven-sh/bun#18744).
|
|
36
|
+
|
|
37
|
+
## 0.9.0
|
|
38
|
+
|
|
39
|
+
### Minor Changes
|
|
40
|
+
|
|
41
|
+
- [`5fef4a7`](https://github.com/na2axl/elysiumjs/commit/5fef4a7199f502fc4fe035299e7670739459a108) Thanks [@na2axl](https://github.com/na2axl)! - Fix tenant isolation, transactions and query caching in the database layer.
|
|
42
|
+
|
|
43
|
+
Breaking changes:
|
|
44
|
+
|
|
45
|
+
- `TenantMiddleware.validateTenant()` now rejects every tenant unless you override it, and tenant identifiers must match `TENANT_ID_PATTERN` (1 to 63 ASCII letters, digits, `_` or `-`, not starting with `pg_` and other than `information_schema`, in any case) before it is called. Requests that send any other tenant now fail with `Invalid tenant`. Requests without a tenant still resolve to `public` on `SimpleTenantMiddleware`. To migrate, check the tenant against your own data:
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
class OrganizationMiddleware extends SimpleTenantMiddleware {
|
|
49
|
+
protected async validateTenant(tenant: string): Promise<boolean> {
|
|
50
|
+
return Organization.exists(tenant);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
- `getTenantSchema()`, `registerTenantSchema()` and `wrapTenantSchema()` throw on tenant identifiers that do not match `TENANT_ID_PATTERN`.
|
|
56
|
+
- `setConnectionTenant()` is removed. It set the tenant on a pooled connection for the whole session, so the tenant leaked to other requests. Use `withRLS()`, or `Database.transaction()` in RLS mode, which scope the tenant to a transaction.
|
|
57
|
+
- `RepositoryInterface.update()` and `delete()`, and the `DrizzleRepository` implementations, return `null` when no row has the id. They returned `undefined` while typed as a row. `DrizzleRepository` also requires a model whose table has an `id` column.
|
|
58
|
+
- `@transactional` handlers run through the `db.transaction` service that `DrizzleModule` provides (its name is exported by `@elysiumjs/core` as `DATABASE_TRANSACTION_SERVICE`, and still re-exported by `@elysiumjs/mnemosyne`). A transactional handler without a database module now fails with an error naming the service.
|
|
59
|
+
- `@transactional` generator handlers commit only when the whole stream was sent. A stream closed early, or whose client disconnects (even while the handler is waiting), rolls back.
|
|
60
|
+
- In RLS mode, a transaction is bound to one tenant for its whole life: the tenant set (with a transaction-local `set_config`) when `Database.transaction()`, a `@transactional` handler, `withRLS()` or `withIsolation()` opens it, from the explicit tenant or the current one (`public` when there is none). Work that finds a transaction open on its connection (`withRLS()`, `withIsolation()`, `Database.transaction()`, repository calls) joins it only when its tenant is the transaction's tenant, and throws `TenantMismatchError` otherwise, without running anything: the tenant of an open transaction never changes. Inside a transaction, `Database.getConnection()` only returns it to work for its tenant (the current tenant, or `public`), and throws `TenantMismatchError` otherwise, so raw queries never run for another tenant; queries on the `tx` handle a callback received run as the transaction's tenant. Work for several tenants, such as an admin fan-out, must use one `withRLS()` per tenant outside any transaction, not nested in one.
|
|
61
|
+
- In RLS mode, each repository call made outside a transaction runs in its own transaction bound to the current tenant: `BEGIN`, `set_config` of the tenant, the query, `COMMIT`. These calls do not use the query cache, since the cache is bypassed inside transactions. Group calls in `Database.transaction()` or `withRLS()` to share one transaction.
|
|
62
|
+
- In RLS mode, code running without a tenant in context (background jobs, CLI commands, scripts) is scoped to the `public` tenant. Set the tenant with `withTenant(tenant, () => ...)` around the work (repository calls and `Database.transaction()` then use it), or with `withRLS(tenant, (tx) => ...)`.
|
|
63
|
+
- `migration:run` validates `--tenant` against `TENANT_ID_PATTERN`. Without a terminal it no longer creates a missing schema: pass `--create-schema` (which also skips the question in a terminal), or it fails.
|
|
64
|
+
- Query cache keys start with the connection name, and each connection has its own cache transaction scope. When several applications or environments share one cache store and use the same connection names for different databases, set `cache: { prefix }` on each connection: the prefix comes before the connection name in every key (empty by default).
|
|
65
|
+
- `DrizzleDatabaseCache` keys and values changed: entries written by earlier versions are ignored. The default TTL of cached queries (including with `cache: true`) is now 60 seconds instead of 1 second, and is set with `cache: { ttl }` in seconds.
|
|
66
|
+
- `Cache.memory` keeps a separate LRU per tag namespace, so `lruSize` applies to each namespace.
|
|
67
|
+
|
|
68
|
+
Fixes and additions:
|
|
69
|
+
|
|
70
|
+
- In RLS mode, repository operations outside a transaction run in one bound to the current tenant, through the strategy's `withIsolation()`. Transactions opened with `Database.transaction()` or by `@transactional` handlers set the tenant variable when they open. `withIsolation()` and `withRLS()` expose their transaction to repositories and `Database.getConnection()`. `TenantMismatchError` is exported.
|
|
71
|
+
- `Database.transaction(callback, connection?)` opens a transaction and registers it for the callback. When a transaction is already open on the same connection, the callback joins it (no savepoint: an error rolls back the whole transaction); in RLS mode only if the current tenant is the transaction's tenant (see above). Transactions are tracked by connection, so every name of a connection (`default` and the name it aliases) sees them, and named connections take part in transactions. `Database.getBaseConnection()`, `Database.inTransaction()` and `Database.runInTransaction()` are added. The `db:tx` context entry is still set for the default connection.
|
|
72
|
+
- `@transactional` generator handlers keep their transaction open until the stream ends, then commit (or roll back if the handler throws). They previously committed before running and failed with `generator.next is not a function`. Generator handlers also see the request context (`tenant`, `http:context`).
|
|
73
|
+
- The query cache is scoped to the connection and the current tenant, is shared correctly by several app instances (table versions stored in the cache), is not read or written inside transactions, and invalidates the tables a transaction changed once it commits. A result is only stored when no mutation happened since its query started, including when concurrent queries of the same key finish out of order. Cached queries honour Drizzle's `ex`, `px`, `exat`, `pxat` and `keepTtl` options.
|
|
74
|
+
- Cache namespaces (`Cache.redis.tags(...)`, `Cache.memory.tags(...)`) no longer overwrite each other: reads, deletes and `clear()` stay within their namespace.
|
|
75
|
+
- `getSessionTenant()` reads the Bun SQL result correctly, and `getCurrentTenant()` works without an application instance (for example under `Application.testing`).
|
|
76
|
+
- `migration:run` takes an advisory lock per tenant, adds a unique index on `elysium_migrations.name` (also to existing tables), finds the migrations from the project root (`.elysiumrc`), sets the `search_path` for the transaction only, asks before opening the transaction whether to create a missing schema, and always closes its client. Existing duplicate migration names must be removed before the unique index can be created.
|
|
77
|
+
- `migration:generate` only removes `"public".` qualifiers from the migration it generates, and leaves string literals, comments and dollar-quoted bodies (`DO` blocks, functions) untouched.
|
|
78
|
+
- `DatabaseDriver.createConnection()` receives the connection name as an optional second argument.
|
|
79
|
+
- `KeyvRedis` deletes with a single `UNLINK` (or `DEL`) per call or batch and reports failed deletes in `clear()` as `error` events.
|
|
80
|
+
|
|
81
|
+
## 0.8.0
|
|
82
|
+
|
|
83
|
+
### Minor Changes
|
|
84
|
+
|
|
85
|
+
- [`8ac91ab`](https://github.com/na2axl/elysiumjs/commit/8ac91ab676b1119b4eecf0ddb3fd0a3e84017cac) Thanks [@na2axl](https://github.com/na2axl)! - Upgrade dependencies to their latest major versions: wampy 8, zod 4, commander 15, @casl/ability 7, OpenTelemetry SDK 2 (sdk-node 0.300, resources 3), @sentry/bun 11. Generated projects now use TypeScript 7.
|
|
86
|
+
|
|
87
|
+
Breaking in `@elysiumjs/mnemosyne-drizzle`: it now requires drizzle-orm 1.0 (release candidate) and generated projects use drizzle-kit 1.0. `ColumnsWithTable` is no longer re-exported. `migration:generate` writes 1.0 migration folders (`<timestamp>_<name>/migration.sql`), and `migration:run` tracks those by folder name while still running flat `*.sql` files. Existing projects can upgrade their migrations folder with `drizzle-kit up`: `migration:run` recognises the moved migrations as already applied and renames their records. The `make:model` stub now generates a `pgTable` and a `DrizzleModel` built from it.
|
|
88
|
+
|
|
89
|
+
Breaking in `@elysiumjs/core`: the deprecated `@elysiajs/swagger` plugin is replaced by `@elysiajs/openapi`, and the app option `swagger` is renamed `openapi` (typed `ElysiaOpenAPIConfig | false`). Without a `path`, the docs now live at `/openapi` instead of `/swagger`. The UI options `scalarConfig` and `swaggerOptions` are now `scalar` and `swagger`. Elysia stays on 1.4 (now `^1.4.30`) until Elysia 2 and its OpenAPI plugin are stable.
|
|
90
|
+
|
|
91
|
+
Also in `@elysiumjs/core`: the WAMP `onChallenge` callback must return a string (or a promise of one); `null` is no longer accepted. `onError` now receives the error and `onReconnectSuccess` receives wampy's details object. `WampClient` is now wampy's own `Wampy` type. `onOpen` is called by Elysium once the connection is established, since wampy 8 no longer has that callback.
|
|
92
|
+
|
|
93
|
+
- [`3625883`](https://github.com/na2axl/elysiumjs/commit/3625883dba92f1596e7444ee52f9399d8e9b049f) Thanks [@na2axl](https://github.com/na2axl)! - Drop the `reflect-metadata` dependency. Decorator metadata now lives in a small store in `@elysiumjs/core`, exported as `Metadata` (`define`, `get`, `getOwn`, `has`). Use it instead of `Reflect.defineMetadata` and `Reflect.getMetadata` to read or write Elysium metadata.
|
|
94
|
+
|
|
95
|
+
Apps no longer need `import 'reflect-metadata'` in their entrypoint, and generated projects leave it out. Keep `emitDecoratorMetadata` enabled: `@Service.inject()` and `@Command.arg()` still infer types from it. An app that loads `reflect-metadata` for another library keeps working; Elysium also reads the types stored there.
|
|
96
|
+
|
|
97
|
+
- [`07236b3`](https://github.com/na2axl/elysiumjs/commit/07236b386c2506590d74e8753ce847c1e184b3ee) Thanks [@na2axl](https://github.com/na2axl)! - Introduce the module system. Modules are now the only way to register anything in an Elysium.js app. This is a breaking change with no compatibility layer.
|
|
98
|
+
|
|
99
|
+
Breaking changes:
|
|
100
|
+
|
|
101
|
+
- `@Module.register()` takes `imports`, `services`, `exports`, `controllers`, `commands`, `jobs`, `prefix` and `env`. Services are private to their module unless exported (or global). The `beforeRegister` and `afterRegister` hooks are replaced by `onInit`, `onServe` and `onDestroy`.
|
|
102
|
+
- Configurable modules are bound with `with()`, for example `HermesModule.with({ level: 'debug' })`. `with()` accepts an object or a function.
|
|
103
|
+
- Decorators only record metadata. Importing a file no longer registers commands, services or event wiring, and the side-effect imports of the packages are removed. List commands, jobs and services in the module that owns them.
|
|
104
|
+
- `@Application.register()` no longer takes `database`, `redis`, `wamp` or `'elysium:hermes'`/`'elysium:cerberus'` keys. Use `DrizzleModule`, `RedisModule`, `WampModule`, `HermesModule` and `CerberusModule`. Package plugins are replaced by modules: add `SentryModule` instead of the `plugin()` function. `plugins` still accepts plain Elysia plugin factories.
|
|
105
|
+
- Constructing an `Application` no longer runs it. Use `await App.start()`; `Application.testing()` boots a module graph for tests. The `styx` binary loads `src/app.ts` (or `index.ts`) and starts the exported application, so a project entry no longer needs to import commands.
|
|
106
|
+
- The job system of core (`Job`, `Worker`, `WorkerPool`, `commandWork`) is removed. `@elysiumjs/heracles` is the only job system: extend its `Job`, list jobs in a module's `jobs`, declare queues with `HeraclesModule.with({ queues })`, and dispatch with `Heracles.dispatch()`.
|
|
107
|
+
- `@elysiumjs/styx` ships `StyxModule`, active in development only. The `migration:generate` and `migration:run` commands move to `DrizzleModule` and are available in production.
|
|
108
|
+
- `@elysiumjs/mnemosyne` ships `MnemosyneModule` for its caches (`redis: false` or `memory: false` opts out). `DrizzleModule` imports it.
|
|
109
|
+
- New modules: `HephaestusModule`, `ArgusModule` (`serviceName` is required), `SentryModule` (needs a `dsn` or `SENTRY_DSN`) and `CerberusModule` (`getSubject` is required).
|
|
110
|
+
- Module and service code throws typed errors (for example `InvalidModuleConfigError`) instead of exiting the process. `Application.run()` prints them and exits; command `run()` methods may still exit, and package plugin factories are unchanged.
|
|
111
|
+
- `Application.register` merges subclass props over the parent's (own props, then own metadata, then parent, then defaults), and concatenates `modules` parent first. Previously defaults and parent values won, so `openapi` could not be enabled.
|
|
112
|
+
- `Heracles.dispatch` starts a stopped queue on first use, so producers no longer start queues. `Queue.start()` is idempotent.
|
|
113
|
+
- `create-elysium` generates apps that use modules, and adds Sentry and Hephaestus to the `modules` list.
|
|
114
|
+
- `@Event.on`/`@Event.once` method listeners on classes in a loaded module's `services` or `controllers` are wired by the kernel and run in their module's context. Jobs, commands and middlewares are not wired, and an instance listener on a class that is not a service throws `EventListenerNotSupportedError`. Listeners on classes not listed in a loaded module no longer fire.
|
|
115
|
+
- New typed errors: `CircularDependencyError` (services that depend on each other, in sync and async resolution), `DuplicateModuleNameError` (two different module classes sharing a name; module scopes and config tokens are name based) and `EventListenerNotSupportedError`.
|
|
116
|
+
- Lazy boot contract: a CLI command initialises only its own module and that module's imports. Sync helpers such as `Database.getConnection()` work when the owning module's `onInit` is synchronous; otherwise they throw `ServiceNotReadyError` naming the module. Use `await Service.resolve()` or set `static boot = true` on the command.
|
|
117
|
+
|
|
3
118
|
## 0.6.0
|
|
4
119
|
|
|
5
120
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@ bun install @elysiumjs/core
|
|
|
24
24
|
- PostgreSQL database support with Drizzle ORM
|
|
25
25
|
- Model classes and repositories for database interactions
|
|
26
26
|
- Multi-tenancy support enabled
|
|
27
|
-
- Swagger
|
|
27
|
+
- OpenAPI documentation (Scalar or Swagger UI)
|
|
28
28
|
|
|
29
29
|
## Usage
|
|
30
30
|
|
|
@@ -42,40 +42,39 @@ export class UserController {
|
|
|
42
42
|
}
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
-
Create a module class:
|
|
45
|
+
Create a module class. A module is the only way to register anything in an Elysium.js app:
|
|
46
|
+
controllers, commands, jobs and services are all declared by the module that owns them.
|
|
46
47
|
|
|
47
48
|
```typescript
|
|
48
49
|
import { Module } from '@elysiumjs/core';
|
|
49
50
|
|
|
50
51
|
import { UserController } from './controllers/http/user.controller';
|
|
52
|
+
import { UserService } from './services/user.service';
|
|
51
53
|
|
|
52
54
|
@Module.register({
|
|
55
|
+
services: [UserService],
|
|
53
56
|
controllers: [UserController]
|
|
54
57
|
})
|
|
55
|
-
export class MainModule extends Module {
|
|
56
|
-
public constructor() {
|
|
57
|
-
super();
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
public async beforeRegister(): Promise<void> {
|
|
61
|
-
console.log('Module is being registered');
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
public async afterRegister(): Promise<void> {
|
|
65
|
-
console.log('Module registered successfully');
|
|
66
|
-
}
|
|
67
|
-
}
|
|
58
|
+
export class MainModule extends Module {}
|
|
68
59
|
```
|
|
69
60
|
|
|
70
|
-
Create an application class
|
|
61
|
+
Create an application class. Modules that need configuration are bound to it with `with()`:
|
|
71
62
|
|
|
72
63
|
```typescript
|
|
73
|
-
import { Application } from '@elysiumjs/core';
|
|
64
|
+
import { Application, RedisModule } from '@elysiumjs/core';
|
|
65
|
+
import { HermesModule } from '@elysiumjs/hermes';
|
|
74
66
|
|
|
75
67
|
import { MainModule } from './main.module';
|
|
76
68
|
|
|
77
69
|
@Application.register({
|
|
78
|
-
modules: [
|
|
70
|
+
modules: [
|
|
71
|
+
HermesModule.with({ level: 'debug', format: 'pretty' }),
|
|
72
|
+
RedisModule.with({
|
|
73
|
+
default: 'cache',
|
|
74
|
+
connections: { cache: { url: process.env.REDIS_URL! } }
|
|
75
|
+
}),
|
|
76
|
+
MainModule
|
|
77
|
+
],
|
|
79
78
|
server: {
|
|
80
79
|
name: 'Elysium',
|
|
81
80
|
port: 3000
|
|
@@ -87,11 +86,9 @@ export class App extends Application {}
|
|
|
87
86
|
Create an entrypoint file:
|
|
88
87
|
|
|
89
88
|
```typescript
|
|
90
|
-
import 'reflect-metadata'; // <- IMPORTANT TO ADD THIS LINE IN THE ENTRYPOINT FILE
|
|
91
|
-
|
|
92
89
|
import { App } from './app';
|
|
93
90
|
|
|
94
|
-
|
|
91
|
+
await App.start();
|
|
95
92
|
```
|
|
96
93
|
|
|
97
94
|
Start the server:
|
|
@@ -106,6 +103,24 @@ Or, if using `styx`, run:
|
|
|
106
103
|
bun styx serve
|
|
107
104
|
```
|
|
108
105
|
|
|
106
|
+
## Lazy boot
|
|
107
|
+
|
|
108
|
+
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`.
|
|
109
|
+
|
|
110
|
+
- **Sync helpers are safe** (`Service.get()`, `Database.getConnection()`, `Redis.getConnection()`, ...) when the module that owns the service initialises synchronously. Elysium then initialises that module on the spot, even if the command does not import it.
|
|
111
|
+
- **Use `await Service.resolve(...)`** when the owning module has an asynchronous `onInit`. A sync `get` then throws `ServiceNotReadyError`, which names the module.
|
|
112
|
+
- **Use `static boot = true`** on a command that needs every module initialised before it runs (workers, servers). It initialises every module and builds every service first.
|
|
113
|
+
- Service factories receive a synchronous `get`; it may only read services whose owning modules initialise synchronously, otherwise it throws `ServiceNotReadyError`.
|
|
114
|
+
|
|
115
|
+
## Migrating from the pre-module API
|
|
116
|
+
|
|
117
|
+
- The `database`, `redis`, `wamp` and `elysium:hermes` keys of `@Application.register()` are gone. Add `DrizzleModule.with()`, `RedisModule.with()`, `WampModule.with()` and `HermesModule.with()` to `modules` instead.
|
|
118
|
+
- `plugins` no longer takes package plugins. Add the package's module (for example `SentryModule`) to `modules`.
|
|
119
|
+
- Importing a file no longer registers its commands or services. List commands in a module's `commands`, services in `services`, and jobs in `jobs`.
|
|
120
|
+
- `new App()` is replaced by `await App.start()`. `Application.testing()` boots a module graph for tests.
|
|
121
|
+
- The `beforeRegister` and `afterRegister` module hooks are replaced by `onInit`, `onServe` and `onDestroy`.
|
|
122
|
+
- Jobs and workers moved to `@elysiumjs/heracles`; `WorkerPool` is removed.
|
|
123
|
+
|
|
109
124
|
## Documentation
|
|
110
125
|
|
|
111
126
|
TODO
|
|
@@ -118,4 +133,4 @@ Feel free to open issues for any bugs or questions you may have. Pull requests a
|
|
|
118
133
|
|
|
119
134
|
## License
|
|
120
135
|
|
|
121
|
-
This project is licensed under the Apache License, Version 2.0. See the [LICENSE](https://github.com/
|
|
136
|
+
This project is licensed under the Apache License, Version 2.0. See the [LICENSE](https://github.com/na2axl/elysiumjs/blob/main/LICENSE) file for more information.
|
package/dist/app.d.ts
CHANGED
|
@@ -1,23 +1,22 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ElysiaOpenAPIConfig } from '@elysiajs/openapi';
|
|
2
2
|
import type { AnyElysia, ElysiaConfig, ErrorContext, TSchema } from 'elysia';
|
|
3
3
|
import type { Route } from './http';
|
|
4
|
-
import type { ModuleClass } from './module';
|
|
5
|
-
import type {
|
|
6
|
-
import type { WampClientProps } from './wamp';
|
|
4
|
+
import type { ConfiguredModule, ModuleClass } from './module';
|
|
5
|
+
import type { TestingApp, TestingOptions } from './testing';
|
|
7
6
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
8
7
|
import { Elysia } from 'elysia';
|
|
9
8
|
import { InteractsWithConsole } from './console';
|
|
9
|
+
import { Kernel } from './kernel';
|
|
10
10
|
/**
|
|
11
11
|
* An application plugin, consisting of a function that returns an Elysia instance.
|
|
12
12
|
* @author Axel Nana <axel.nana@workbud.com>
|
|
13
13
|
*/
|
|
14
14
|
export type ElysiumPlugin = (app: Application) => Promise<AnyElysia>;
|
|
15
15
|
/**
|
|
16
|
-
* Properties required when declaring an app using the `@
|
|
16
|
+
* Properties required when declaring an app using the `@Application.register()` decorator.
|
|
17
17
|
* @author Axel Nana <axel.nana@workbud.com>
|
|
18
18
|
*/
|
|
19
19
|
export type AppProps = {
|
|
20
|
-
[key: string]: any;
|
|
21
20
|
/**
|
|
22
21
|
* Enables or disables debug mode.
|
|
23
22
|
*/
|
|
@@ -32,41 +31,15 @@ export type AppProps = {
|
|
|
32
31
|
port?: number;
|
|
33
32
|
};
|
|
34
33
|
/**
|
|
35
|
-
* The
|
|
34
|
+
* The modules the application is made of. They act as the imports of the root module.
|
|
36
35
|
*/
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* The default connection name.
|
|
40
|
-
*/
|
|
41
|
-
default: string;
|
|
42
|
-
/**
|
|
43
|
-
* The list of connections.
|
|
44
|
-
*/
|
|
45
|
-
connections: Record<string, WampClientProps>;
|
|
46
|
-
};
|
|
47
|
-
/**
|
|
48
|
-
* The list of modules provided the app.
|
|
49
|
-
*/
|
|
50
|
-
modules?: ModuleClass[];
|
|
51
|
-
/**
|
|
52
|
-
* The redis configuration for the app.
|
|
53
|
-
*/
|
|
54
|
-
redis?: {
|
|
55
|
-
/**
|
|
56
|
-
* The default connection name.
|
|
57
|
-
*/
|
|
58
|
-
default: string;
|
|
59
|
-
/**
|
|
60
|
-
* The list of connections.
|
|
61
|
-
*/
|
|
62
|
-
connections: Record<string, RedisConnectionProps>;
|
|
63
|
-
};
|
|
36
|
+
modules?: (ModuleClass | ConfiguredModule<any>)[];
|
|
64
37
|
/**
|
|
65
|
-
*
|
|
38
|
+
* OpenAPI documentation configuration.
|
|
66
39
|
*
|
|
67
|
-
* Set it to `false` to disable
|
|
40
|
+
* Set it to `false` to disable OpenAPI documentation.
|
|
68
41
|
*/
|
|
69
|
-
|
|
42
|
+
openapi?: ElysiaOpenAPIConfig | false;
|
|
70
43
|
/**
|
|
71
44
|
* The validation schema for the application environment.
|
|
72
45
|
*/
|
|
@@ -78,9 +51,15 @@ export type AppProps = {
|
|
|
78
51
|
};
|
|
79
52
|
/**
|
|
80
53
|
* Type for the application context.
|
|
54
|
+
*
|
|
55
|
+
* - `tenant`: the current tenant.
|
|
56
|
+
* - `http:context`: the context of the HTTP request being handled.
|
|
57
|
+
* - `db:transactions`: the open database transactions, keyed by the connection they run on.
|
|
58
|
+
* - `db:tx`: the transaction open on the default database connection. Kept for existing callers;
|
|
59
|
+
* prefer `db:transactions`.
|
|
81
60
|
* @author Axel Nana <axel.nana@workbud.com>
|
|
82
61
|
*/
|
|
83
|
-
export type AppContext = AsyncLocalStorage<Map<'tenant' | 'http:context' | 'db:tx', unknown>>;
|
|
62
|
+
export type AppContext = AsyncLocalStorage<Map<'tenant' | 'http:context' | 'db:transactions' | 'db:tx', unknown>>;
|
|
84
63
|
/**
|
|
85
64
|
* Base class for the application main entry.
|
|
86
65
|
* @author Axel Nana <axel.nana@workbud.com>
|
|
@@ -94,6 +73,7 @@ export declare abstract class Application extends InteractsWithConsole {
|
|
|
94
73
|
static get instance(): Application;
|
|
95
74
|
/**
|
|
96
75
|
* Gets the application context shared between asynchronous operations.
|
|
76
|
+
* Falls back to a module-level storage when no application instance exists.
|
|
97
77
|
*/
|
|
98
78
|
static get context(): AppContext;
|
|
99
79
|
/**
|
|
@@ -105,6 +85,21 @@ export declare abstract class Application extends InteractsWithConsole {
|
|
|
105
85
|
* Creates a new instance of the application.
|
|
106
86
|
*/
|
|
107
87
|
constructor();
|
|
88
|
+
/**
|
|
89
|
+
* Creates the application and runs it.
|
|
90
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
91
|
+
*/
|
|
92
|
+
static start<T extends Application>(this: new () => T): Promise<void>;
|
|
93
|
+
/**
|
|
94
|
+
* Boots a module graph without a CLI or an HTTP server, for tests.
|
|
95
|
+
* @param options The modules to boot, service overrides, environment and boot mode.
|
|
96
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
97
|
+
*/
|
|
98
|
+
static testing(options: TestingOptions): Promise<TestingApp>;
|
|
99
|
+
/**
|
|
100
|
+
* The kernel running this application's module graph. Available once `run()` has started.
|
|
101
|
+
*/
|
|
102
|
+
get kernel(): Kernel;
|
|
108
103
|
/**
|
|
109
104
|
* Gets the Elysia instance.
|
|
110
105
|
*/
|
|
@@ -134,32 +129,55 @@ export declare abstract class Application extends InteractsWithConsole {
|
|
|
134
129
|
* @param _elysia The Elysia instance.
|
|
135
130
|
*/
|
|
136
131
|
protected onStop(_elysia: Elysia<Route>): Promise<void>;
|
|
137
|
-
protected run(): Promise<void>;
|
|
138
132
|
/**
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
* @
|
|
133
|
+
* Runs the CLI action given in `argv`: describe, `list`, `help`, `serve` or a command.
|
|
134
|
+
* Prints any error and exits with a non-zero code.
|
|
135
|
+
* @param argv The command line arguments, without the executable and script.
|
|
136
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
137
|
+
*/
|
|
138
|
+
run(argv?: string[]): Promise<void>;
|
|
139
|
+
private props;
|
|
140
|
+
private dispatch;
|
|
141
|
+
/** Prints an error, shuts down what was started and exits with code 1. */
|
|
142
|
+
private fail;
|
|
143
|
+
/** Exits the process, after detaching the signal handlers. */
|
|
144
|
+
private exit;
|
|
145
|
+
private removeSignalHandlers;
|
|
146
|
+
private installSignalHandlers;
|
|
147
|
+
/** The commands available in the module graph, with their owning module. */
|
|
148
|
+
private getCommands;
|
|
149
|
+
private findCommand;
|
|
150
|
+
/**
|
|
151
|
+
* Builds a command inside its owning module: lazily by default, after an eager boot
|
|
152
|
+
* when the command sets `boot`.
|
|
153
|
+
*/
|
|
154
|
+
private prepareCommand;
|
|
155
|
+
/** Registers the kernel (and boots it eagerly if asked), then announces the launch. */
|
|
156
|
+
private bootKernel;
|
|
157
|
+
/**
|
|
158
|
+
* Prints the help of a command.
|
|
159
|
+
* @param name The command name.
|
|
142
160
|
*/
|
|
143
|
-
|
|
161
|
+
private commandHelp;
|
|
144
162
|
/**
|
|
145
163
|
* Executes a command.
|
|
146
|
-
* @param
|
|
164
|
+
* @param name The command to execute.
|
|
147
165
|
* @param argv The command line arguments.
|
|
148
166
|
*/
|
|
149
167
|
private commandExec;
|
|
150
168
|
/**
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
* `elysium.command.*` service name pattern.
|
|
154
|
-
*
|
|
155
|
-
* @returns An array of all discovered command classes, filtered by build mode.
|
|
156
|
-
*/
|
|
157
|
-
private getCommands;
|
|
158
|
-
private commandWork;
|
|
159
|
-
/**
|
|
160
|
-
* Starts the server on the specified port.
|
|
169
|
+
* Starts the server: boots the kernel, builds the root Elysia app, lets modules hook into it,
|
|
170
|
+
* mounts their controllers and listens.
|
|
161
171
|
*/
|
|
162
172
|
private commandServe;
|
|
173
|
+
/** Lets every module hook into the root app, then mounts the controllers of each module. */
|
|
174
|
+
private serveModules;
|
|
175
|
+
/** Builds the Elysia plugin of a module: its middlewares and the plugins of its controllers. */
|
|
176
|
+
private mountModule;
|
|
177
|
+
/** Registers the error handler and the start and stop hooks of the root app. */
|
|
178
|
+
private setupServerHooks;
|
|
179
|
+
/** Turns an error raised while handling a request into the response sent to the client. */
|
|
180
|
+
private handleServerError;
|
|
163
181
|
/**
|
|
164
182
|
* Describes the CLI and available commands.
|
|
165
183
|
*/
|
package/dist/command.d.ts
CHANGED
|
@@ -90,7 +90,18 @@ export type CommandClass<T extends Command = Command> = Class<T> & {
|
|
|
90
90
|
* Whether the command is only available in development mode.
|
|
91
91
|
*/
|
|
92
92
|
readonly dev: boolean;
|
|
93
|
+
/**
|
|
94
|
+
* Whether the application is booted eagerly before the command runs.
|
|
95
|
+
*/
|
|
96
|
+
readonly boot: boolean;
|
|
93
97
|
};
|
|
98
|
+
/**
|
|
99
|
+
* Gets the name a command is invoked by: the `name` given to `@Command.register()`,
|
|
100
|
+
* or else the static `command`.
|
|
101
|
+
* @param cls The command class.
|
|
102
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
103
|
+
*/
|
|
104
|
+
export declare function getCommandName(cls: Class<any>): string;
|
|
94
105
|
/**
|
|
95
106
|
* Base class for commands.
|
|
96
107
|
*
|
|
@@ -118,12 +129,17 @@ export declare abstract class Command extends InteractsWithConsole {
|
|
|
118
129
|
*/
|
|
119
130
|
static readonly dev: boolean;
|
|
120
131
|
/**
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
|
|
132
|
+
* Whether the whole application is booted before the command runs.
|
|
133
|
+
* When `true`, every module is initialised and every service constructed first,
|
|
134
|
+
* as for `serve`. Otherwise modules are initialised lazily, on demand.
|
|
135
|
+
*/
|
|
136
|
+
static readonly boot: boolean;
|
|
137
|
+
/**
|
|
138
|
+
* Marks a class as a command. Records metadata only: the command still has to be
|
|
139
|
+
* listed in the `commands` of a module to be available in the CLI.
|
|
125
140
|
*
|
|
126
141
|
* @param options Optional registration options.
|
|
142
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
127
143
|
*/
|
|
128
144
|
static register(options?: {
|
|
129
145
|
name?: string;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checks the `default` and `connections` keys of a connection module's config.
|
|
3
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
4
|
+
* @param module The module name, for error messages.
|
|
5
|
+
* @param config The resolved config.
|
|
6
|
+
* @returns Whether a separate alias entry is needed for the default connection.
|
|
7
|
+
* @throws {InvalidModuleConfigError} When the default is unknown or collides with a `default` connection.
|
|
8
|
+
*/
|
|
9
|
+
export declare function needsDefaultAlias(module: string, config: {
|
|
10
|
+
default: string;
|
|
11
|
+
connections: Record<string, unknown>;
|
|
12
|
+
}): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Runs every task even if some fail, then rethrows: the single error, or an `AggregateError`.
|
|
15
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
16
|
+
*/
|
|
17
|
+
export declare function runAll(tasks: (() => unknown)[]): Promise<void>;
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import type { Class } from 'type-fest';
|
|
2
|
+
import type { ModuleNode } from './module-graph';
|
|
3
|
+
import type { ServiceDefinition } from './service';
|
|
4
|
+
import { ServiceScope } from './service';
|
|
5
|
+
/**
|
|
6
|
+
* A service registered in the container.
|
|
7
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
8
|
+
*/
|
|
9
|
+
export type ContainerEntry = {
|
|
10
|
+
/** The name the service is registered under. */
|
|
11
|
+
name: string;
|
|
12
|
+
/** The service's scope. */
|
|
13
|
+
scope: ServiceScope;
|
|
14
|
+
/** Whether the service is visible from every scope. */
|
|
15
|
+
global: boolean;
|
|
16
|
+
/** The module that declared the service, or `null` for services registered imperatively. */
|
|
17
|
+
owner: ModuleNode | null;
|
|
18
|
+
/** Builds a new instance of the service. */
|
|
19
|
+
create: () => unknown;
|
|
20
|
+
/** The singleton instance, once built. */
|
|
21
|
+
instance?: unknown;
|
|
22
|
+
/** Whether the instance has been built and its `onInit` has completed. */
|
|
23
|
+
initialised: boolean;
|
|
24
|
+
/** Pending `onInit` of a singleton, when it is asynchronous. */
|
|
25
|
+
initPromise?: Promise<void>;
|
|
26
|
+
/** The error thrown by a singleton's `onInit`, once it has failed. */
|
|
27
|
+
initFailure?: {
|
|
28
|
+
error: unknown;
|
|
29
|
+
};
|
|
30
|
+
/** The service class, when the entry was registered from one. Used to await its dependencies. */
|
|
31
|
+
cls?: Class<any>;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Supplies the visibility rules of the module graph to the container.
|
|
35
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
36
|
+
*/
|
|
37
|
+
export type VisibilityResolver = {
|
|
38
|
+
/** The modules whose exports `node` can see. */
|
|
39
|
+
visibleOwners(node: ModuleNode): readonly ModuleNode[];
|
|
40
|
+
/** The service names `owner` exports, or `true` when it exports all its services. */
|
|
41
|
+
exportsOf(owner: ModuleNode): readonly string[] | true;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* The scoped service container: a global scope plus one scope per module.
|
|
45
|
+
* Visibility between module scopes is supplied by the module graph.
|
|
46
|
+
*
|
|
47
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
48
|
+
*/
|
|
49
|
+
export declare namespace Container {
|
|
50
|
+
/**
|
|
51
|
+
* Registers a service definition.
|
|
52
|
+
* @param owner The declaring module, or `null` for a service registered outside any module.
|
|
53
|
+
* @param def A service class or factory definition.
|
|
54
|
+
* @param config The owning module's resolved config, handed to factory definitions.
|
|
55
|
+
* @returns The name the service was registered under.
|
|
56
|
+
* @throws {DuplicateGlobalServiceError} If a global service with that name already exists.
|
|
57
|
+
*/
|
|
58
|
+
function register(owner: ModuleNode | null, def: ServiceDefinition, config: unknown): string;
|
|
59
|
+
/**
|
|
60
|
+
* Adds a prebuilt entry. A global entry goes to the global scope, any other to its owner's scope.
|
|
61
|
+
* @throws {DuplicateGlobalServiceError} If the name is already taken in the global scope.
|
|
62
|
+
*/
|
|
63
|
+
function add(entry: ContainerEntry): void;
|
|
64
|
+
/**
|
|
65
|
+
* Declares global service names that win over module registrations: `register` skips
|
|
66
|
+
* any definition with one of these names, so lookups reach the global entry.
|
|
67
|
+
* Pass `null` to clear.
|
|
68
|
+
*/
|
|
69
|
+
function setOverrides(names: Iterable<string> | null): void;
|
|
70
|
+
/**
|
|
71
|
+
* Sets the module graph's visibility rules. Without a resolver, a module only sees
|
|
72
|
+
* its own services and the global ones.
|
|
73
|
+
*/
|
|
74
|
+
function setVisibility(resolver: VisibilityResolver | null): void;
|
|
75
|
+
/**
|
|
76
|
+
* Sets the hook called before building a service whose owning module may not be initialised.
|
|
77
|
+
* It returns `void` when the module is ready, or a promise while initialisation is pending.
|
|
78
|
+
*/
|
|
79
|
+
function setModuleInitHook(hook: ((owner: ModuleNode) => Promise<void> | void) | null): void;
|
|
80
|
+
/**
|
|
81
|
+
* Sets the lookup consulted when a name isn't found, to report services
|
|
82
|
+
* that belong to modules skipped for the current environment.
|
|
83
|
+
*/
|
|
84
|
+
function setSkippedLookup(lookup: ((name: string) => {
|
|
85
|
+
module: string;
|
|
86
|
+
environment: string;
|
|
87
|
+
} | null) | null): void;
|
|
88
|
+
/**
|
|
89
|
+
* Finds a service visible from `from`: its own scope, then the exports of the modules
|
|
90
|
+
* it can see, then the global scope. Without `from`, only the global scope is searched.
|
|
91
|
+
*/
|
|
92
|
+
function lookup(name: string, from: ModuleNode | undefined): {
|
|
93
|
+
entry: ContainerEntry;
|
|
94
|
+
owner: ModuleNode | null;
|
|
95
|
+
} | null;
|
|
96
|
+
/**
|
|
97
|
+
* Gets a service visible from the current module context.
|
|
98
|
+
* @returns The service, or `null` if none is visible.
|
|
99
|
+
* @throws {ServiceNotReadyError} If the service or its module is still initialising.
|
|
100
|
+
*/
|
|
101
|
+
function get(name: string): unknown;
|
|
102
|
+
/**
|
|
103
|
+
* Gets a service of the global scope, ignoring the services of the current module context.
|
|
104
|
+
* @returns The service, or `null` if there is no global service with that name.
|
|
105
|
+
* @throws {ServiceNotReadyError} If the service or its module is still initialising.
|
|
106
|
+
*/
|
|
107
|
+
function getGlobal(name: string): unknown;
|
|
108
|
+
/**
|
|
109
|
+
* Like {@link get}, but awaits the `onInit` of the service and of its owning module.
|
|
110
|
+
* @throws {ServiceNotFoundError | ServiceNotVisibleError | EnvironmentSkippedModuleError}
|
|
111
|
+
*/
|
|
112
|
+
function resolve(name: string, requester: string): Promise<unknown>;
|
|
113
|
+
/**
|
|
114
|
+
* Builds `cls` with its injected dependencies, resolved from the current module context.
|
|
115
|
+
* @throws {ServiceNotFoundError | ServiceNotVisibleError | EnvironmentSkippedModuleError}
|
|
116
|
+
*/
|
|
117
|
+
function make<T>(cls: Class<T>): T;
|
|
118
|
+
/** Whether the global scope has a service with that name. */
|
|
119
|
+
const hasGlobal: (name: string) => boolean;
|
|
120
|
+
/** The names registered in the global scope. */
|
|
121
|
+
const globalNames: () => string[];
|
|
122
|
+
/** Removes a service from the global scope. */
|
|
123
|
+
const removeGlobal: (name: string) => void;
|
|
124
|
+
/** Clears the global scope. */
|
|
125
|
+
const clearGlobal: () => void;
|
|
126
|
+
/** The live singletons, in construction order (those whose `onInit` failed are left out). Returns a snapshot. */
|
|
127
|
+
const constructed: () => readonly {
|
|
128
|
+
name: string;
|
|
129
|
+
instance: unknown;
|
|
130
|
+
owner: ModuleNode | null;
|
|
131
|
+
}[];
|
|
132
|
+
/** Clears every scope and the construction record. */
|
|
133
|
+
function reset(): void;
|
|
134
|
+
}
|