@ny4n/server 0.0.0-stage → 1.0.1
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 +21 -0
- package/README.md +62 -2
- package/dist/connection.d.ts +32 -0
- package/dist/connection.d.ts.map +1 -0
- package/dist/connection.js +4 -0
- package/dist/connection.js.map +1 -0
- package/dist/errors.d.ts +25 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +41 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/listener.d.ts +8 -0
- package/dist/listener.d.ts.map +1 -0
- package/dist/listener.js +2 -0
- package/dist/listener.js.map +1 -0
- package/dist/server.d.ts +45 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +547 -0
- package/dist/server.js.map +1 -0
- package/dist/session-router.d.ts +39 -0
- package/dist/session-router.d.ts.map +1 -0
- package/dist/session-router.js +264 -0
- package/dist/session-router.js.map +1 -0
- package/dist/testing/client.d.ts +35 -0
- package/dist/testing/client.d.ts.map +1 -0
- package/dist/testing/client.js +138 -0
- package/dist/testing/client.js.map +1 -0
- package/dist/testing/host.d.ts +55 -0
- package/dist/testing/host.d.ts.map +1 -0
- package/dist/testing/host.js +178 -0
- package/dist/testing/host.js.map +1 -0
- package/dist/testing/index.d.ts +6 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +4 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/server.d.ts +13 -0
- package/dist/testing/server.d.ts.map +1 -0
- package/dist/testing/server.js +17 -0
- package/dist/testing/server.js.map +1 -0
- package/dist/transports/unix/address.d.ts +3 -0
- package/dist/transports/unix/address.d.ts.map +1 -0
- package/dist/transports/unix/address.js +9 -0
- package/dist/transports/unix/address.js.map +1 -0
- package/dist/transports/unix/index.d.ts +5 -0
- package/dist/transports/unix/index.d.ts.map +1 -0
- package/dist/transports/unix/index.js +4 -0
- package/dist/transports/unix/index.js.map +1 -0
- package/dist/transports/unix/listener.d.ts +24 -0
- package/dist/transports/unix/listener.d.ts.map +1 -0
- package/dist/transports/unix/listener.js +421 -0
- package/dist/transports/unix/listener.js.map +1 -0
- package/dist/transports/unix/preset.d.ts +6 -0
- package/dist/transports/unix/preset.d.ts.map +1 -0
- package/dist/transports/unix/preset.js +22 -0
- package/dist/transports/unix/preset.js.map +1 -0
- package/dist/transports/unix/types.d.ts +15 -0
- package/dist/transports/unix/types.d.ts.map +1 -0
- package/dist/transports/unix/types.js +2 -0
- package/dist/transports/unix/types.js.map +1 -0
- package/dist/types.d.ts +52 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +55 -5
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Mario Zechner
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,63 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @ny4n/server
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Experimental local server that routes clients to application-hosted durable Sessions.
|
|
4
|
+
|
|
5
|
+
The current slice supports server- and Session-scoped facet-service routing and multi-presentation attachment. `RoutedServerServiceHost.attachClient()` creates one connection-scoped server service endpoint with narrow attachment-management capabilities. `RoutedSessionHandle.attachClient()` returns a presentation-scoped Session capability. Its `invokeService()` forwards an opaque service/member envelope to the selected Session endpoint; the server validates the attachment route but does not load the facet contract.
|
|
6
|
+
|
|
7
|
+
- server service calls and subscriptions route opaquely through the connection's `RoutedServerServiceAttachment`;
|
|
8
|
+
- the application-owned `SessionDirectory` projects the private catalog into replicated presentation-safe state;
|
|
9
|
+
- the application-owned `SessionManagement` creates, removes, attaches, and detaches Sessions without exposing route IDs in business results;
|
|
10
|
+
- attachment changes are published out of band after the router installs or clears the live route;
|
|
11
|
+
- Session service calls route through `invokeService` without server-side business-payload decoding;
|
|
12
|
+
- service subscription updates remain scoped to the requesting attachment;
|
|
13
|
+
- application observations such as transcripts route as ordinary service state without server-owned business schemas.
|
|
14
|
+
|
|
15
|
+
A Session may have multiple presentation attachments. Repeating `attach` from one connection is idempotent; every successful attachment has a server-generated `attachmentId` delivered only as routing control data. Session requests carry `{ serverId, sessionId, attachmentId }`, and the server rejects stale or mismatched routes. Losing a connection rejects its local responses but releases its attachment only after admitted service calls settle. The host decides when zero presentation demand and worker-local Harness activity permit worker retirement. Server shutdown closes every routed Session handle, releasing its worker and Session writer ownership.
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { randomUUID } from "node:crypto";
|
|
19
|
+
import {
|
|
20
|
+
type RoutedServerServiceHost,
|
|
21
|
+
type RoutedSessionHandle,
|
|
22
|
+
type ServerHost,
|
|
23
|
+
type SessionMetadata,
|
|
24
|
+
SessionNotFoundError,
|
|
25
|
+
} from "@ny4n/server";
|
|
26
|
+
import { createUnixServer, getUnixSocketPath } from "@ny4n/server/unix";
|
|
27
|
+
|
|
28
|
+
interface StoredSession extends SessionMetadata {
|
|
29
|
+
path: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async function startServer(
|
|
33
|
+
serverServices: RoutedServerServiceHost,
|
|
34
|
+
sessions: Map<string, StoredSession>,
|
|
35
|
+
openRoutedSession: (session: StoredSession) => Promise<RoutedSessionHandle>,
|
|
36
|
+
) {
|
|
37
|
+
const host: ServerHost<StoredSession> = {
|
|
38
|
+
serverServices,
|
|
39
|
+
async resolveSession(sessionId) {
|
|
40
|
+
const metadata = sessions.get(sessionId);
|
|
41
|
+
if (!metadata) throw new SessionNotFoundError(`Unknown session: ${sessionId}`);
|
|
42
|
+
return metadata;
|
|
43
|
+
},
|
|
44
|
+
openSession: (metadata) => openRoutedSession(metadata),
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const serverId = randomUUID();
|
|
48
|
+
const server = createUnixServer(host, {
|
|
49
|
+
serverId,
|
|
50
|
+
path: getUnixSocketPath(serverId, "/run/user/1000/nyan"),
|
|
51
|
+
});
|
|
52
|
+
await server.start();
|
|
53
|
+
return server;
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Applications supply a required server service host, a bounded Session resolver, and a routed Session factory. `SessionMetadata` only requires an `id`; applications may extend it with their own storage fields. Session discovery and management are application-owned services; the protocol server only asks the resolver for metadata when routing an attachment. The host owns acquiring the worker-local Session and Harness. Failures are cleaned up in that worker. Neither an open JavaScript Session nor a Harness crosses the process boundary.
|
|
58
|
+
|
|
59
|
+
`serverId` is a logical identity supplied by the launcher, not a socket address. The Unix preset requires an explicit physical `path`; `getUnixSocketPath()` derives one from a caller-selected directory. Choose a short, private runtime directory rather than deriving the route from an unbounded home-directory path. A long-lived launcher can reuse the same ID and path when replacing a server process.
|
|
60
|
+
|
|
61
|
+
`Server` composes transports through `ServerListener`; peer authentication remains application policy and is not implemented by the experimental Unix transport. The Unix submodule provides `createUnixListener()` and `createUnixServer()`. Low-level routed-envelope validation, CBOR, and framing come from `@ny4n/protocol`; Chord owns service-control parsing, error codes, snapshots and updates, and each subscription's replicated-state encoder.
|
|
62
|
+
|
|
63
|
+
Server and worker lifecycle is managed outside the public Nyan protocol. The replaceable application server converts connection attachments into private demand updates; the worker combines generation-tagged demand with authoritative Harness activity. The experimental coordinator only supplies stable routing and reports generic server-generation connection changes.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { ServiceStateEncoder } from "@ny4n/chord";
|
|
2
|
+
import type { ClientMessageDecoder, RpcTarget } from "@ny4n/protocol";
|
|
3
|
+
import type { MaybePromise, RoutedServerServiceAttachment } from "./types.ts";
|
|
4
|
+
/** An established, authorized ordered byte connection. */
|
|
5
|
+
export interface ByteConnection {
|
|
6
|
+
readonly closed: boolean;
|
|
7
|
+
send(chunk: Uint8Array): Promise<void>;
|
|
8
|
+
close(finalChunk?: Uint8Array): MaybePromise<void>;
|
|
9
|
+
}
|
|
10
|
+
export interface ByteConnectionHandler {
|
|
11
|
+
onData(chunk: Uint8Array): void;
|
|
12
|
+
onClose(): void;
|
|
13
|
+
onError(error: Error): void;
|
|
14
|
+
}
|
|
15
|
+
export type ByteConnectionAcceptor = (connection: ByteConnection) => ByteConnectionHandler;
|
|
16
|
+
export type ConnectionStage = "awaitingHello" | "handshaking" | "ready" | "closing" | "closed";
|
|
17
|
+
export interface ConnectionState {
|
|
18
|
+
connection: ByteConnection;
|
|
19
|
+
decoder: ClientMessageDecoder;
|
|
20
|
+
serviceStateEncoders: Map<string, ServiceStateEncoder>;
|
|
21
|
+
stage: ConnectionStage;
|
|
22
|
+
disconnected: boolean;
|
|
23
|
+
handshake?: Promise<void>;
|
|
24
|
+
handshakeTimeout: NodeJS.Timeout;
|
|
25
|
+
serverServices?: RoutedServerServiceAttachment;
|
|
26
|
+
activeRequests: Map<string, {
|
|
27
|
+
controller: AbortController;
|
|
28
|
+
target: RpcTarget;
|
|
29
|
+
}>;
|
|
30
|
+
}
|
|
31
|
+
export declare function isTerminalConnection(state: ConnectionState): boolean;
|
|
32
|
+
//# sourceMappingURL=connection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,KAAK,EAAE,oBAAoB,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEtE,OAAO,KAAK,EAAE,YAAY,EAAE,6BAA6B,EAAE,MAAM,YAAY,CAAC;AAE9E,0DAA0D;AAC1D,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,IAAI,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,KAAK,CAAC,UAAU,CAAC,EAAE,UAAU,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;CACnD;AAED,MAAM,WAAW,qBAAqB;IACrC,MAAM,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;IAChC,OAAO,IAAI,IAAI,CAAC;IAChB,OAAO,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,MAAM,sBAAsB,GAAG,CAAC,UAAU,EAAE,cAAc,KAAK,qBAAqB,CAAC;AAE3F,MAAM,MAAM,eAAe,GAAG,eAAe,GAAG,aAAa,GAAG,OAAO,GAAG,SAAS,GAAG,QAAQ,CAAC;AAE/F,MAAM,WAAW,eAAe;IAC/B,UAAU,EAAE,cAAc,CAAC;IAC3B,OAAO,EAAE,oBAAoB,CAAC;IAC9B,oBAAoB,EAAE,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;IACvD,KAAK,EAAE,eAAe,CAAC;IACvB,YAAY,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,gBAAgB,EAAE,MAAM,CAAC,OAAO,CAAC;IACjC,cAAc,CAAC,EAAE,6BAA6B,CAAC;IAC/C,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,UAAU,EAAE,eAAe,CAAC;QAAC,MAAM,EAAE,SAAS,CAAA;KAAE,CAAC,CAAC;CAChF;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAEpE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection.js","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"AAkCA,MAAM,UAAU,oBAAoB,CAAC,KAAsB;IAC1D,OAAO,KAAK,CAAC,YAAY,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,KAAK,KAAK,QAAQ,CAAC;AACpF,CAAC","sourcesContent":["import type { ServiceStateEncoder } from \"@ny4n/chord\";\nimport type { ClientMessageDecoder, RpcTarget } from \"@ny4n/protocol\";\n\nimport type { MaybePromise, RoutedServerServiceAttachment } from \"./types.ts\";\n\n/** An established, authorized ordered byte connection. */\nexport interface ByteConnection {\n\treadonly closed: boolean;\n\tsend(chunk: Uint8Array): Promise<void>;\n\tclose(finalChunk?: Uint8Array): MaybePromise<void>;\n}\n\nexport interface ByteConnectionHandler {\n\tonData(chunk: Uint8Array): void;\n\tonClose(): void;\n\tonError(error: Error): void;\n}\n\nexport type ByteConnectionAcceptor = (connection: ByteConnection) => ByteConnectionHandler;\n\nexport type ConnectionStage = \"awaitingHello\" | \"handshaking\" | \"ready\" | \"closing\" | \"closed\";\n\nexport interface ConnectionState {\n\tconnection: ByteConnection;\n\tdecoder: ClientMessageDecoder;\n\tserviceStateEncoders: Map<string, ServiceStateEncoder>;\n\tstage: ConnectionStage;\n\tdisconnected: boolean;\n\thandshake?: Promise<void>;\n\thandshakeTimeout: NodeJS.Timeout;\n\tserverServices?: RoutedServerServiceAttachment;\n\tactiveRequests: Map<string, { controller: AbortController; target: RpcTarget }>;\n}\n\nexport function isTerminalConnection(state: ConnectionState): boolean {\n\treturn state.disconnected || state.stage === \"closing\" || state.stage === \"closed\";\n}\n"]}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { RemoteServiceErrorCode } from "@ny4n/chord";
|
|
2
|
+
type ServerOperationErrorCode = RemoteServiceErrorCode | "wrong_server" | "session_not_found" | "session_ambiguous" | "session_not_attached" | "server_draining";
|
|
3
|
+
export declare const INTERNAL_SERVER_ERROR_MESSAGE = "Internal server error";
|
|
4
|
+
/** A host or lifecycle error that can safely cross the protocol boundary. */
|
|
5
|
+
export declare class ServerError extends Error {
|
|
6
|
+
readonly code: ServerOperationErrorCode;
|
|
7
|
+
constructor(code: ServerOperationErrorCode, message: string);
|
|
8
|
+
}
|
|
9
|
+
export declare class WrongServerError extends ServerError {
|
|
10
|
+
constructor();
|
|
11
|
+
}
|
|
12
|
+
export declare class SessionNotFoundError extends ServerError {
|
|
13
|
+
constructor(message?: string);
|
|
14
|
+
}
|
|
15
|
+
export declare class SessionAmbiguousError extends ServerError {
|
|
16
|
+
constructor();
|
|
17
|
+
}
|
|
18
|
+
export declare class SessionNotAttachedError extends ServerError {
|
|
19
|
+
constructor();
|
|
20
|
+
}
|
|
21
|
+
export declare class ServerDrainingError extends ServerError {
|
|
22
|
+
constructor();
|
|
23
|
+
}
|
|
24
|
+
export {};
|
|
25
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAE1D,KAAK,wBAAwB,GAC1B,sBAAsB,GACtB,cAAc,GACd,mBAAmB,GACnB,mBAAmB,GACnB,sBAAsB,GACtB,iBAAiB,CAAC;AAErB,eAAO,MAAM,6BAA6B,0BAA0B,CAAC;AAErE,6EAA6E;AAC7E,qBAAa,WAAY,SAAQ,KAAK;IACrC,QAAQ,CAAC,IAAI,EAAE,wBAAwB,CAAC;IAExC,YAAY,IAAI,EAAE,wBAAwB,EAAE,OAAO,EAAE,MAAM,EAI1D;CACD;AAED,qBAAa,gBAAiB,SAAQ,WAAW;IAChD,cAGC;CACD;AAED,qBAAa,oBAAqB,SAAQ,WAAW;IACpD,YAAY,OAAO,SAA0B,EAG5C;CACD;AAED,qBAAa,qBAAsB,SAAQ,WAAW;IACrD,cAGC;CACD;AAED,qBAAa,uBAAwB,SAAQ,WAAW;IACvD,cAGC;CACD;AAED,qBAAa,mBAAoB,SAAQ,WAAW;IACnD,cAGC;CACD"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export const INTERNAL_SERVER_ERROR_MESSAGE = "Internal server error";
|
|
2
|
+
/** A host or lifecycle error that can safely cross the protocol boundary. */
|
|
3
|
+
export class ServerError extends Error {
|
|
4
|
+
code;
|
|
5
|
+
constructor(code, message) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = "ServerError";
|
|
8
|
+
this.code = code;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export class WrongServerError extends ServerError {
|
|
12
|
+
constructor() {
|
|
13
|
+
super("wrong_server", "Request was addressed to another server");
|
|
14
|
+
this.name = "WrongServerError";
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export class SessionNotFoundError extends ServerError {
|
|
18
|
+
constructor(message = "Session was not found") {
|
|
19
|
+
super("session_not_found", message);
|
|
20
|
+
this.name = "SessionNotFoundError";
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export class SessionAmbiguousError extends ServerError {
|
|
24
|
+
constructor() {
|
|
25
|
+
super("session_ambiguous", "Session ID matches more than one session");
|
|
26
|
+
this.name = "SessionAmbiguousError";
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export class SessionNotAttachedError extends ServerError {
|
|
30
|
+
constructor() {
|
|
31
|
+
super("session_not_attached", "Session is not attached to this client");
|
|
32
|
+
this.name = "SessionNotAttachedError";
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export class ServerDrainingError extends ServerError {
|
|
36
|
+
constructor() {
|
|
37
|
+
super("server_draining", "Server is draining");
|
|
38
|
+
this.name = "ServerDrainingError";
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAUA,MAAM,CAAC,MAAM,6BAA6B,GAAG,uBAAuB,CAAC;AAErE,6EAA6E;AAC7E,MAAM,OAAO,WAAY,SAAQ,KAAK;IAC5B,IAAI,CAA2B;IAExC,YAAY,IAA8B,EAAE,OAAe;QAC1D,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;QAC1B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IAClB,CAAC;CACD;AAED,MAAM,OAAO,gBAAiB,SAAQ,WAAW;IAChD;QACC,KAAK,CAAC,cAAc,EAAE,yCAAyC,CAAC,CAAC;QACjE,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;IAChC,CAAC;CACD;AAED,MAAM,OAAO,oBAAqB,SAAQ,WAAW;IACpD,YAAY,OAAO,GAAG,uBAAuB;QAC5C,KAAK,CAAC,mBAAmB,EAAE,OAAO,CAAC,CAAC;QACpC,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAC;IACpC,CAAC;CACD;AAED,MAAM,OAAO,qBAAsB,SAAQ,WAAW;IACrD;QACC,KAAK,CAAC,mBAAmB,EAAE,0CAA0C,CAAC,CAAC;QACvE,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;IACrC,CAAC;CACD;AAED,MAAM,OAAO,uBAAwB,SAAQ,WAAW;IACvD;QACC,KAAK,CAAC,sBAAsB,EAAE,wCAAwC,CAAC,CAAC;QACxE,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAC;IACvC,CAAC;CACD;AAED,MAAM,OAAO,mBAAoB,SAAQ,WAAW;IACnD;QACC,KAAK,CAAC,iBAAiB,EAAE,oBAAoB,CAAC,CAAC;QAC/C,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;IACnC,CAAC;CACD","sourcesContent":["import type { RemoteServiceErrorCode } from \"@ny4n/chord\";\n\ntype ServerOperationErrorCode =\n\t| RemoteServiceErrorCode\n\t| \"wrong_server\"\n\t| \"session_not_found\"\n\t| \"session_ambiguous\"\n\t| \"session_not_attached\"\n\t| \"server_draining\";\n\nexport const INTERNAL_SERVER_ERROR_MESSAGE = \"Internal server error\";\n\n/** A host or lifecycle error that can safely cross the protocol boundary. */\nexport class ServerError extends Error {\n\treadonly code: ServerOperationErrorCode;\n\n\tconstructor(code: ServerOperationErrorCode, message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"ServerError\";\n\t\tthis.code = code;\n\t}\n}\n\nexport class WrongServerError extends ServerError {\n\tconstructor() {\n\t\tsuper(\"wrong_server\", \"Request was addressed to another server\");\n\t\tthis.name = \"WrongServerError\";\n\t}\n}\n\nexport class SessionNotFoundError extends ServerError {\n\tconstructor(message = \"Session was not found\") {\n\t\tsuper(\"session_not_found\", message);\n\t\tthis.name = \"SessionNotFoundError\";\n\t}\n}\n\nexport class SessionAmbiguousError extends ServerError {\n\tconstructor() {\n\t\tsuper(\"session_ambiguous\", \"Session ID matches more than one session\");\n\t\tthis.name = \"SessionAmbiguousError\";\n\t}\n}\n\nexport class SessionNotAttachedError extends ServerError {\n\tconstructor() {\n\t\tsuper(\"session_not_attached\", \"Session is not attached to this client\");\n\t\tthis.name = \"SessionNotAttachedError\";\n\t}\n}\n\nexport class ServerDrainingError extends ServerError {\n\tconstructor() {\n\t\tsuper(\"server_draining\", \"Server is draining\");\n\t\tthis.name = \"ServerDrainingError\";\n\t}\n}\n"]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC","sourcesContent":["export * from \"./errors.ts\";\nexport * from \"./listener.ts\";\nexport * from \"./server.ts\";\nexport * from \"./types.ts\";\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ByteConnectionAcceptor } from "./connection.ts";
|
|
2
|
+
/** Supplies established byte connections after any required transport authentication. */
|
|
3
|
+
export interface ServerListener {
|
|
4
|
+
/** Starts listening and passes authorized connections to accept. */
|
|
5
|
+
start(accept: ByteConnectionAcceptor): Promise<void>;
|
|
6
|
+
close(): Promise<void>;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=listener.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"listener.d.ts","sourceRoot":"","sources":["../src/listener.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAE9D,yFAAyF;AACzF,MAAM,WAAW,cAAc;IAC9B,oEAAoE;IACpE,KAAK,CAAC,MAAM,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB"}
|
package/dist/listener.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"listener.js","sourceRoot":"","sources":["../src/listener.ts"],"names":[],"mappings":"","sourcesContent":["import type { ByteConnectionAcceptor } from \"./connection.ts\";\n\n/** Supplies established byte connections after any required transport authentication. */\nexport interface ServerListener {\n\t/** Starts listening and passes authorized connections to accept. */\n\tstart(accept: ByteConnectionAcceptor): Promise<void>;\n\tclose(): Promise<void>;\n}\n"]}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { type ByteConnection, type ByteConnectionHandler } from "./connection.ts";
|
|
2
|
+
import type { ServerHost, ServerOptions, SessionMetadata } from "./types.ts";
|
|
3
|
+
export declare class Server<TMetadata extends SessionMetadata = SessionMetadata> {
|
|
4
|
+
readonly serverId: string;
|
|
5
|
+
/** Resolves after shutdown, or rejects when listener or routed-Session cleanup fails. */
|
|
6
|
+
readonly closed: Promise<void>;
|
|
7
|
+
private readonly host;
|
|
8
|
+
private readonly listeners;
|
|
9
|
+
private readonly maxFrameLength;
|
|
10
|
+
private readonly handshakeTimeoutMs;
|
|
11
|
+
private readonly onConnectionCountChanged;
|
|
12
|
+
private readonly onError;
|
|
13
|
+
private readonly connections;
|
|
14
|
+
private readonly sessions;
|
|
15
|
+
private closing;
|
|
16
|
+
private closePromise?;
|
|
17
|
+
private closedSettled;
|
|
18
|
+
private rejectClosed;
|
|
19
|
+
private resolveClosed;
|
|
20
|
+
private startPromise?;
|
|
21
|
+
private started;
|
|
22
|
+
constructor(host: ServerHost<TMetadata>, options: ServerOptions);
|
|
23
|
+
start(): Promise<this>;
|
|
24
|
+
private startInternal;
|
|
25
|
+
accept(connection: ByteConnection): ByteConnectionHandler;
|
|
26
|
+
close(): Promise<void>;
|
|
27
|
+
private closeInternal;
|
|
28
|
+
private receive;
|
|
29
|
+
private dispatchMessage;
|
|
30
|
+
private finishHandshake;
|
|
31
|
+
private handleCancel;
|
|
32
|
+
private handleRequest;
|
|
33
|
+
private transportClosed;
|
|
34
|
+
private disconnect;
|
|
35
|
+
private sendServiceUpdate;
|
|
36
|
+
private sendMessage;
|
|
37
|
+
private failProtocol;
|
|
38
|
+
private closeServerState;
|
|
39
|
+
private closeConnection;
|
|
40
|
+
private toProtocolError;
|
|
41
|
+
private notifyConnectionCountChanged;
|
|
42
|
+
private reportError;
|
|
43
|
+
private settleClosed;
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AA8BA,OAAO,EACN,KAAK,cAAc,EACnB,KAAK,qBAAqB,EAG1B,MAAM,iBAAiB,CAAC;AAIzB,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAM7E,qBAAa,MAAM,CAAC,SAAS,SAAS,eAAe,GAAG,eAAe;IACtE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,yFAAyF;IACzF,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAE/B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAwB;IAC7C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA4B;IACtD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAS;IAC5C,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAAwC;IACjF,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuC;IAC/D,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA8B;IAC1D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA2B;IACpD,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,YAAY,CAAC,CAAgB;IACrC,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,YAAY,CAA4B;IAChD,OAAO,CAAC,aAAa,CAAc;IACnC,OAAO,CAAC,YAAY,CAAC,CAAgB;IACrC,OAAO,CAAC,OAAO,CAAS;IAExB,YAAY,IAAI,EAAE,UAAU,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,aAAa,EA0B9D;IAED,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAMrB;YAEa,aAAa;IAiC3B,MAAM,CAAC,UAAU,EAAE,cAAc,GAAG,qBAAqB,CAsCxD;IAEK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAK3B;YAEa,aAAa;IAyB3B,OAAO,CAAC,OAAO;IAef,OAAO,CAAC,eAAe;YAuCT,eAAe;IAoC7B,OAAO,CAAC,YAAY;YAQN,aAAa;IAoG3B,OAAO,CAAC,eAAe;IAWvB,OAAO,CAAC,UAAU;YAqBJ,iBAAiB;YAcjB,WAAW;YAsBX,YAAY;YAeZ,gBAAgB;YAehB,eAAe;IAQ7B,OAAO,CAAC,eAAe;IAWvB,OAAO,CAAC,4BAA4B;IAQpC,OAAO,CAAC,WAAW;IAQnB,OAAO,CAAC,YAAY;CAMpB"}
|