@open-matrix/hosting (0.1.7)

Published 2026-09-03 18:26:30 +00:00 by hivecast-admin

Installation

@open-matrix:registry=
npm install @open-matrix/hosting@0.1.7
"@open-matrix/hosting": "0.1.7"

About this package

@open-matrix/hosting

Browser-neutral TypeScript implementation of the .NET Generic Host programming model. It is an application-lifetime kernel, not an HTTP server and not a Matrix actor runtime.

What it owns

  • composition through Host.CreateApplicationBuilder();
  • immutable root dependency injection;
  • configuration, options, logging and metrics builders;
  • startup validation;
  • hosted-service activation;
  • application lifetime signals;
  • background-service failure policy;
  • ordered startup and reverse-ordered shutdown;
  • synchronous and asynchronous resource disposal.

What it deliberately does not own

  • Node process signals;
  • browser page or custom-element events;
  • transport connection establishment;
  • NATS reconnection or drain;
  • package loading;
  • Matrix actors, mailboxes, readiness or supervision.

Those are leaf services or runtime-domain responsibilities attached through IHostLifetime and IHostedService.

const builder = Host.CreateApplicationBuilder({
  ApplicationName: 'worker',
  ValidateScopes: true,
  ValidateOnBuild: true,
});

builder.Configuration.AddInMemoryCollection({
  Worker: { IntervalMs: 1000 },
});

builder.Services.AddHostedService(Worker);

await using host = builder.Build();
await host.RunAsync();

Build() resolves the host infrastructure and freezes registrations, but it does not resolve or start hosted services. Operational work begins inside StartAsync().

AddHostedService() is idempotent per implementation type. Repeated registrations reuse one stable forwarding descriptor, so one singleton is constructed, started, and stopped exactly once. Duplicate detection remains a property of IServiceCollection; a repeated registration after Build() still fails through the collection's read-only guard rather than being silently accepted by an out-of-band registration cache.

Startup rollback is a retryable resource transaction. Once IHostLifetime.WaitForStartAsync() has completed, a validator or pre-service lifecycle failure must attempt IHostLifetime.StopAsync() even when no hosted service reached StartAsync(). A transient lifetime-stop failure remains observable in the startup aggregate, retains the lifetime as the incomplete cleanup phase, leaves the host non-operational, and is retried by a later StopAsync() or DisposeAsync() before provider ownership can be released.

Options validation policy

ValidateOnStart() is scoped to an options-instance name rather than to the options type as a whole. The default instance and every explicitly named instance each receive an independent startup validator. Repeating ValidateOnStart() for the same name is idempotent, while requesting a second name never suppresses its validator. Startup validation resolves the named value through IOptionsMonitor<T>.Get(name) and reports OptionsValidationError.OptionsName for the exact failing instance.

Logging-scope policy

Portable scopes use ILogger.WithScope(). Each call snapshots and freezes the supplied state and returns a new logger whose scope chain is immutable. Parent, child, and sibling loggers therefore share no mutable stack: caller mutation after scope creation, overlapping asynchronous continuations, or derivation of a sibling scope cannot rewrite another logger's correlation context. Scope order remains outermost to innermost in each emitted LogRecord.

The .NET-shaped BeginScope() form is deliberately ambient and requires an execution-environment-specific IAmbientLogScopeProvider. The browser-neutral package fails loudly when no provider is installed rather than maintaining a factory-wide mutable stack. Node hosts may install the AsyncLocalStorage adapter from @open-matrix/hosting-node; browser hosts require an explicit execution-context bridge. Ambient scopes are prepended to immutable explicit scopes when a record is emitted.

Dependency-injection policy

The included provider supports singleton, scoped and transient lifetimes, multiple and keyed registrations, constructor activation through explicit static Inject metadata, validation and LIFO disposal. It intentionally does not support asynchronous factories. Connectors and other asynchronous resources must be inert services initialized by hosted services.

Provider disposal is a retryable, dependency-preserving resource transaction. Open child scopes are disposed in reverse creation order before root/singleton resources. Within each scope, successfully completed LIFO phases are retained as complete; a failed phase keeps its exact resource and cursor for a later Dispose() or DisposeAsync() attempt. Resolution and scope creation are refused after disposal begins, so a partially torn-down object graph cannot return to operational use while cleanup remains incomplete.

The default provider is replaceable through IServiceProviderFactory.

TypeScript/.NET adaptation points

  • CancellationToken is implemented over AbortController.
  • CancellationTokenSource.CancelAfter() owns at most one replaceable timer. A later finite deadline replaces the earlier timer, -1 and Infinity disable it, stale queued callbacks cannot cancel the source, and a fired deadline releases its timer handle before publishing cancellation.
  • service interfaces use runtime ServiceKey<T> values because TypeScript interfaces are erased;
  • AggregateError represents aggregated lifecycle failures;
  • Symbol.asyncDispose provides await using integration;
  • PascalCase is confined to this bounded context to preserve the .NET mental model without renaming the rest of Matrix.

Attribution

The lifecycle model and selected algorithms are structurally adapted from the MIT-licensed .NET runtime. See NOTICE.md.

Dependencies

Development Dependencies

ID Version
@types/node ^25.0.10
typescript ^5.7.0
Details
npm
2026-09-03 18:26:30 +00:00
3
MIT
latest
28 KiB
Assets (1)
Versions (5) View all
0.1.7 2026-09-03
0.1.5 2026-08-18
0.1.4 2026-08-18
0.1.3 2026-08-17
0.1.2 2026-08-16