@benzenejs/mesh-dispatch 0.0.0-stage → 0.1.0-beta.2

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.
@@ -0,0 +1,13 @@
1
+ import { IMiddlewarePipelineBuilder } from '@benzenejs/abstractions-middleware';
2
+ import { MeshDispatchOptions } from './MeshDispatchOptions';
3
+ /** The reserved-style topic the dispatch handler is served on. */
4
+ export declare const DispatchTopic = "benzene:mesh:dispatch";
5
+ /**
6
+ * Registers the opt-in `benzene:mesh:dispatch` handler, which invokes ONE registered service's real handler with a
7
+ * caller-supplied payload. Opt-in by construction AND gated at runtime (refused in Production unless
8
+ * `MeshDispatchOptions.allowInProduction` is set). C# extension method -> free function taking the builder.
9
+ *
10
+ * Requires a `MeshServiceRegistry` registered in the same container, and at least one
11
+ * `IMeshServiceDispatcher` for the transports in use (the HTTP dispatcher is registered here).
12
+ */
13
+ export declare function useMeshDispatch<TContext>(app: IMiddlewarePipelineBuilder<TContext>, options?: MeshDispatchOptions): IMiddlewarePipelineBuilder<TContext>;
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DispatchTopic = void 0;
4
+ exports.useMeshDispatch = useMeshDispatch;
5
+ /** Port of Benzene.Mesh.Dispatch.Extensions. */
6
+ const abstractions_1 = require("@benzenejs/abstractions");
7
+ const abstractions_message_handlers_1 = require("@benzenejs/abstractions-message-handlers");
8
+ const core_message_handlers_1 = require("@benzenejs/core-message-handlers");
9
+ const core_messages_1 = require("@benzenejs/core-messages");
10
+ const mesh_contracts_1 = require("@benzenejs/mesh-contracts");
11
+ const IMeshDispatchEnvironment_1 = require("./IMeshDispatchEnvironment");
12
+ const HttpMeshServiceDispatcher_1 = require("./HttpMeshServiceDispatcher");
13
+ const IMeshServiceDispatcher_1 = require("./IMeshServiceDispatcher");
14
+ const MeshDispatchGate_1 = require("./MeshDispatchGate");
15
+ const MeshDispatchMessageHandler_1 = require("./MeshDispatchMessageHandler");
16
+ const MeshDispatchOptions_1 = require("./MeshDispatchOptions");
17
+ const MeshDispatchRequest_1 = require("./MeshDispatchRequest");
18
+ /** The reserved-style topic the dispatch handler is served on. */
19
+ exports.DispatchTopic = 'benzene:mesh:dispatch';
20
+ /**
21
+ * Registers the opt-in `benzene:mesh:dispatch` handler, which invokes ONE registered service's real handler with a
22
+ * caller-supplied payload. Opt-in by construction AND gated at runtime (refused in Production unless
23
+ * `MeshDispatchOptions.allowInProduction` is set). C# extension method -> free function taking the builder.
24
+ *
25
+ * Requires a `MeshServiceRegistry` registered in the same container, and at least one
26
+ * `IMeshServiceDispatcher` for the transports in use (the HTTP dispatcher is registered here).
27
+ */
28
+ function useMeshDispatch(app, options) {
29
+ app.register((x) => {
30
+ x.addSingletonInstance(MeshDispatchOptions_1.MeshDispatchOptions, options ?? new MeshDispatchOptions_1.MeshDispatchOptions());
31
+ (0, abstractions_1.tryAddSingletonFactory)(x, IMeshDispatchEnvironment_1.IMeshDispatchEnvironment, () => new IMeshDispatchEnvironment_1.EnvironmentVariableMeshDispatchEnvironment());
32
+ x.addSingletonFactory(MeshDispatchGate_1.MeshDispatchGate, (r) => new MeshDispatchGate_1.MeshDispatchGate(r.getService(MeshDispatchOptions_1.MeshDispatchOptions), r.getService(IMeshDispatchEnvironment_1.IMeshDispatchEnvironment)));
33
+ x.addSingletonFactory(IMeshServiceDispatcher_1.IMeshServiceDispatcher, () => new HttpMeshServiceDispatcher_1.HttpMeshServiceDispatcher());
34
+ x.addSingletonInstance(abstractions_message_handlers_1.IMessageHandlerDefinition, core_message_handlers_1.MessageHandlerDefinition.createInstance(exports.DispatchTopic, '', MeshDispatchRequest_1.MeshDispatchRequest, core_messages_1.RawStringMessage, MeshDispatchMessageHandler_1.MeshDispatchMessageHandler));
35
+ x.addScopedFactory(MeshDispatchMessageHandler_1.MeshDispatchMessageHandler, (r) => new MeshDispatchMessageHandler_1.MeshDispatchMessageHandler(r.getService(MeshDispatchGate_1.MeshDispatchGate), r.getService(mesh_contracts_1.MeshServiceRegistry), r.getServices(IMeshServiceDispatcher_1.IMeshServiceDispatcher)));
36
+ });
37
+ return app;
38
+ }
@@ -0,0 +1,25 @@
1
+ /** Port of Benzene.Mesh.Dispatch.HttpMeshServiceDispatcher. */
2
+ import { MeshServiceRegistryEntry } from '@benzenejs/mesh-contracts';
3
+ import { MeshDispatchEnvelope, MeshDispatchResult } from './MeshDispatchEnvelope';
4
+ import { IMeshServiceDispatcher } from './IMeshServiceDispatcher';
5
+ /** The `SourceOptions` key overriding the invoke URL. */
6
+ export declare const InvokeUrlOption = "invokeUrl";
7
+ /** The default envelope path a service is invoked on: `/benzene/invoke` (design-principles §5.2). */
8
+ export declare const DefaultInvokePath = "/benzene/invoke";
9
+ /** A `fetch`-like function - the port of C# `HttpClient.PostAsync` (`HttpClient` -> injectable `fetch`). */
10
+ export type DispatchFetch = (url: string, init: RequestInit) => Promise<Response>;
11
+ /**
12
+ * Dispatches to an HTTP-reachable service by POSTing the Benzene message envelope (`{ topic, headers, body }`)
13
+ * to its wire-envelope endpoint. The endpoint URL comes from the entry's `sourceOptions["invokeUrl"]` when
14
+ * present, otherwise derived from the entry's `specUrl` origin as `<origin>/benzene/invoke`.
15
+ *
16
+ * `HttpClient` -> an injectable `fetch` (default global `fetch`), the same adaptation as
17
+ * `@benzenejs/health-checks-http`. A `fetch` that rejects - the call failed before the service replied - is a
18
+ * `timeout` or `service-unavailable` result ({@link transportFailureDispatchResult}), not an exception.
19
+ */
20
+ export declare class HttpMeshServiceDispatcher implements IMeshServiceDispatcher {
21
+ private readonly fetchFn;
22
+ constructor(fetchFn?: DispatchFetch);
23
+ get key(): string;
24
+ dispatchAsync(entry: MeshServiceRegistryEntry, envelope: MeshDispatchEnvelope, cancellationToken?: AbortSignal): Promise<MeshDispatchResult>;
25
+ }
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HttpMeshServiceDispatcher = exports.DefaultInvokePath = exports.InvokeUrlOption = void 0;
4
+ /** Port of Benzene.Mesh.Dispatch.HttpMeshServiceDispatcher. */
5
+ const mesh_contracts_1 = require("@benzenejs/mesh-contracts");
6
+ const MeshDispatchEnvelope_1 = require("./MeshDispatchEnvelope");
7
+ /** The `SourceOptions` key overriding the invoke URL. */
8
+ exports.InvokeUrlOption = 'invokeUrl';
9
+ /** The default envelope path a service is invoked on: `/benzene/invoke` (design-principles §5.2). */
10
+ exports.DefaultInvokePath = '/benzene/invoke';
11
+ /**
12
+ * Dispatches to an HTTP-reachable service by POSTing the Benzene message envelope (`{ topic, headers, body }`)
13
+ * to its wire-envelope endpoint. The endpoint URL comes from the entry's `sourceOptions["invokeUrl"]` when
14
+ * present, otherwise derived from the entry's `specUrl` origin as `<origin>/benzene/invoke`.
15
+ *
16
+ * `HttpClient` -> an injectable `fetch` (default global `fetch`), the same adaptation as
17
+ * `@benzenejs/health-checks-http`. A `fetch` that rejects - the call failed before the service replied - is a
18
+ * `timeout` or `service-unavailable` result ({@link transportFailureDispatchResult}), not an exception.
19
+ */
20
+ class HttpMeshServiceDispatcher {
21
+ fetchFn;
22
+ constructor(fetchFn = fetch) {
23
+ this.fetchFn = fetchFn;
24
+ }
25
+ get key() {
26
+ return mesh_contracts_1.MeshServiceSource.http;
27
+ }
28
+ async dispatchAsync(entry, envelope, cancellationToken) {
29
+ const url = resolveInvokeUrl(entry);
30
+ const payload = JSON.stringify({
31
+ topic: envelope.topic,
32
+ headers: envelope.headers,
33
+ body: envelope.body,
34
+ });
35
+ let response;
36
+ try {
37
+ response = await this.fetchFn(url, {
38
+ method: 'POST',
39
+ headers: { 'content-type': 'application/json' },
40
+ body: payload,
41
+ signal: cancellationToken,
42
+ });
43
+ }
44
+ catch (error) {
45
+ // Failed before any reply (transport-bindings §1): a `timeout`/`service-unavailable` result, not a throw.
46
+ return (0, MeshDispatchEnvelope_1.transportFailureDispatchResult)(error);
47
+ }
48
+ const responseBody = await response.text();
49
+ // The envelope endpoint answers 200 for every envelope it processed, the outcome inside the envelope
50
+ // (transport-bindings §1, D17), so the result is read from the body whatever the status. Only a body
51
+ // that is not an envelope (a carrier failure, or a non-Benzene endpoint) falls back to the status line.
52
+ const replied = tryParseResponseEnvelope(responseBody);
53
+ return replied === undefined
54
+ ? new MeshDispatchEnvelope_1.MeshDispatchResult(String(response.status), responseBody)
55
+ : new MeshDispatchEnvelope_1.MeshDispatchResult(replied.statusCode, replied.body, replied.headers);
56
+ }
57
+ }
58
+ exports.HttpMeshServiceDispatcher = HttpMeshServiceDispatcher;
59
+ function resolveInvokeUrl(entry) {
60
+ const explicitUrl = entry.sourceOptions?.[exports.InvokeUrlOption];
61
+ if (explicitUrl !== undefined && explicitUrl.trim() !== '') {
62
+ return explicitUrl;
63
+ }
64
+ if (entry.specUrl.trim() === '') {
65
+ throw new Error(`Mesh service "${entry.name}" has no "${exports.InvokeUrlOption}" in sourceOptions and no specUrl to derive an invoke URL from.`);
66
+ }
67
+ return new URL(entry.specUrl).origin + exports.DefaultInvokePath;
68
+ }
69
+ function tryParseResponseEnvelope(body) {
70
+ try {
71
+ const parsed = JSON.parse(body);
72
+ if (typeof parsed !== 'object' || parsed === null || typeof parsed.statusCode !== 'string') {
73
+ return undefined;
74
+ }
75
+ const envelope = parsed;
76
+ return {
77
+ statusCode: envelope.statusCode,
78
+ body: typeof envelope.body === 'string' ? envelope.body : undefined,
79
+ headers: typeof envelope.headers === 'object' && envelope.headers !== null
80
+ ? envelope.headers
81
+ : undefined,
82
+ };
83
+ }
84
+ catch {
85
+ return undefined;
86
+ }
87
+ }
@@ -0,0 +1,21 @@
1
+ /** Port of Benzene.Mesh.Dispatch.IMeshDispatchEnvironment (+ EnvironmentVariableMeshDispatchEnvironment). */
2
+ import { ServiceToken } from '@benzenejs/abstractions';
3
+ /** Tells the dispatch gate whether the process is running in a Production environment. */
4
+ export interface IMeshDispatchEnvironment {
5
+ /** Whether this process considers itself to be running in Production. */
6
+ readonly isProduction: boolean;
7
+ }
8
+ /** Container token: the gate resolves the environment by this interface. */
9
+ export declare const IMeshDispatchEnvironment: ServiceToken<IMeshDispatchEnvironment>;
10
+ /**
11
+ * Default {@link IMeshDispatchEnvironment} reading the environment from process env vars. An UNSET
12
+ * environment is treated as Production - the safe default, so dispatch is off unless the host is
13
+ * explicitly non-production (or `MeshDispatchOptions.allowInProduction` is set).
14
+ *
15
+ * DIVERGENCE: C# reads `ASPNETCORE_ENVIRONMENT`/`DOTNET_ENVIRONMENT`; the Node port reads `NODE_ENV` first
16
+ * (the Node convention) then falls back to those two for teams migrating from .NET. Production when the
17
+ * resolved value is empty/unset or equals `production`/`Production` (case-insensitive).
18
+ */
19
+ export declare class EnvironmentVariableMeshDispatchEnvironment implements IMeshDispatchEnvironment {
20
+ get isProduction(): boolean;
21
+ }
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EnvironmentVariableMeshDispatchEnvironment = exports.IMeshDispatchEnvironment = void 0;
4
+ /** Port of Benzene.Mesh.Dispatch.IMeshDispatchEnvironment (+ EnvironmentVariableMeshDispatchEnvironment). */
5
+ const abstractions_1 = require("@benzenejs/abstractions");
6
+ /** Container token: the gate resolves the environment by this interface. */
7
+ exports.IMeshDispatchEnvironment = (0, abstractions_1.serviceToken)('IMeshDispatchEnvironment');
8
+ /**
9
+ * Default {@link IMeshDispatchEnvironment} reading the environment from process env vars. An UNSET
10
+ * environment is treated as Production - the safe default, so dispatch is off unless the host is
11
+ * explicitly non-production (or `MeshDispatchOptions.allowInProduction` is set).
12
+ *
13
+ * DIVERGENCE: C# reads `ASPNETCORE_ENVIRONMENT`/`DOTNET_ENVIRONMENT`; the Node port reads `NODE_ENV` first
14
+ * (the Node convention) then falls back to those two for teams migrating from .NET. Production when the
15
+ * resolved value is empty/unset or equals `production`/`Production` (case-insensitive).
16
+ */
17
+ class EnvironmentVariableMeshDispatchEnvironment {
18
+ get isProduction() {
19
+ const environment = process.env.NODE_ENV ??
20
+ process.env.ASPNETCORE_ENVIRONMENT ??
21
+ process.env.DOTNET_ENVIRONMENT;
22
+ return (environment === undefined ||
23
+ environment.trim() === '' ||
24
+ environment.toLowerCase() === 'production');
25
+ }
26
+ }
27
+ exports.EnvironmentVariableMeshDispatchEnvironment = EnvironmentVariableMeshDispatchEnvironment;
@@ -0,0 +1,17 @@
1
+ /** Port of Benzene.Mesh.Dispatch.IMeshServiceDispatcher. */
2
+ import { ServiceToken } from '@benzenejs/abstractions';
3
+ import { MeshServiceRegistryEntry } from '@benzenejs/mesh-contracts';
4
+ import { MeshDispatchEnvelope, MeshDispatchResult } from './MeshDispatchEnvelope';
5
+ /**
6
+ * Dispatches a message to ONE target service over a specific transport - the write-side counterpart of the
7
+ * aggregator's read-only service source. Keyed by `MeshServiceRegistryEntry.source` so the handler picks
8
+ * the right transport per service. `CancellationToken` -> optional `AbortSignal`.
9
+ */
10
+ export interface IMeshServiceDispatcher {
11
+ /** The `MeshServiceRegistryEntry.source` value this dispatcher handles. */
12
+ readonly key: string;
13
+ /** Sends `envelope` to the service described by `entry` and returns its response. */
14
+ dispatchAsync(entry: MeshServiceRegistryEntry, envelope: MeshDispatchEnvelope, cancellationToken?: AbortSignal): Promise<MeshDispatchResult>;
15
+ }
16
+ /** Container token: dispatchers are registered and collected via `getServices(IMeshServiceDispatcher)`. */
17
+ export declare const IMeshServiceDispatcher: ServiceToken<IMeshServiceDispatcher>;
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.IMeshServiceDispatcher = void 0;
4
+ /** Port of Benzene.Mesh.Dispatch.IMeshServiceDispatcher. */
5
+ const abstractions_1 = require("@benzenejs/abstractions");
6
+ /** Container token: dispatchers are registered and collected via `getServices(IMeshServiceDispatcher)`. */
7
+ exports.IMeshServiceDispatcher = (0, abstractions_1.serviceToken)('IMeshServiceDispatcher');
@@ -0,0 +1,25 @@
1
+ /** The message to dispatch to a target service (the standard Benzene message envelope). */
2
+ export declare class MeshDispatchEnvelope {
3
+ readonly topic: string;
4
+ readonly headers: Record<string, string>;
5
+ readonly body: string;
6
+ constructor(topic: string, headers: Record<string, string>, body: string);
7
+ }
8
+ /** The response a target service returned to a dispatched message. */
9
+ export declare class MeshDispatchResult {
10
+ readonly statusCode: string;
11
+ readonly body: string | undefined;
12
+ readonly headers: Record<string, string>;
13
+ /**
14
+ * @param statusCode The status the service returned (a Benzene result status, or an HTTP status for HTTP targets).
15
+ * @param body The response body the service returned.
16
+ */
17
+ constructor(statusCode: string, body: string | undefined, headers?: Record<string, string>);
18
+ }
19
+ /**
20
+ * The {@link MeshDispatchResult} for a dispatch that failed before the target service replied - no
21
+ * connection, a refused or reset request, an aborted or timed-out call: `timeout` when the deadline elapsed,
22
+ * otherwise `service-unavailable`, with the failure's message as the body (transport-bindings.md §1, spec 1.1:
23
+ * a result, not an exception). A port addition shared by the HTTP and Lambda dispatchers.
24
+ */
25
+ export declare function transportFailureDispatchResult(error: unknown): MeshDispatchResult;
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MeshDispatchResult = exports.MeshDispatchEnvelope = void 0;
4
+ exports.transportFailureDispatchResult = transportFailureDispatchResult;
5
+ /** Port of Benzene.Mesh.Dispatch.MeshDispatchEnvelope (+ MeshDispatchResult). */
6
+ const results_1 = require("@benzenejs/results");
7
+ /** The message to dispatch to a target service (the standard Benzene message envelope). */
8
+ class MeshDispatchEnvelope {
9
+ topic;
10
+ headers;
11
+ body;
12
+ constructor(topic, headers, body) {
13
+ this.topic = topic;
14
+ this.headers = headers;
15
+ this.body = body;
16
+ }
17
+ }
18
+ exports.MeshDispatchEnvelope = MeshDispatchEnvelope;
19
+ /** The response a target service returned to a dispatched message. */
20
+ class MeshDispatchResult {
21
+ statusCode;
22
+ body;
23
+ headers;
24
+ /**
25
+ * @param statusCode The status the service returned (a Benzene result status, or an HTTP status for HTTP targets).
26
+ * @param body The response body the service returned.
27
+ */
28
+ constructor(statusCode, body, headers) {
29
+ this.statusCode = statusCode;
30
+ this.body = body;
31
+ this.headers = headers ?? {};
32
+ }
33
+ }
34
+ exports.MeshDispatchResult = MeshDispatchResult;
35
+ /**
36
+ * The {@link MeshDispatchResult} for a dispatch that failed before the target service replied - no
37
+ * connection, a refused or reset request, an aborted or timed-out call: `timeout` when the deadline elapsed,
38
+ * otherwise `service-unavailable`, with the failure's message as the body (transport-bindings.md §1, spec 1.1:
39
+ * a result, not an exception). A port addition shared by the HTTP and Lambda dispatchers.
40
+ */
41
+ function transportFailureDispatchResult(error) {
42
+ const result = (0, results_1.transportFailureResult)(error);
43
+ return new MeshDispatchResult(result.status, result.errors[0]?.message);
44
+ }
@@ -0,0 +1,17 @@
1
+ /** Port of Benzene.Mesh.Dispatch.MeshDispatchGate. */
2
+ import { IMeshDispatchEnvironment } from './IMeshDispatchEnvironment';
3
+ import { MeshDispatchOptions } from './MeshDispatchOptions';
4
+ /** Decides whether a dispatch is currently permitted, and gives the reason when it isn't. */
5
+ export declare class MeshDispatchGate {
6
+ private readonly options;
7
+ private readonly environment;
8
+ constructor(options: MeshDispatchOptions, environment: IMeshDispatchEnvironment);
9
+ /**
10
+ * Dispatch is allowed only outside Production, unless `allowInProduction` is explicitly set. Firing a
11
+ * real handler with test data has real side-effects, so the default is off (an unset environment counts
12
+ * as Production).
13
+ */
14
+ get isAllowed(): boolean;
15
+ /** A human-readable reason dispatch is blocked, returned when {@link isAllowed} is false. */
16
+ get blockedReason(): string;
17
+ }
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MeshDispatchGate = void 0;
4
+ /** Decides whether a dispatch is currently permitted, and gives the reason when it isn't. */
5
+ class MeshDispatchGate {
6
+ options;
7
+ environment;
8
+ constructor(options, environment) {
9
+ this.options = options;
10
+ this.environment = environment;
11
+ }
12
+ /**
13
+ * Dispatch is allowed only outside Production, unless `allowInProduction` is explicitly set. Firing a
14
+ * real handler with test data has real side-effects, so the default is off (an unset environment counts
15
+ * as Production).
16
+ */
17
+ get isAllowed() {
18
+ return !this.environment.isProduction || this.options.allowInProduction;
19
+ }
20
+ /** A human-readable reason dispatch is blocked, returned when {@link isAllowed} is false. */
21
+ get blockedReason() {
22
+ return ('Mesh dispatch is disabled in this environment. It invokes a service\'s real handler with the ' +
23
+ 'supplied payload (real side-effects execute), so it is off in Production unless ' +
24
+ 'MeshDispatchOptions.allowInProduction is explicitly set.');
25
+ }
26
+ }
27
+ exports.MeshDispatchGate = MeshDispatchGate;
@@ -0,0 +1,21 @@
1
+ /** Port of Benzene.Mesh.Dispatch.MeshDispatchMessageHandler. */
2
+ import { IBenzeneResultOf } from '@benzenejs/abstractions';
3
+ import { IMessageHandler } from '@benzenejs/abstractions-message-handlers';
4
+ import { RawStringMessage } from '@benzenejs/core-messages';
5
+ import { MeshServiceRegistry } from '@benzenejs/mesh-contracts';
6
+ import { IMeshServiceDispatcher } from './IMeshServiceDispatcher';
7
+ import { MeshDispatchGate } from './MeshDispatchGate';
8
+ import { MeshDispatchRequest } from './MeshDispatchRequest';
9
+ /**
10
+ * Serves the `benzene:mesh:dispatch` topic: invokes ONE registered service's real handler with a caller-supplied
11
+ * payload and returns its response. Off unless {@link MeshDispatchGate.isAllowed} (opt-in registration AND
12
+ * non-Production / allowInProduction) - a real handler runs, with real side-effects. Bounded to one
13
+ * declared service, never a shared queue.
14
+ */
15
+ export declare class MeshDispatchMessageHandler implements IMessageHandler<MeshDispatchRequest, RawStringMessage> {
16
+ private readonly gate;
17
+ private readonly registry;
18
+ private readonly dispatchers;
19
+ constructor(gate: MeshDispatchGate, registry: MeshServiceRegistry, dispatchers: Iterable<IMeshServiceDispatcher>);
20
+ handleAsync(request: MeshDispatchRequest): Promise<IBenzeneResultOf<RawStringMessage>>;
21
+ }
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MeshDispatchMessageHandler = void 0;
4
+ const core_messages_1 = require("@benzenejs/core-messages");
5
+ const results_1 = require("@benzenejs/results");
6
+ const MeshDispatchEnvelope_1 = require("./MeshDispatchEnvelope");
7
+ /**
8
+ * Serves the `benzene:mesh:dispatch` topic: invokes ONE registered service's real handler with a caller-supplied
9
+ * payload and returns its response. Off unless {@link MeshDispatchGate.isAllowed} (opt-in registration AND
10
+ * non-Production / allowInProduction) - a real handler runs, with real side-effects. Bounded to one
11
+ * declared service, never a shared queue.
12
+ */
13
+ class MeshDispatchMessageHandler {
14
+ gate;
15
+ registry;
16
+ dispatchers;
17
+ constructor(gate, registry, dispatchers) {
18
+ this.gate = gate;
19
+ this.registry = registry;
20
+ this.dispatchers = [...dispatchers];
21
+ }
22
+ async handleAsync(request) {
23
+ if (!this.gate.isAllowed) {
24
+ return results_1.BenzeneResult.forbidden(this.gate.blockedReason);
25
+ }
26
+ if (request === undefined ||
27
+ request === null ||
28
+ (request.service ?? '').trim() === '' ||
29
+ (request.topic ?? '').trim() === '') {
30
+ return results_1.BenzeneResult.badRequest("A dispatch request needs both a 'service' and a 'topic'.");
31
+ }
32
+ const service = request.service;
33
+ const entry = this.registry.services.find((s) => s.name.toLowerCase() === service.toLowerCase());
34
+ if (entry === undefined) {
35
+ return results_1.BenzeneResult.notFound(`No service named '${service}' is registered in the mesh.`);
36
+ }
37
+ const dispatcher = this.dispatchers.find((d) => d.key.toLowerCase() === entry.source.toLowerCase());
38
+ if (dispatcher === undefined) {
39
+ return results_1.BenzeneResult.setErrors(results_1.BenzeneResultStatus.notImplemented, `No dispatcher is registered for source '${entry.source}' (service '${entry.name}'). ` +
40
+ 'Register the matching transport dispatcher (e.g. addMeshLambdaDispatcher() for AwsLambdaInvoke).');
41
+ }
42
+ const envelope = new MeshDispatchEnvelope_1.MeshDispatchEnvelope(request.topic, request.headers ?? {}, request.body ?? '');
43
+ const result = await dispatcher.dispatchAsync(entry, envelope);
44
+ const json = JSON.stringify(result);
45
+ return results_1.BenzeneResult.ok(new core_messages_1.RawStringMessage(json));
46
+ }
47
+ }
48
+ exports.MeshDispatchMessageHandler = MeshDispatchMessageHandler;
@@ -0,0 +1,10 @@
1
+ /** Port of Benzene.Mesh.Dispatch.MeshDispatchOptions. */
2
+ /** Configuration for the opt-in mesh dispatch feature. Resolved from the container by its own class. */
3
+ export declare class MeshDispatchOptions {
4
+ /**
5
+ * Whether dispatch is permitted in Production. Defaults to `false`: a dispatch invokes a target
6
+ * service's REAL handler with caller-supplied data (real side-effects execute), so it stays off in
7
+ * Production unless a human deliberately opts in.
8
+ */
9
+ allowInProduction: boolean;
10
+ }
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ /** Port of Benzene.Mesh.Dispatch.MeshDispatchOptions. */
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.MeshDispatchOptions = void 0;
5
+ /** Configuration for the opt-in mesh dispatch feature. Resolved from the container by its own class. */
6
+ class MeshDispatchOptions {
7
+ /**
8
+ * Whether dispatch is permitted in Production. Defaults to `false`: a dispatch invokes a target
9
+ * service's REAL handler with caller-supplied data (real side-effects execute), so it stays off in
10
+ * Production unless a human deliberately opts in.
11
+ */
12
+ allowInProduction = false;
13
+ }
14
+ exports.MeshDispatchOptions = MeshDispatchOptions;
@@ -0,0 +1,15 @@
1
+ /** Port of Benzene.Mesh.Dispatch.MeshDispatchRequest. */
2
+ /**
3
+ * A request to dispatch a test message to one registered service (the `benzene:mesh:dispatch` body). Plain
4
+ * settable fields (wire-deserialized input); `string?`/`Dictionary?` -> `| undefined`.
5
+ */
6
+ export declare class MeshDispatchRequest {
7
+ /** The target service's name (its key in the mesh registry). */
8
+ service: string | undefined;
9
+ /** The topic to invoke on the target service. */
10
+ topic: string | undefined;
11
+ /** Message headers to carry (optional). */
12
+ headers: Record<string, string> | undefined;
13
+ /** The serialized message body (optional). */
14
+ body: string | undefined;
15
+ }
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ /** Port of Benzene.Mesh.Dispatch.MeshDispatchRequest. */
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.MeshDispatchRequest = void 0;
5
+ /**
6
+ * A request to dispatch a test message to one registered service (the `benzene:mesh:dispatch` body). Plain
7
+ * settable fields (wire-deserialized input); `string?`/`Dictionary?` -> `| undefined`.
8
+ */
9
+ class MeshDispatchRequest {
10
+ /** The target service's name (its key in the mesh registry). */
11
+ service;
12
+ /** The topic to invoke on the target service. */
13
+ topic;
14
+ /** Message headers to carry (optional). */
15
+ headers;
16
+ /** The serialized message body (optional). */
17
+ body;
18
+ }
19
+ exports.MeshDispatchRequest = MeshDispatchRequest;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Port of Benzene.Mesh.Dispatch - the opt-in, environment-gated `benzene:mesh:dispatch` handler that invokes ONE
3
+ * registered service's real handler with a caller-supplied payload (the direct-to-consumer test path).
4
+ * Refused in Production unless `MeshDispatchOptions.allowInProduction` is set. Ships the HTTP dispatcher
5
+ * (over an injectable `fetch`); other transports register their own `IMeshServiceDispatcher`.
6
+ */
7
+ export * from './MeshDispatchOptions';
8
+ export * from './MeshDispatchRequest';
9
+ export * from './MeshDispatchEnvelope';
10
+ export * from './IMeshDispatchEnvironment';
11
+ export * from './IMeshServiceDispatcher';
12
+ export * from './MeshDispatchGate';
13
+ export * from './HttpMeshServiceDispatcher';
14
+ export * from './MeshDispatchMessageHandler';
15
+ export * from './Extensions';
package/dist/index.js ADDED
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ /**
18
+ * Port of Benzene.Mesh.Dispatch - the opt-in, environment-gated `benzene:mesh:dispatch` handler that invokes ONE
19
+ * registered service's real handler with a caller-supplied payload (the direct-to-consumer test path).
20
+ * Refused in Production unless `MeshDispatchOptions.allowInProduction` is set. Ships the HTTP dispatcher
21
+ * (over an injectable `fetch`); other transports register their own `IMeshServiceDispatcher`.
22
+ */
23
+ __exportStar(require("./MeshDispatchOptions"), exports);
24
+ __exportStar(require("./MeshDispatchRequest"), exports);
25
+ __exportStar(require("./MeshDispatchEnvelope"), exports);
26
+ __exportStar(require("./IMeshDispatchEnvironment"), exports);
27
+ __exportStar(require("./IMeshServiceDispatcher"), exports);
28
+ __exportStar(require("./MeshDispatchGate"), exports);
29
+ __exportStar(require("./HttpMeshServiceDispatcher"), exports);
30
+ __exportStar(require("./MeshDispatchMessageHandler"), exports);
31
+ __exportStar(require("./Extensions"), exports);
package/package.json CHANGED
@@ -1,6 +1,21 @@
1
1
  {
2
2
  "name": "@benzenejs/mesh-dispatch",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0-beta.2",
4
+ "description": "TypeScript port of Benzene.Mesh.Dispatch",
5
+ "license": "MIT",
6
+ "type": "commonjs",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "dependencies": {
10
+ "@benzenejs/abstractions": "0.1.0-beta.2",
11
+ "@benzenejs/abstractions-message-handlers": "0.1.0-beta.2",
12
+ "@benzenejs/abstractions-middleware": "0.1.0-beta.2",
13
+ "@benzenejs/core-message-handlers": "0.1.0-beta.2",
14
+ "@benzenejs/core-messages": "0.1.0-beta.2",
15
+ "@benzenejs/mesh-contracts": "0.1.0-beta.2",
16
+ "@benzenejs/results": "0.1.0-beta.2"
17
+ },
18
+ "files": [
19
+ "dist"
20
+ ]
21
+ }
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.