Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion conformance/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ comline-core = { git = "https://github.com/ComlineProject/core", rev = "50c17cc7
[dev-dependencies]
comline-codegen = { path = "../codegen" }
comline-codegen-rust = { git = "https://github.com/ComlineProject/comline-rust", rev = "17c7b2318d89926458ec92e1b223dbf58ff36f1f" }
comline-codegen-typescript = { git = "https://github.com/ComlineProject/comline-typescript", rev = "c535f141cdae5c73257c8236826560554c5e7719" }
comline-codegen-typescript = { git = "https://github.com/ComlineProject/comline-typescript", rev = "70b617d9fbf9f4c90bce5bc11f406db66a8e2d26" }
106 changes: 97 additions & 9 deletions conformance/tests/golden/protocol/typescript.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,35 @@
// Generated by Comline

// Canonical digest of the frozen IR this file was generated from — the
// two ends of a connection must agree on it.
import {
Client,
Server,
Handshake,
RuntimeError,
resolveKind,
DatagramFraming,
type Codec,
type Dispatch,
type Framing,
type Kind,
type Reply,
type Transport,
} from "@comline/runtime";

/** Canonical digest of the frozen IR this file was generated from — the
* two ends of a connection must agree on it. */
export const IR_HASH = 0xbdbe5c6fd7420bd0n;

export interface NotFound {
}

export class NotFoundError extends Error {
static readonly ordinal = 0;
constructor(readonly data: NotFound) {
super("NotFound");
this.name = "NotFoundError";
}
}

export interface ServiceLookupParams {
id: number;
}
Expand All @@ -15,16 +38,81 @@ export interface ServiceNotifyParams {
code: number;
}

export type ServiceLookupError =
| { code: 0; name: "NotFound"; data: NotFound };

export type ServiceError =
| { code: 0; name: "NotFound"; data: NotFound };

export interface Service {
/** @throws {ServiceLookupError} */
/** @throws {NotFoundError} */
lookup(params: ServiceLookupParams): Promise<Record>;
count(): Promise<number>;
notify(params: ServiceNotifyParams): Promise<void>;
}

export const SERVICE_CALLS = ["lookup", "count", "notify"] as const;

export class ServiceDispatcher implements Dispatch {
constructor(private readonly impl: Service) {}

calls(): readonly string[] {
return SERVICE_CALLS;
}

async dispatch(call: Kind, params: Uint8Array, codec: Codec, reply: Reply): Promise<void> {
switch (resolveKind(call, SERVICE_CALLS)) {
case 0: {
const p = codec.decode<ServiceLookupParams>(params);
try {
reply.ok(codec.encode((await this.impl.lookup(p)) ?? null));
} catch (e) {
if (e instanceof NotFoundError) { reply.err(NotFoundError.ordinal, codec.encode(e.data)); return; }
throw e;
}
return;
}
case 1: {
reply.ok(codec.encode((await this.impl.count()) ?? null));
return;
}
case 2: {
const p = codec.decode<ServiceNotifyParams>(params);
await this.impl.notify(p);
return;
}
default:
throw RuntimeError.unknownCall();
}
}
}

export class ServiceClient {
constructor(private readonly client: Client) {}

static async connect(transport: Transport, codec: Codec, framing: Framing = new DatagramFraming()): Promise<ServiceClient> {
const hs = new Handshake({ irHash: IR_HASH, wireFormat: codec.name, framing: framing.name });
return new ServiceClient(await Client.connect(transport, codec, hs, framing));
}

async lookup(params: ServiceLookupParams): Promise<Record> {
const env = await this.client.call({ id: 0, name: "lookup" }, params);
if ("ok" in env) return this.client.codec.decode<Record>(env.ok);
switch (env.err.id) {
case NotFoundError.ordinal:
throw new NotFoundError(this.client.codec.decode<NotFound>(env.err.body));
default:
throw RuntimeError.remote(env.err.id);
}
}

async count(): Promise<number> {
const env = await this.client.call({ id: 1, name: "count" }, null);
if ("ok" in env) return this.client.codec.decode<number>(env.ok);
throw RuntimeError.remote(env.err.id);
}

async notify(params: ServiceNotifyParams): Promise<void> {
await this.client.notify({ id: 2, name: "notify" }, params);
}
}

export function serveService(impl: Service, transport: Transport, codec: Codec, framing: Framing = new DatagramFraming()): Promise<void> {
const hs = new Handshake({ irHash: IR_HASH, wireFormat: codec.name, framing: framing.name });
return new Server(new ServiceDispatcher(impl), codec, framing).serveHandshaked(transport, hs);
}

Loading