@spine-event-engine/server 2.0.0-snapshot.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.
- package/LICENSE +201 -0
- package/README.md +569 -0
- package/REFERENCE.md +488 -0
- package/dist/bus/command-bus.d.ts +61 -0
- package/dist/bus/command-bus.d.ts.map +1 -0
- package/dist/bus/command-bus.js +201 -0
- package/dist/bus/command-bus.js.map +1 -0
- package/dist/bus/command-dispatcher-registry.d.ts +33 -0
- package/dist/bus/command-dispatcher-registry.d.ts.map +1 -0
- package/dist/bus/command-dispatcher-registry.js +75 -0
- package/dist/bus/command-dispatcher-registry.js.map +1 -0
- package/dist/bus/command-dispatcher.d.ts +25 -0
- package/dist/bus/command-dispatcher.d.ts.map +1 -0
- package/dist/bus/command-dispatcher.js +15 -0
- package/dist/bus/command-dispatcher.js.map +1 -0
- package/dist/bus/command-errors.d.ts +23 -0
- package/dist/bus/command-errors.d.ts.map +1 -0
- package/dist/bus/command-errors.js +52 -0
- package/dist/bus/command-errors.js.map +1 -0
- package/dist/bus/event-bus.d.ts +113 -0
- package/dist/bus/event-bus.d.ts.map +1 -0
- package/dist/bus/event-bus.js +518 -0
- package/dist/bus/event-bus.js.map +1 -0
- package/dist/bus/event-dispatcher-origin-schemas.d.ts +17 -0
- package/dist/bus/event-dispatcher-origin-schemas.d.ts.map +1 -0
- package/dist/bus/event-dispatcher-origin-schemas.js +32 -0
- package/dist/bus/event-dispatcher-origin-schemas.js.map +1 -0
- package/dist/bus/event-dispatcher-registry.d.ts +48 -0
- package/dist/bus/event-dispatcher-registry.d.ts.map +1 -0
- package/dist/bus/event-dispatcher-registry.js +176 -0
- package/dist/bus/event-dispatcher-registry.js.map +1 -0
- package/dist/bus/event-dispatcher.d.ts +48 -0
- package/dist/bus/event-dispatcher.d.ts.map +1 -0
- package/dist/bus/event-dispatcher.js +15 -0
- package/dist/bus/event-dispatcher.js.map +1 -0
- package/dist/context/bounded-context.d.ts +606 -0
- package/dist/context/bounded-context.d.ts.map +1 -0
- package/dist/context/bounded-context.js +1780 -0
- package/dist/context/bounded-context.js.map +1 -0
- package/dist/context/entity-inbox.d.ts +63 -0
- package/dist/context/entity-inbox.d.ts.map +1 -0
- package/dist/context/entity-inbox.js +380 -0
- package/dist/context/entity-inbox.js.map +1 -0
- package/dist/context/local-inbox-handoff.d.ts +244 -0
- package/dist/context/local-inbox-handoff.d.ts.map +1 -0
- package/dist/context/local-inbox-handoff.js +334 -0
- package/dist/context/local-inbox-handoff.js.map +1 -0
- package/dist/context/projection-handoff.d.ts +46 -0
- package/dist/context/projection-handoff.d.ts.map +1 -0
- package/dist/context/projection-handoff.js +172 -0
- package/dist/context/projection-handoff.js.map +1 -0
- package/dist/context/tenant-index.d.ts +41 -0
- package/dist/context/tenant-index.d.ts.map +1 -0
- package/dist/context/tenant-index.js +92 -0
- package/dist/context/tenant-index.js.map +1 -0
- package/dist/delivery/conditional-pickup.d.ts +25 -0
- package/dist/delivery/conditional-pickup.d.ts.map +1 -0
- package/dist/delivery/conditional-pickup.js +26 -0
- package/dist/delivery/conditional-pickup.js.map +1 -0
- package/dist/delivery/delivery-builder.d.ts +216 -0
- package/dist/delivery/delivery-builder.d.ts.map +1 -0
- package/dist/delivery/delivery-builder.js +323 -0
- package/dist/delivery/delivery-builder.js.map +1 -0
- package/dist/delivery/delivery-lease.d.ts +15 -0
- package/dist/delivery/delivery-lease.d.ts.map +1 -0
- package/dist/delivery/delivery-lease.js +44 -0
- package/dist/delivery/delivery-lease.js.map +1 -0
- package/dist/delivery/delivery-loop.d.ts +113 -0
- package/dist/delivery/delivery-loop.d.ts.map +1 -0
- package/dist/delivery/delivery-loop.js +119 -0
- package/dist/delivery/delivery-loop.js.map +1 -0
- package/dist/delivery/delivery-monitor.d.ts +165 -0
- package/dist/delivery/delivery-monitor.d.ts.map +1 -0
- package/dist/delivery/delivery-monitor.js +168 -0
- package/dist/delivery/delivery-monitor.js.map +1 -0
- package/dist/delivery/delivery-ports.d.ts +141 -0
- package/dist/delivery/delivery-ports.d.ts.map +1 -0
- package/dist/delivery/delivery-ports.js +15 -0
- package/dist/delivery/delivery-ports.js.map +1 -0
- package/dist/delivery/delivery-run-control.d.ts +50 -0
- package/dist/delivery/delivery-run-control.d.ts.map +1 -0
- package/dist/delivery/delivery-run-control.js +66 -0
- package/dist/delivery/delivery-run-control.js.map +1 -0
- package/dist/delivery/delivery-run-coordinator.d.ts +218 -0
- package/dist/delivery/delivery-run-coordinator.d.ts.map +1 -0
- package/dist/delivery/delivery-run-coordinator.js +609 -0
- package/dist/delivery/delivery-run-coordinator.js.map +1 -0
- package/dist/delivery/delivery-storage-error.d.ts +13 -0
- package/dist/delivery/delivery-storage-error.d.ts.map +1 -0
- package/dist/delivery/delivery-storage-error.js +30 -0
- package/dist/delivery/delivery-storage-error.js.map +1 -0
- package/dist/delivery/delivery-supervisor.d.ts +175 -0
- package/dist/delivery/delivery-supervisor.d.ts.map +1 -0
- package/dist/delivery/delivery-supervisor.js +469 -0
- package/dist/delivery/delivery-supervisor.js.map +1 -0
- package/dist/delivery/delivery-worker.d.ts +180 -0
- package/dist/delivery/delivery-worker.d.ts.map +1 -0
- package/dist/delivery/delivery-worker.js +232 -0
- package/dist/delivery/delivery-worker.js.map +1 -0
- package/dist/delivery/delivery.d.ts +238 -0
- package/dist/delivery/delivery.d.ts.map +1 -0
- package/dist/delivery/delivery.js +400 -0
- package/dist/delivery/delivery.js.map +1 -0
- package/dist/delivery/inbox-records.d.ts +19 -0
- package/dist/delivery/inbox-records.d.ts.map +1 -0
- package/dist/delivery/inbox-records.js +291 -0
- package/dist/delivery/inbox-records.js.map +1 -0
- package/dist/delivery/inbox-storage.d.ts +81 -0
- package/dist/delivery/inbox-storage.d.ts.map +1 -0
- package/dist/delivery/inbox-storage.js +353 -0
- package/dist/delivery/inbox-storage.js.map +1 -0
- package/dist/delivery/inbox.d.ts +263 -0
- package/dist/delivery/inbox.d.ts.map +1 -0
- package/dist/delivery/inbox.js +172 -0
- package/dist/delivery/inbox.js.map +1 -0
- package/dist/delivery/parked-delivery-obligations.d.ts +182 -0
- package/dist/delivery/parked-delivery-obligations.d.ts.map +1 -0
- package/dist/delivery/parked-delivery-obligations.js +390 -0
- package/dist/delivery/parked-delivery-obligations.js.map +1 -0
- package/dist/delivery/shard-index.d.ts +41 -0
- package/dist/delivery/shard-index.d.ts.map +1 -0
- package/dist/delivery/shard-index.js +67 -0
- package/dist/delivery/shard-index.js.map +1 -0
- package/dist/delivery/sharded-work-registry.d.ts +114 -0
- package/dist/delivery/sharded-work-registry.d.ts.map +1 -0
- package/dist/delivery/sharded-work-registry.js +360 -0
- package/dist/delivery/sharded-work-registry.js.map +1 -0
- package/dist/entity/entity-metadata.d.ts +173 -0
- package/dist/entity/entity-metadata.d.ts.map +1 -0
- package/dist/entity/entity-metadata.js +187 -0
- package/dist/entity/entity-metadata.js.map +1 -0
- package/dist/entity/entity-storage-descriptor.d.ts +64 -0
- package/dist/entity/entity-storage-descriptor.d.ts.map +1 -0
- package/dist/entity/entity-storage-descriptor.js +227 -0
- package/dist/entity/entity-storage-descriptor.js.map +1 -0
- package/dist/entity/entity-transaction.d.ts +372 -0
- package/dist/entity/entity-transaction.d.ts.map +1 -0
- package/dist/entity/entity-transaction.js +413 -0
- package/dist/entity/entity-transaction.js.map +1 -0
- package/dist/entity/entity-transition-validation.d.ts +47 -0
- package/dist/entity/entity-transition-validation.d.ts.map +1 -0
- package/dist/entity/entity-transition-validation.js +327 -0
- package/dist/entity/entity-transition-validation.js.map +1 -0
- package/dist/entity/entity.d.ts +499 -0
- package/dist/entity/entity.d.ts.map +1 -0
- package/dist/entity/entity.js +829 -0
- package/dist/entity/entity.js.map +1 -0
- package/dist/entity/implicit-required-id.d.ts +15 -0
- package/dist/entity/implicit-required-id.d.ts.map +1 -0
- package/dist/entity/implicit-required-id.js +123 -0
- package/dist/entity/implicit-required-id.js.map +1 -0
- package/dist/entity/spec-scanner.d.ts +14 -0
- package/dist/entity/spec-scanner.d.ts.map +1 -0
- package/dist/entity/spec-scanner.js +37 -0
- package/dist/entity/spec-scanner.js.map +1 -0
- package/dist/handler/build-time-handler-analyzer.d.ts +150 -0
- package/dist/handler/build-time-handler-analyzer.d.ts.map +1 -0
- package/dist/handler/build-time-handler-analyzer.js +1101 -0
- package/dist/handler/build-time-handler-analyzer.js.map +1 -0
- package/dist/handler/command-registration-readiness.d.ts +99 -0
- package/dist/handler/command-registration-readiness.d.ts.map +1 -0
- package/dist/handler/command-registration-readiness.js +118 -0
- package/dist/handler/command-registration-readiness.js.map +1 -0
- package/dist/handler/event-handler-filter.d.ts +43 -0
- package/dist/handler/event-handler-filter.d.ts.map +1 -0
- package/dist/handler/event-handler-filter.js +128 -0
- package/dist/handler/event-handler-filter.js.map +1 -0
- package/dist/handler/event-registration-readiness.d.ts +196 -0
- package/dist/handler/event-registration-readiness.d.ts.map +1 -0
- package/dist/handler/event-registration-readiness.js +187 -0
- package/dist/handler/event-registration-readiness.js.map +1 -0
- package/dist/handler/external.d.ts +10 -0
- package/dist/handler/external.d.ts.map +1 -0
- package/dist/handler/external.js +15 -0
- package/dist/handler/external.js.map +1 -0
- package/dist/handler/generated-handler-registry.d.ts +154 -0
- package/dist/handler/generated-handler-registry.d.ts.map +1 -0
- package/dist/handler/generated-handler-registry.js +223 -0
- package/dist/handler/generated-handler-registry.js.map +1 -0
- package/dist/handler/generated-registry-discovery.d.ts +94 -0
- package/dist/handler/generated-registry-discovery.d.ts.map +1 -0
- package/dist/handler/generated-registry-discovery.js +228 -0
- package/dist/handler/generated-registry-discovery.js.map +1 -0
- package/dist/handler/generated-registry-writer.d.ts +58 -0
- package/dist/handler/generated-registry-writer.d.ts.map +1 -0
- package/dist/handler/generated-registry-writer.js +458 -0
- package/dist/handler/generated-registry-writer.js.map +1 -0
- package/dist/handler/handler-codegen.d.ts +48 -0
- package/dist/handler/handler-codegen.d.ts.map +1 -0
- package/dist/handler/handler-codegen.js +214 -0
- package/dist/handler/handler-codegen.js.map +1 -0
- package/dist/handler/handler-decorators.d.ts +134 -0
- package/dist/handler/handler-decorators.d.ts.map +1 -0
- package/dist/handler/handler-decorators.js +239 -0
- package/dist/handler/handler-decorators.js.map +1 -0
- package/dist/handler/handler-metadata.d.ts +510 -0
- package/dist/handler/handler-metadata.d.ts.map +1 -0
- package/dist/handler/handler-metadata.js +391 -0
- package/dist/handler/handler-metadata.js.map +1 -0
- package/dist/handler/registration-readiness-metadata.d.ts +68 -0
- package/dist/handler/registration-readiness-metadata.d.ts.map +1 -0
- package/dist/handler/registration-readiness-metadata.js +170 -0
- package/dist/handler/registration-readiness-metadata.js.map +1 -0
- package/dist/handler/rejection-source.d.ts +11 -0
- package/dist/handler/rejection-source.d.ts.map +1 -0
- package/dist/handler/rejection-source.js +25 -0
- package/dist/handler/rejection-source.js.map +1 -0
- package/dist/index.d.ts +46 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +54 -0
- package/dist/index.js.map +1 -0
- package/dist/integration/external-messages.d.ts +49 -0
- package/dist/integration/external-messages.d.ts.map +1 -0
- package/dist/integration/external-messages.js +112 -0
- package/dist/integration/external-messages.js.map +1 -0
- package/dist/integration/integration-broker.d.ts +74 -0
- package/dist/integration/integration-broker.d.ts.map +1 -0
- package/dist/integration/integration-broker.js +444 -0
- package/dist/integration/integration-broker.js.map +1 -0
- package/dist/integration/third-party-context.d.ts +56 -0
- package/dist/integration/third-party-context.d.ts.map +1 -0
- package/dist/integration/third-party-context.js +130 -0
- package/dist/integration/third-party-context.js.map +1 -0
- package/dist/repository/command-errors.d.ts +25 -0
- package/dist/repository/command-errors.d.ts.map +1 -0
- package/dist/repository/command-errors.js +43 -0
- package/dist/repository/command-errors.js.map +1 -0
- package/dist/repository/command-routing.d.ts +72 -0
- package/dist/repository/command-routing.d.ts.map +1 -0
- package/dist/repository/command-routing.js +98 -0
- package/dist/repository/command-routing.js.map +1 -0
- package/dist/repository/commit-fence.d.ts +22 -0
- package/dist/repository/commit-fence.d.ts.map +1 -0
- package/dist/repository/commit-fence.js +37 -0
- package/dist/repository/commit-fence.js.map +1 -0
- package/dist/repository/event-routing.d.ts +71 -0
- package/dist/repository/event-routing.d.ts.map +1 -0
- package/dist/repository/event-routing.js +96 -0
- package/dist/repository/event-routing.js.map +1 -0
- package/dist/repository/primitive-id.d.ts +86 -0
- package/dist/repository/primitive-id.d.ts.map +1 -0
- package/dist/repository/primitive-id.js +110 -0
- package/dist/repository/primitive-id.js.map +1 -0
- package/dist/repository/repository.d.ts +670 -0
- package/dist/repository/repository.d.ts.map +1 -0
- package/dist/repository/repository.js +3298 -0
- package/dist/repository/repository.js.map +1 -0
- package/dist/repository/routing-declarations.d.ts +75 -0
- package/dist/repository/routing-declarations.d.ts.map +1 -0
- package/dist/repository/routing-declarations.js +101 -0
- package/dist/repository/routing-declarations.js.map +1 -0
- package/dist/repository/state-update-routing.d.ts +66 -0
- package/dist/repository/state-update-routing.d.ts.map +1 -0
- package/dist/repository/state-update-routing.js +91 -0
- package/dist/repository/state-update-routing.js.map +1 -0
- package/dist/runtime/runtime.d.ts +156 -0
- package/dist/runtime/runtime.d.ts.map +1 -0
- package/dist/runtime/runtime.js +215 -0
- package/dist/runtime/runtime.js.map +1 -0
- package/dist/runtime/signal-intake.d.ts +84 -0
- package/dist/runtime/signal-intake.d.ts.map +1 -0
- package/dist/runtime/signal-intake.js +94 -0
- package/dist/runtime/signal-intake.js.map +1 -0
- package/dist/runtime/signal-metadata.d.ts +242 -0
- package/dist/runtime/signal-metadata.d.ts.map +1 -0
- package/dist/runtime/signal-metadata.js +368 -0
- package/dist/runtime/signal-metadata.js.map +1 -0
- package/dist/server/browser-server.d.ts +49 -0
- package/dist/server/browser-server.d.ts.map +1 -0
- package/dist/server/browser-server.js +633 -0
- package/dist/server/browser-server.js.map +1 -0
- package/dist/server/durable-subscription-bindings.d.ts +122 -0
- package/dist/server/durable-subscription-bindings.d.ts.map +1 -0
- package/dist/server/durable-subscription-bindings.js +524 -0
- package/dist/server/durable-subscription-bindings.js.map +1 -0
- package/dist/server/environment-attachment.d.ts +356 -0
- package/dist/server/environment-attachment.d.ts.map +1 -0
- package/dist/server/environment-attachment.js +2061 -0
- package/dist/server/environment-attachment.js.map +1 -0
- package/dist/server/environment-delivery-records.d.ts +77 -0
- package/dist/server/environment-delivery-records.d.ts.map +1 -0
- package/dist/server/environment-delivery-records.js +282 -0
- package/dist/server/environment-delivery-records.js.map +1 -0
- package/dist/server/environment-delivery-worker.d.ts +161 -0
- package/dist/server/environment-delivery-worker.d.ts.map +1 -0
- package/dist/server/environment-delivery-worker.js +664 -0
- package/dist/server/environment-delivery-worker.js.map +1 -0
- package/dist/server/environment.d.ts +43 -0
- package/dist/server/environment.d.ts.map +1 -0
- package/dist/server/environment.js +90 -0
- package/dist/server/environment.js.map +1 -0
- package/dist/server/managed-child-subscription.d.ts +11 -0
- package/dist/server/managed-child-subscription.d.ts.map +1 -0
- package/dist/server/managed-child-subscription.js +44 -0
- package/dist/server/managed-child-subscription.js.map +1 -0
- package/dist/server/managed-server-application.d.ts +324 -0
- package/dist/server/managed-server-application.d.ts.map +1 -0
- package/dist/server/managed-server-application.js +937 -0
- package/dist/server/managed-server-application.js.map +1 -0
- package/dist/server/node-coordinator.d.ts +191 -0
- package/dist/server/node-coordinator.d.ts.map +1 -0
- package/dist/server/node-coordinator.js +552 -0
- package/dist/server/node-coordinator.js.map +1 -0
- package/dist/server/process-server-coordinator.d.ts +15 -0
- package/dist/server/process-server-coordinator.d.ts.map +1 -0
- package/dist/server/process-server-coordinator.js +112 -0
- package/dist/server/process-server-coordinator.js.map +1 -0
- package/dist/server/reserved-spine-rpc-paths.json +8 -0
- package/dist/server/retryable-close.d.ts +28 -0
- package/dist/server/retryable-close.d.ts.map +1 -0
- package/dist/server/retryable-close.js +129 -0
- package/dist/server/retryable-close.js.map +1 -0
- package/dist/server/server-environment.d.ts +180 -0
- package/dist/server/server-environment.d.ts.map +1 -0
- package/dist/server/server-environment.js +418 -0
- package/dist/server/server-environment.js.map +1 -0
- package/dist/server/server-log.d.ts +18 -0
- package/dist/server/server-log.d.ts.map +1 -0
- package/dist/server/server-log.js +96 -0
- package/dist/server/server-log.js.map +1 -0
- package/dist/server/server.d.ts +389 -0
- package/dist/server/server.d.ts.map +1 -0
- package/dist/server/server.js +752 -0
- package/dist/server/server.js.map +1 -0
- package/dist/services/spine-services.d.ts +88 -0
- package/dist/services/spine-services.d.ts.map +1 -0
- package/dist/services/spine-services.js +1586 -0
- package/dist/services/spine-services.js.map +1 -0
- package/dist/stand/stand.d.ts +294 -0
- package/dist/stand/stand.d.ts.map +1 -0
- package/dist/stand/stand.js +680 -0
- package/dist/stand/stand.js.map +1 -0
- package/dist/stand/subscription-observer.d.ts +53 -0
- package/dist/stand/subscription-observer.d.ts.map +1 -0
- package/dist/stand/subscription-observer.js +444 -0
- package/dist/stand/subscription-observer.js.map +1 -0
- package/dist/stand/subscription-records.d.ts +12 -0
- package/dist/stand/subscription-records.d.ts.map +1 -0
- package/dist/stand/subscription-records.js +99 -0
- package/dist/stand/subscription-records.js.map +1 -0
- package/dist/stand/subscription-registry.d.ts +285 -0
- package/dist/stand/subscription-registry.d.ts.map +1 -0
- package/dist/stand/subscription-registry.js +403 -0
- package/dist/stand/subscription-registry.js.map +1 -0
- package/dist/stand/subscription-runtime.d.ts +102 -0
- package/dist/stand/subscription-runtime.d.ts.map +1 -0
- package/dist/stand/subscription-runtime.js +380 -0
- package/dist/stand/subscription-runtime.js.map +1 -0
- package/dist/testing/index.d.ts +14 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +34 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +65 -0
|
@@ -0,0 +1,633 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026, CodeMatters. All rights reserved.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
|
5
|
+
* in compliance with the License. You may obtain a copy of the License at
|
|
6
|
+
*
|
|
7
|
+
* https://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
*
|
|
9
|
+
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
|
10
|
+
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
|
11
|
+
* or implied. See the License for the specific language governing permissions and limitations under
|
|
12
|
+
* the License.
|
|
13
|
+
*/
|
|
14
|
+
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
15
|
+
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
|
16
|
+
/* eslint-disable @typescript-eslint/require-await */
|
|
17
|
+
import * as http from "node:http";
|
|
18
|
+
import { createNativeGatewayServices, DynamicSubscriptionCreator, DynamicUnaryForwarder, InMemorySubscriptionBindings, NativeSubscriptionCreator, SubscriptionGateway, TransportFacts, UnaryGateway, } from "@spine-event-engine/auth";
|
|
19
|
+
import { connectNodeAdapter, createGrpcTransport, Http2SessionManager, } from "@connectrpc/connect-node";
|
|
20
|
+
import { AuthenticationService } from "@spine-event-engine/proto/auth";
|
|
21
|
+
import { ApplicationNode } from "@spine-event-engine/deployment";
|
|
22
|
+
import { CommandService, QueryService, SubscriptionService, } from "@spine-event-engine/proto/client";
|
|
23
|
+
import { attachDurableSubscriptionCleanup, isDurableSubscriptionBindings, } from "./durable-subscription-bindings.js";
|
|
24
|
+
import reservedSpineRpcPaths from "./reserved-spine-rpc-paths.json" with { type: "json" };
|
|
25
|
+
const gracefulBrowserDrainMs = 100;
|
|
26
|
+
const reservedPathSet = new Set(reservedSpineRpcPaths);
|
|
27
|
+
/**
|
|
28
|
+
* Runs the private native endpoint behind one admitted browser listener.
|
|
29
|
+
*
|
|
30
|
+
* @internal
|
|
31
|
+
*/
|
|
32
|
+
export const BrowserServer = Object.freeze({
|
|
33
|
+
async open(native, options) {
|
|
34
|
+
BrowserServer.requireDurableBindings(options, options.production);
|
|
35
|
+
const origins = BrowserServer.origins(options.origins);
|
|
36
|
+
const authRoutes = BrowserServer.authRoutes(options.authRoutes);
|
|
37
|
+
const activeAuth = new Set();
|
|
38
|
+
const maxActiveAuthRequests = options.maxActiveAuthRequests ?? 64;
|
|
39
|
+
if (!Number.isSafeInteger(maxActiveAuthRequests) || maxActiveAuthRequests < 1)
|
|
40
|
+
throw new Error("Browser maxActiveAuthRequests must be a positive safe integer.");
|
|
41
|
+
let draining = false;
|
|
42
|
+
const running = BrowserServerValues.running(native);
|
|
43
|
+
const backendBaseUrls = native === undefined
|
|
44
|
+
? []
|
|
45
|
+
: Array.isArray(native)
|
|
46
|
+
? BrowserServer.backendUrls(native)
|
|
47
|
+
: [
|
|
48
|
+
typeof native === "string"
|
|
49
|
+
? native
|
|
50
|
+
: BrowserServerValues.requiredRunning(running).baseUrl,
|
|
51
|
+
];
|
|
52
|
+
const fixedNodes = backendBaseUrls.map((endpoint, index) => new ApplicationNode({ id: `fixed/${index.toString()}`, endpoint }));
|
|
53
|
+
let dynamic;
|
|
54
|
+
let stopDiscovery;
|
|
55
|
+
const forwarder = (dynamic = new DynamicUnaryForwarder({
|
|
56
|
+
create: async (node) => {
|
|
57
|
+
const manager = options.dynamicManagerFactory?.(node) ??
|
|
58
|
+
new Http2SessionManager(node.endpoint, undefined, node.tlsServerName === undefined ? undefined : { servername: node.tlsServerName });
|
|
59
|
+
const client = new NativeSubscriptionCreator(createGrpcTransport({
|
|
60
|
+
...BrowserServer.dynamicTransportOptions(node),
|
|
61
|
+
sessionManager: manager,
|
|
62
|
+
}));
|
|
63
|
+
return {
|
|
64
|
+
forward: client.forward.bind(client),
|
|
65
|
+
subscribe: client.subscribe.bind(client),
|
|
66
|
+
activate: client.activate.bind(client),
|
|
67
|
+
cancel: client.cancel.bind(client),
|
|
68
|
+
dispose: client.dispose.bind(client),
|
|
69
|
+
close: async () => {
|
|
70
|
+
manager.abort();
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
},
|
|
74
|
+
}));
|
|
75
|
+
const creator = new DynamicSubscriptionCreator(dynamic);
|
|
76
|
+
const bindings = options.bindings ??
|
|
77
|
+
new InMemorySubscriptionBindings({
|
|
78
|
+
nextId: () => globalThis.crypto.randomUUID(),
|
|
79
|
+
dispose: (definition, signal) => creator.cancel({ wire: definition }, signal),
|
|
80
|
+
});
|
|
81
|
+
const requests = BrowserServer.requests(options);
|
|
82
|
+
const admission = options.publicAccess === true
|
|
83
|
+
? { publicAccess: true }
|
|
84
|
+
: { sessions: options.sessions };
|
|
85
|
+
const unary = new UnaryGateway({
|
|
86
|
+
...(options.registry === undefined ? {} : { registry: options.registry }),
|
|
87
|
+
maxRequestBytes: 1_048_576,
|
|
88
|
+
...admission,
|
|
89
|
+
authorize: options.authorize,
|
|
90
|
+
contexts: options.contexts,
|
|
91
|
+
clock: options.clock,
|
|
92
|
+
forward: forwarder.forward.bind(forwarder),
|
|
93
|
+
});
|
|
94
|
+
const subscriptions = new SubscriptionGateway({
|
|
95
|
+
bindings,
|
|
96
|
+
...admission,
|
|
97
|
+
authorize: options.authorize,
|
|
98
|
+
contexts: options.contexts,
|
|
99
|
+
clock: options.clock,
|
|
100
|
+
creator,
|
|
101
|
+
});
|
|
102
|
+
try {
|
|
103
|
+
if (options.discovery !== undefined)
|
|
104
|
+
stopDiscovery = await BrowserServerValues.watch(options.discovery, dynamic);
|
|
105
|
+
else
|
|
106
|
+
await dynamic.reconcile(fixedNodes);
|
|
107
|
+
if (isDurableSubscriptionBindings(bindings))
|
|
108
|
+
attachDurableSubscriptionCleanup(bindings, (definition, signal) => creator.cancel({ wire: definition }, signal));
|
|
109
|
+
const durableBindings = bindings;
|
|
110
|
+
if (durableBindings.recoverActive !== undefined) {
|
|
111
|
+
const now = options.clock.now();
|
|
112
|
+
const nowMs = Number(now.seconds.toString()) * 1_000 +
|
|
113
|
+
Math.floor(Number(now.nanos.toString()) / 1_000_000);
|
|
114
|
+
if (Number.isSafeInteger(nowMs))
|
|
115
|
+
await durableBindings.recoverActive({
|
|
116
|
+
nowMs,
|
|
117
|
+
onDefinition: async (definition, whenExpires) => {
|
|
118
|
+
await creator.rehydrate(definition);
|
|
119
|
+
subscriptions.scheduleExpiry(whenExpires);
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
const failures = [error];
|
|
126
|
+
for (const cleanup of [
|
|
127
|
+
() => subscriptions.close(),
|
|
128
|
+
() => stopDiscovery?.(),
|
|
129
|
+
() => dynamic.close(),
|
|
130
|
+
() => running?.close(),
|
|
131
|
+
])
|
|
132
|
+
try {
|
|
133
|
+
await cleanup();
|
|
134
|
+
}
|
|
135
|
+
catch (cleanupError) {
|
|
136
|
+
failures.push(cleanupError);
|
|
137
|
+
}
|
|
138
|
+
if (failures.length === 1)
|
|
139
|
+
throw error;
|
|
140
|
+
throw new AggregateError(failures, "Browser subscription recovery startup rollback failed.");
|
|
141
|
+
}
|
|
142
|
+
const services = createNativeGatewayServices({ unary, subscriptions, requests });
|
|
143
|
+
const handler = connectNodeAdapter({
|
|
144
|
+
routes: (router) => {
|
|
145
|
+
router.service(AuthenticationService, services.authentication);
|
|
146
|
+
router.service(CommandService, services.command);
|
|
147
|
+
router.service(QueryService, services.query);
|
|
148
|
+
router.service(SubscriptionService, services.subscription);
|
|
149
|
+
},
|
|
150
|
+
readMaxBytes: options.readMaxBytes,
|
|
151
|
+
writeMaxBytes: options.writeMaxBytes,
|
|
152
|
+
});
|
|
153
|
+
const server = http.createServer((request, response) => {
|
|
154
|
+
if (draining) {
|
|
155
|
+
response.statusCode = 503;
|
|
156
|
+
response.end();
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
const path = request.url ?? "";
|
|
160
|
+
const auth = authRoutes.get(path);
|
|
161
|
+
if (auth !== undefined && request.method === auth.method) {
|
|
162
|
+
if (activeAuth.size >= maxActiveAuthRequests) {
|
|
163
|
+
response.statusCode = 503;
|
|
164
|
+
response.end();
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
void BrowserServer.dispatchAuth(request, response, auth, activeAuth, options.writeMaxBytes);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
if (auth !== undefined) {
|
|
171
|
+
const origin = request.headers.origin;
|
|
172
|
+
if (request.method === "OPTIONS" && origin !== undefined && auth.origins.includes(origin)) {
|
|
173
|
+
response.setHeader("access-control-allow-origin", origin);
|
|
174
|
+
response.setHeader("access-control-allow-methods", `${auth.method},OPTIONS`);
|
|
175
|
+
response.statusCode = 204;
|
|
176
|
+
}
|
|
177
|
+
else
|
|
178
|
+
response.statusCode = request.method === "OPTIONS" ? 403 : 405;
|
|
179
|
+
response.end();
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
const origin = request.headers.origin;
|
|
183
|
+
if (origin === undefined || !origins.has(origin)) {
|
|
184
|
+
response.statusCode = 403;
|
|
185
|
+
response.end();
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
response.setHeader("access-control-allow-origin", origin);
|
|
189
|
+
response.setHeader("access-control-allow-credentials", "true");
|
|
190
|
+
response.setHeader("access-control-expose-headers", "grpc-status,grpc-message");
|
|
191
|
+
response.setHeader("vary", "Origin");
|
|
192
|
+
if (request.method === "OPTIONS") {
|
|
193
|
+
response.setHeader("access-control-allow-methods", "POST,OPTIONS");
|
|
194
|
+
response.setHeader("access-control-allow-headers", "authorization,content-type,connect-protocol-version,x-csrf-token");
|
|
195
|
+
response.setHeader("access-control-max-age", "600");
|
|
196
|
+
response.statusCode = 204;
|
|
197
|
+
response.end();
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
handler(request, response);
|
|
201
|
+
});
|
|
202
|
+
let address;
|
|
203
|
+
try {
|
|
204
|
+
address = await BrowserServer.listen(server, options.host, options.port);
|
|
205
|
+
}
|
|
206
|
+
catch (error) {
|
|
207
|
+
const cleanup = await Promise.allSettled([
|
|
208
|
+
subscriptions.close(),
|
|
209
|
+
stopDiscovery?.(),
|
|
210
|
+
dynamic.close(),
|
|
211
|
+
BrowserServer.closeListener(server),
|
|
212
|
+
]);
|
|
213
|
+
const failures = cleanup.flatMap((result) => result.status === "rejected" ? [result.reason] : []);
|
|
214
|
+
if (failures.length > 0)
|
|
215
|
+
throw new AggregateError([error, ...failures], "Server browser startup rollback failed.");
|
|
216
|
+
throw error;
|
|
217
|
+
}
|
|
218
|
+
return new RunningBrowserServer(server, running, subscriptions, address, activeAuth, () => {
|
|
219
|
+
draining = true;
|
|
220
|
+
}, stopDiscovery, dynamic);
|
|
221
|
+
},
|
|
222
|
+
requests(options) {
|
|
223
|
+
return {
|
|
224
|
+
credential: (context) => {
|
|
225
|
+
const headers = Object.fromEntries(context.requestHeader.entries());
|
|
226
|
+
const extracted = options.cookies?.extract(headers);
|
|
227
|
+
if (extracted !== undefined) {
|
|
228
|
+
return extracted.kind === "rejected" ? { kind: "bearer", value: "" } : extracted;
|
|
229
|
+
}
|
|
230
|
+
const authorization = context.requestHeader.get("authorization") ?? "";
|
|
231
|
+
return /^Bearer [^\s]+$/.test(authorization)
|
|
232
|
+
? { kind: "bearer", value: authorization.slice("Bearer ".length) }
|
|
233
|
+
: undefined;
|
|
234
|
+
},
|
|
235
|
+
transport: (context) => {
|
|
236
|
+
const origin = context.requestHeader.get("origin");
|
|
237
|
+
return TransportFacts.from({
|
|
238
|
+
service: "browser",
|
|
239
|
+
method: "gateway",
|
|
240
|
+
...(origin === null ? {} : { origin }),
|
|
241
|
+
headers: Object.fromEntries(context.requestHeader.entries()),
|
|
242
|
+
});
|
|
243
|
+
},
|
|
244
|
+
};
|
|
245
|
+
},
|
|
246
|
+
origins(values) {
|
|
247
|
+
const origins = new Set(values.map((value) => {
|
|
248
|
+
let origin;
|
|
249
|
+
try {
|
|
250
|
+
origin = new URL(value);
|
|
251
|
+
}
|
|
252
|
+
catch {
|
|
253
|
+
throw new Error("Server browser origins must be canonical HTTP(S) origins.");
|
|
254
|
+
}
|
|
255
|
+
if ((origin.protocol !== "http:" && origin.protocol !== "https:") ||
|
|
256
|
+
origin.origin !== value ||
|
|
257
|
+
origin.pathname !== "/" ||
|
|
258
|
+
origin.search ||
|
|
259
|
+
origin.hash) {
|
|
260
|
+
throw new Error("Server browser origins must be canonical HTTP(S) origins.");
|
|
261
|
+
}
|
|
262
|
+
return value;
|
|
263
|
+
}));
|
|
264
|
+
if (origins.size === 0 || origins.size !== values.length)
|
|
265
|
+
throw new Error("Server browser origins must be unique and non-empty.");
|
|
266
|
+
return origins;
|
|
267
|
+
},
|
|
268
|
+
backendUrl(value) {
|
|
269
|
+
let backend;
|
|
270
|
+
try {
|
|
271
|
+
backend = new URL(value);
|
|
272
|
+
}
|
|
273
|
+
catch {
|
|
274
|
+
throw new Error("Server browser backend must be a canonical HTTP(S) origin.");
|
|
275
|
+
}
|
|
276
|
+
if ((backend.protocol !== "http:" && backend.protocol !== "https:") ||
|
|
277
|
+
backend.origin !== value ||
|
|
278
|
+
backend.pathname !== "/" ||
|
|
279
|
+
backend.search ||
|
|
280
|
+
backend.hash ||
|
|
281
|
+
backend.username ||
|
|
282
|
+
backend.password) {
|
|
283
|
+
throw new Error("Server browser backend must be a canonical HTTP(S) origin.");
|
|
284
|
+
}
|
|
285
|
+
return value;
|
|
286
|
+
},
|
|
287
|
+
backendUrls(values) {
|
|
288
|
+
if (values.length < 1)
|
|
289
|
+
throw new Error("Server browser backends must contain at least one origin.");
|
|
290
|
+
const urls = values.map((value) => BrowserServer.backendUrl(value));
|
|
291
|
+
if (new Set(urls).size !== urls.length)
|
|
292
|
+
throw new Error("Server browser backends must be unique canonical HTTP(S) origins.");
|
|
293
|
+
return urls;
|
|
294
|
+
},
|
|
295
|
+
dynamicTransportOptions(node) {
|
|
296
|
+
return {
|
|
297
|
+
baseUrl: node.endpoint,
|
|
298
|
+
...(node.tlsServerName === undefined
|
|
299
|
+
? {}
|
|
300
|
+
: { nodeOptions: { servername: node.tlsServerName } }),
|
|
301
|
+
};
|
|
302
|
+
},
|
|
303
|
+
requireDurableBindings(options, production) {
|
|
304
|
+
const supplied = options;
|
|
305
|
+
const standalone = options.backend !== undefined || options.discovery !== undefined;
|
|
306
|
+
if (options.backend !== undefined) {
|
|
307
|
+
BrowserServer.backendUrls(BrowserServerValues.backendUrlsFor(options.backend));
|
|
308
|
+
}
|
|
309
|
+
if (standalone) {
|
|
310
|
+
if ((supplied.sessions === undefined) === (options.publicAccess !== true))
|
|
311
|
+
throw new Error("Standalone browser server requires exactly one of sessions or publicAccess.");
|
|
312
|
+
if (supplied.sessions !== undefined && typeof supplied.sessions.resolve !== "function")
|
|
313
|
+
throw new Error("Standalone browser server requires sessions with a resolver.");
|
|
314
|
+
if (typeof options.authorize !== "function")
|
|
315
|
+
throw new Error("Standalone browser server requires authorization.");
|
|
316
|
+
if (supplied.contexts === undefined ||
|
|
317
|
+
typeof supplied.contexts.resolve !== "function" ||
|
|
318
|
+
typeof supplied.contexts.resolveContext !== "function")
|
|
319
|
+
throw new Error("Standalone browser server requires context resolution.");
|
|
320
|
+
if (supplied.clock === undefined || typeof supplied.clock.now !== "function")
|
|
321
|
+
throw new Error("Standalone browser server requires a clock.");
|
|
322
|
+
if (options.bindings === undefined && options.publicAccess !== true)
|
|
323
|
+
throw new Error("Standalone browser server requires explicit subscription bindings.");
|
|
324
|
+
}
|
|
325
|
+
if (supplied.publicAccess === true && supplied.bindings !== undefined)
|
|
326
|
+
throw new Error("Public browser access owns process-local subscription bindings.");
|
|
327
|
+
if (standalone && production && options.registry === undefined)
|
|
328
|
+
throw new Error("Production standalone browser server requires a type registry.");
|
|
329
|
+
const bindings = options.bindings;
|
|
330
|
+
if (production && options.publicAccess !== true && !isDurableSubscriptionBindings(bindings))
|
|
331
|
+
throw new Error("Production browser server requires durable subscription bindings.");
|
|
332
|
+
if (standalone &&
|
|
333
|
+
options.publicAccess !== true &&
|
|
334
|
+
production &&
|
|
335
|
+
(bindings === undefined ||
|
|
336
|
+
!("namespace" in bindings) ||
|
|
337
|
+
typeof bindings.namespace !== "string" ||
|
|
338
|
+
!bindings.namespace.trim()))
|
|
339
|
+
throw new Error("Production standalone browser server requires named durable subscription bindings.");
|
|
340
|
+
},
|
|
341
|
+
authRoutes(routes) {
|
|
342
|
+
const result = new Map();
|
|
343
|
+
for (const route of routes ?? []) {
|
|
344
|
+
const method = route.method;
|
|
345
|
+
if (method !== "GET" && method !== "POST")
|
|
346
|
+
throw new Error("Browser auth routes must use GET or POST.");
|
|
347
|
+
if (reservedPathSet.has(route.path))
|
|
348
|
+
throw new Error("Browser auth routes must not use reserved Spine RPC paths.");
|
|
349
|
+
if (!/^\/(?!\/)(?:[A-Za-z0-9_-]+\/)*[A-Za-z0-9_-]+$/.test(route.path))
|
|
350
|
+
throw new Error("Browser auth routes must use exact canonical non-root paths.");
|
|
351
|
+
if (!Number.isSafeInteger(route.maxRequestBytes) ||
|
|
352
|
+
route.maxRequestBytes <= 0 ||
|
|
353
|
+
route.maxRequestBytes > 4_194_304)
|
|
354
|
+
throw new Error("Browser auth route maxRequestBytes must be a positive safe transport bound.");
|
|
355
|
+
if (!Number.isSafeInteger(route.timeoutMs) ||
|
|
356
|
+
route.timeoutMs < 1 ||
|
|
357
|
+
route.timeoutMs > 2_147_483_647)
|
|
358
|
+
throw new Error("Browser auth route timeoutMs must be a safe positive millisecond duration.");
|
|
359
|
+
BrowserServer.origins(route.origins);
|
|
360
|
+
if (result.has(route.path))
|
|
361
|
+
throw new Error("Browser auth routes must use one method per canonical path.");
|
|
362
|
+
result.set(route.path, route);
|
|
363
|
+
}
|
|
364
|
+
return result;
|
|
365
|
+
},
|
|
366
|
+
async dispatchAuth(request, response, route, active, writeMaxBytes) {
|
|
367
|
+
const origin = request.headers.origin;
|
|
368
|
+
if ((origin === undefined && route.allowMissingOrigin !== true) ||
|
|
369
|
+
(origin !== undefined && !route.origins.includes(origin))) {
|
|
370
|
+
response.statusCode = 403;
|
|
371
|
+
response.end();
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
const length = request.headers["content-length"];
|
|
375
|
+
if (length !== undefined && (!/^\d+$/.test(length) || Number(length) > route.maxRequestBytes)) {
|
|
376
|
+
response.statusCode = 413;
|
|
377
|
+
response.end();
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
const controller = new AbortController();
|
|
381
|
+
active.add(controller);
|
|
382
|
+
const timer = setTimeout(() => {
|
|
383
|
+
controller.abort();
|
|
384
|
+
request.resume();
|
|
385
|
+
}, route.timeoutMs);
|
|
386
|
+
const abort = () => {
|
|
387
|
+
controller.abort();
|
|
388
|
+
};
|
|
389
|
+
request.once("aborted", abort);
|
|
390
|
+
response.once("close", abort);
|
|
391
|
+
let rejectAbort;
|
|
392
|
+
const aborted = new Promise((_resolve, reject) => {
|
|
393
|
+
rejectAbort = reject;
|
|
394
|
+
});
|
|
395
|
+
let responseReader;
|
|
396
|
+
const onAbort = () => {
|
|
397
|
+
rejectAbort(new Error("browser auth request aborted"));
|
|
398
|
+
void responseReader?.cancel().catch(() => undefined);
|
|
399
|
+
};
|
|
400
|
+
controller.signal.addEventListener("abort", onAbort, { once: true });
|
|
401
|
+
try {
|
|
402
|
+
const requestChunks = [];
|
|
403
|
+
let size = 0;
|
|
404
|
+
const requestBody = request[Symbol.asyncIterator]();
|
|
405
|
+
for (;;) {
|
|
406
|
+
const next = await Promise.race([requestBody.next(), aborted]);
|
|
407
|
+
if (next.done)
|
|
408
|
+
break;
|
|
409
|
+
const bytes = Buffer.from(next.value);
|
|
410
|
+
size += bytes.byteLength;
|
|
411
|
+
if (size > route.maxRequestBytes) {
|
|
412
|
+
response.statusCode = 413;
|
|
413
|
+
response.end();
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
requestChunks.push(bytes);
|
|
417
|
+
}
|
|
418
|
+
const url = new URL(request.url ?? route.path, `http://${request.headers.host ?? "localhost"}`);
|
|
419
|
+
const input = new Request(url, {
|
|
420
|
+
method: route.method,
|
|
421
|
+
headers: request.headers,
|
|
422
|
+
...(size === 0 ? {} : { body: Buffer.concat(requestChunks) }),
|
|
423
|
+
signal: controller.signal,
|
|
424
|
+
});
|
|
425
|
+
const result = await Promise.race([route.onRequest(input, controller.signal), aborted]);
|
|
426
|
+
if (controller.signal.aborted) {
|
|
427
|
+
if (!response.writableEnded) {
|
|
428
|
+
response.statusCode = 504;
|
|
429
|
+
response.end();
|
|
430
|
+
}
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
responseReader = result.body?.getReader();
|
|
434
|
+
const responseChunks = [];
|
|
435
|
+
let responseBytes = 0;
|
|
436
|
+
if (responseReader !== undefined) {
|
|
437
|
+
for (;;) {
|
|
438
|
+
const next = await Promise.race([responseReader.read(), aborted]);
|
|
439
|
+
if (next.done)
|
|
440
|
+
break;
|
|
441
|
+
responseBytes += next.value.byteLength;
|
|
442
|
+
if (responseBytes > writeMaxBytes) {
|
|
443
|
+
await responseReader.cancel().catch(() => undefined);
|
|
444
|
+
response.statusCode = 413;
|
|
445
|
+
response.end();
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
responseChunks.push(next.value);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
response.statusCode = result.status;
|
|
452
|
+
result.headers.forEach((value, key) => response.setHeader(key, value));
|
|
453
|
+
response.end(Buffer.concat(responseChunks));
|
|
454
|
+
}
|
|
455
|
+
catch {
|
|
456
|
+
if (!response.writableEnded) {
|
|
457
|
+
if (controller.signal.aborted) {
|
|
458
|
+
response.shouldKeepAlive = false;
|
|
459
|
+
response.setHeader("connection", "close");
|
|
460
|
+
}
|
|
461
|
+
response.statusCode = controller.signal.aborted ? 504 : 500;
|
|
462
|
+
response.end();
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
finally {
|
|
466
|
+
clearTimeout(timer);
|
|
467
|
+
request.off("aborted", abort);
|
|
468
|
+
response.off("close", abort);
|
|
469
|
+
controller.signal.removeEventListener("abort", onAbort);
|
|
470
|
+
active.delete(controller);
|
|
471
|
+
}
|
|
472
|
+
},
|
|
473
|
+
listen(server, host, port) {
|
|
474
|
+
return new Promise((resolve, reject) => {
|
|
475
|
+
const onError = (error) => {
|
|
476
|
+
reject(error);
|
|
477
|
+
};
|
|
478
|
+
server.once("error", onError);
|
|
479
|
+
server.listen(port, host, () => {
|
|
480
|
+
server.off("error", onError);
|
|
481
|
+
resolve(server.address());
|
|
482
|
+
});
|
|
483
|
+
});
|
|
484
|
+
},
|
|
485
|
+
closeListener(server) {
|
|
486
|
+
return new Promise((resolve, reject) => {
|
|
487
|
+
if (!server.listening) {
|
|
488
|
+
resolve();
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
491
|
+
const timer = setTimeout(() => {
|
|
492
|
+
server.closeAllConnections();
|
|
493
|
+
}, gracefulBrowserDrainMs);
|
|
494
|
+
timer.unref();
|
|
495
|
+
server.close((error) => {
|
|
496
|
+
clearTimeout(timer);
|
|
497
|
+
if (error === undefined)
|
|
498
|
+
resolve();
|
|
499
|
+
else
|
|
500
|
+
reject(error);
|
|
501
|
+
});
|
|
502
|
+
});
|
|
503
|
+
},
|
|
504
|
+
});
|
|
505
|
+
const BrowserServerValues = Object.freeze({
|
|
506
|
+
async watch(source, forwarder) {
|
|
507
|
+
return await source.watch((nodes) => {
|
|
508
|
+
void forwarder.reconcile(nodes);
|
|
509
|
+
});
|
|
510
|
+
},
|
|
511
|
+
requiredRunning(value) {
|
|
512
|
+
if (value === undefined)
|
|
513
|
+
throw new Error("Browser server local backend is absent.");
|
|
514
|
+
return value;
|
|
515
|
+
},
|
|
516
|
+
firstCreator(values) {
|
|
517
|
+
const creator = values[0];
|
|
518
|
+
if (creator === undefined)
|
|
519
|
+
throw new Error("Browser server backend is absent.");
|
|
520
|
+
return creator;
|
|
521
|
+
},
|
|
522
|
+
running(native) {
|
|
523
|
+
return native === undefined || typeof native === "string" || Array.isArray(native)
|
|
524
|
+
? undefined
|
|
525
|
+
: native;
|
|
526
|
+
},
|
|
527
|
+
backendUrlsFor(backend) {
|
|
528
|
+
const source = backend;
|
|
529
|
+
if (typeof source.baseUrl === "string" && source.baseUrls === undefined)
|
|
530
|
+
return [source.baseUrl];
|
|
531
|
+
if (source.baseUrl === undefined && Array.isArray(source.baseUrls)) {
|
|
532
|
+
const values = source.baseUrls;
|
|
533
|
+
if (values.some((value) => typeof value !== "string"))
|
|
534
|
+
throw new Error("Server browser backend URLs must be strings.");
|
|
535
|
+
return values;
|
|
536
|
+
}
|
|
537
|
+
throw new Error("Server browser backend must configure exactly one of baseUrl or baseUrls.");
|
|
538
|
+
},
|
|
539
|
+
});
|
|
540
|
+
class RunningBrowserServer {
|
|
541
|
+
#server;
|
|
542
|
+
#subscriptions;
|
|
543
|
+
#native;
|
|
544
|
+
#activeAuth;
|
|
545
|
+
#onDrain;
|
|
546
|
+
#stopDiscovery;
|
|
547
|
+
#dynamic;
|
|
548
|
+
host;
|
|
549
|
+
port;
|
|
550
|
+
baseUrl;
|
|
551
|
+
#closed;
|
|
552
|
+
#listenerClose;
|
|
553
|
+
#listenerClosed = false;
|
|
554
|
+
#subscriptionsClosed = false;
|
|
555
|
+
#discoveryStopped = false;
|
|
556
|
+
#dynamicClosed = false;
|
|
557
|
+
#nativeClosed = false;
|
|
558
|
+
constructor(server, native, subscriptions, address, activeAuth, onDrain, stopDiscovery, dynamic) {
|
|
559
|
+
this.#server = server;
|
|
560
|
+
this.#native = native;
|
|
561
|
+
this.#subscriptions = subscriptions;
|
|
562
|
+
this.#activeAuth = activeAuth;
|
|
563
|
+
this.#onDrain = onDrain;
|
|
564
|
+
this.#stopDiscovery = stopDiscovery;
|
|
565
|
+
this.#dynamic = dynamic;
|
|
566
|
+
this.host = typeof address.address === "string" ? address.address : "127.0.0.1";
|
|
567
|
+
this.port = address.port;
|
|
568
|
+
const host = this.host.includes(":") && !this.host.startsWith("[") ? `[${this.host}]` : this.host;
|
|
569
|
+
this.baseUrl = `http://${host}:${this.port.toString()}`;
|
|
570
|
+
}
|
|
571
|
+
close() {
|
|
572
|
+
this.#closed ??= this.#closeOnce().catch((error) => {
|
|
573
|
+
this.#closed = undefined;
|
|
574
|
+
throw error;
|
|
575
|
+
});
|
|
576
|
+
return this.#closed;
|
|
577
|
+
}
|
|
578
|
+
async #closeOnce() {
|
|
579
|
+
this.#onDrain();
|
|
580
|
+
for (const controller of this.#activeAuth)
|
|
581
|
+
controller.abort();
|
|
582
|
+
const listener = this.#listenerClosed ? undefined : this.#closeListenerPhase();
|
|
583
|
+
const failures = [];
|
|
584
|
+
await this.#phase(this.#subscriptionsClosed ? undefined : this.#subscriptions.close(), () => {
|
|
585
|
+
this.#subscriptionsClosed = true;
|
|
586
|
+
}, failures);
|
|
587
|
+
await this.#phase(this.#discoveryStopped ? undefined : this.#stopDiscovery?.(), () => {
|
|
588
|
+
this.#discoveryStopped = true;
|
|
589
|
+
}, failures);
|
|
590
|
+
await this.#phase(this.#dynamicClosed ? undefined : this.#dynamic?.close(), () => {
|
|
591
|
+
this.#dynamicClosed = true;
|
|
592
|
+
}, failures);
|
|
593
|
+
await this.#phase(listener, () => {
|
|
594
|
+
this.#listenerClosed = true;
|
|
595
|
+
}, failures);
|
|
596
|
+
await this.#phase(this.#native === undefined || this.#nativeClosed ? undefined : this.#native.close(), () => {
|
|
597
|
+
this.#nativeClosed = true;
|
|
598
|
+
}, failures);
|
|
599
|
+
if (failures.length > 0) {
|
|
600
|
+
const reason = failures[0];
|
|
601
|
+
const primary = reason instanceof Error ? reason : new Error(String(reason));
|
|
602
|
+
if (failures.length > 1)
|
|
603
|
+
Object.defineProperty(primary, "cleanupErrors", { value: failures.slice(1) });
|
|
604
|
+
throw primary;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
async #phase(operation, onSuccess, failures) {
|
|
608
|
+
if (operation === undefined)
|
|
609
|
+
return;
|
|
610
|
+
try {
|
|
611
|
+
await operation;
|
|
612
|
+
onSuccess();
|
|
613
|
+
}
|
|
614
|
+
catch (error) {
|
|
615
|
+
failures.push(error);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
#closeListenerPhase() {
|
|
619
|
+
const current = this.#listenerClose;
|
|
620
|
+
if (current !== undefined)
|
|
621
|
+
return current;
|
|
622
|
+
const closing = BrowserServer.closeListener(this.#server);
|
|
623
|
+
this.#listenerClose = closing;
|
|
624
|
+
void closing.then(() => {
|
|
625
|
+
this.#listenerClosed = true;
|
|
626
|
+
this.#listenerClose = undefined;
|
|
627
|
+
}, () => {
|
|
628
|
+
this.#listenerClose = undefined;
|
|
629
|
+
});
|
|
630
|
+
return closing;
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
//# sourceMappingURL=browser-server.js.map
|