@clamator/protocol 0.1.0

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/README.md ADDED
@@ -0,0 +1,50 @@
1
+ # @clamator/protocol
2
+
3
+ Pure JSON-RPC 2.0 protocol primitives plus Zod-derived envelope types for clamator. **No I/O, ever** — anything that touches a network, filesystem, or process belongs in a transport adapter.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @clamator/protocol
9
+ ```
10
+
11
+ ## When you reach for this
12
+
13
+ - Authoring a Zod contract that will be fed to [`@clamator/codegen`](https://www.npmjs.com/package/@clamator/codegen).
14
+ - Building a custom transport adapter that needs the wire-envelope schema, the `Transport` and `Dispatcher` interfaces, or the reserved JSON-RPC error codes.
15
+
16
+ If you only consume generated clients and servers, you don't import this package directly — your transport package (`@clamator/over-memory`, `@clamator/over-redis`) re-exports the few symbols you need.
17
+
18
+ ## Defining a contract
19
+
20
+ Contracts are the source of truth that both sides — and the codegen — consume:
21
+
22
+ ```typescript
23
+ const arith = defineContract('arith', {
24
+ add: defineMethod({
25
+ params: z.object({ a: z.number(), b: z.number() }),
26
+ result: z.object({ sum: z.number() }),
27
+ }),
28
+ divide: defineMethod({
29
+ params: z.object({ a: z.number(), b: z.number() }),
30
+ result: z.object({ q: z.number() }),
31
+ }),
32
+ ping: defineNotification({ params: z.object({ tag: z.string().optional() }) }),
33
+ });
34
+ ```
35
+
36
+ (Verbatim from `ts/packages/over-memory/tests/loopback.test.ts`.)
37
+
38
+ ## Key exports
39
+
40
+ - `defineContract`, `defineMethod`, `defineNotification` — declare a service's methods and notifications with Zod schemas for params and results.
41
+ - `RpcError` — the error type you throw from a handler to surface a structured JSON-RPC error to the caller.
42
+ - `ClamatorProtocolError`, `ClamatorTransportError` — distinguishable error classes for protocol-level vs. transport-level failures.
43
+ - `Transport`, `Dispatcher` — interfaces a custom transport adapter implements.
44
+
45
+ ## Links
46
+
47
+ - Sibling (Python): [`clamator-protocol`](https://pypi.org/project/clamator-protocol/)
48
+ - Codegen: [`@clamator/codegen`](https://www.npmjs.com/package/@clamator/codegen)
49
+ - Design spec: [`docs/2026-05-07-clamator-design.md`](../../../docs/2026-05-07-clamator-design.md)
50
+ - Agent rules: [`AGENTS.md`](./AGENTS.md)
@@ -0,0 +1,19 @@
1
+ import type { Transport } from './transport.js';
2
+ export interface ClamatorClient {
3
+ call<P, R>(service: string, method: string, params: P): Promise<R>;
4
+ notify<P>(service: string, method: string, params: P): Promise<void>;
5
+ }
6
+ export interface RpcClientCoreOptions {
7
+ defaultTimeoutMs?: number;
8
+ }
9
+ export declare class RpcClientCore implements ClamatorClient {
10
+ private readonly transport;
11
+ private state;
12
+ private readonly defaultTimeoutMs;
13
+ constructor(transport: Transport, opts?: RpcClientCoreOptions);
14
+ call<P, R>(service: string, method: string, params: P): Promise<R>;
15
+ notify<P>(service: string, method: string, params: P): Promise<void>;
16
+ start(): Promise<void>;
17
+ stop(): Promise<void>;
18
+ }
19
+ //# sourceMappingURL=client-core.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-core.d.ts","sourceRoot":"","sources":["../src/client-core.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAIhD,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACnE,MAAM,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACtE;AAED,MAAM,WAAW,oBAAoB;IACnC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,qBAAa,aAAc,YAAW,cAAc;IAItC,OAAO,CAAC,QAAQ,CAAC,SAAS;IAHtC,OAAO,CAAC,KAAK,CAA0C;IACvD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;gBAEb,SAAS,EAAE,SAAS,EAAE,IAAI,GAAE,oBAAyB;IAI5E,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAiBlE,MAAM,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAMpE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAOtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAK5B"}
@@ -0,0 +1,59 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { SERVICE_RE, METHOD_RE, parseEnvelope, EnvelopeKind, buildRequest, buildNotification } from './envelope.js';
3
+ import { RpcError, ClamatorProtocolError } from './error.js';
4
+ export class RpcClientCore {
5
+ transport;
6
+ state = 'idle';
7
+ defaultTimeoutMs;
8
+ constructor(transport, opts = {}) {
9
+ this.transport = transport;
10
+ this.defaultTimeoutMs = opts.defaultTimeoutMs ?? 30_000;
11
+ }
12
+ async call(service, method, params) {
13
+ if (!SERVICE_RE.test(service))
14
+ throw new Error(`invalid service "${service}"`);
15
+ if (!METHOD_RE.test(method))
16
+ throw new Error(`invalid method "${method}"`);
17
+ const id = randomUUID();
18
+ const env = buildRequest(`${service}.${method}`, params, id);
19
+ const reply = await this.transport.send(env, { timeoutMs: this.defaultTimeoutMs });
20
+ let parsed;
21
+ try {
22
+ parsed = parseEnvelope(reply);
23
+ }
24
+ catch (e) {
25
+ throw new ClamatorProtocolError(`invalid response envelope: ${e.message}`);
26
+ }
27
+ if (parsed.kind === EnvelopeKind.SuccessResponse)
28
+ return parsed.result;
29
+ if (parsed.kind === EnvelopeKind.ErrorResponse) {
30
+ const { code, message, data } = parsed.error;
31
+ throw new RpcError(code, message, data);
32
+ }
33
+ throw new ClamatorProtocolError(`unexpected response kind: ${parsed.kind}`);
34
+ }
35
+ async notify(service, method, params) {
36
+ if (!SERVICE_RE.test(service))
37
+ throw new Error(`invalid service "${service}"`);
38
+ if (!METHOD_RE.test(method))
39
+ throw new Error(`invalid method "${method}"`);
40
+ await this.transport.notify(buildNotification(`${service}.${method}`, params));
41
+ }
42
+ async start() {
43
+ if (this.state === 'stopped')
44
+ throw new Error('client has been stopped');
45
+ if (this.state === 'started')
46
+ return;
47
+ await this.transport.start();
48
+ this.state = 'started';
49
+ }
50
+ async stop() {
51
+ if (this.state !== 'started') {
52
+ this.state = 'stopped';
53
+ return;
54
+ }
55
+ await this.transport.stop();
56
+ this.state = 'stopped';
57
+ }
58
+ }
59
+ //# sourceMappingURL=client-core.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-core.js","sourceRoot":"","sources":["../src/client-core.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import type { z } from 'zod';
2
+ export interface MethodDef<P extends z.ZodTypeAny, R extends z.ZodTypeAny> {
3
+ params: P;
4
+ result: R;
5
+ notification?: false;
6
+ }
7
+ export interface NotificationDef<P extends z.ZodTypeAny> {
8
+ params: P;
9
+ notification: true;
10
+ }
11
+ export type AnyMethodDef = MethodDef<z.ZodTypeAny, z.ZodTypeAny> | NotificationDef<z.ZodTypeAny>;
12
+ export interface Contract<S extends string, M extends Record<string, AnyMethodDef>> {
13
+ service: S;
14
+ methods: M;
15
+ }
16
+ export declare function defineMethod<P extends z.ZodTypeAny, R extends z.ZodTypeAny>(def: MethodDef<P, R>): MethodDef<P, R>;
17
+ export declare function defineNotification<P extends z.ZodTypeAny>(def: Omit<NotificationDef<P>, 'notification'>): NotificationDef<P>;
18
+ export declare function defineContract<S extends string, M extends Record<string, AnyMethodDef>>(service: S, methods: M): Contract<S, M>;
19
+ /** Inferred handler signatures from a contract's method map. */
20
+ export type HandlersFor<M extends Record<string, AnyMethodDef>> = {
21
+ [K in keyof M]: M[K] extends NotificationDef<infer P> ? (params: z.infer<P>) => Promise<void> : M[K] extends MethodDef<infer P, infer R> ? (params: z.infer<P>) => Promise<z.infer<R>> : never;
22
+ };
23
+ //# sourceMappingURL=contract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAG7B,MAAM,WAAW,SAAS,CAAC,CAAC,SAAS,CAAC,CAAC,UAAU,EAAE,CAAC,SAAS,CAAC,CAAC,UAAU;IACvE,MAAM,EAAE,CAAC,CAAC;IACV,MAAM,EAAE,CAAC,CAAC;IACV,YAAY,CAAC,EAAE,KAAK,CAAC;CACtB;AAED,MAAM,WAAW,eAAe,CAAC,CAAC,SAAS,CAAC,CAAC,UAAU;IACrD,MAAM,EAAE,CAAC,CAAC;IACV,YAAY,EAAE,IAAI,CAAC;CACpB;AAED,MAAM,MAAM,YAAY,GAAG,SAAS,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,UAAU,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;AAEjG,MAAM,WAAW,QAAQ,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC;IAChF,OAAO,EAAE,CAAC,CAAC;IACX,OAAO,EAAE,CAAC,CAAC;CACZ;AAED,wBAAgB,YAAY,CAAC,CAAC,SAAS,CAAC,CAAC,UAAU,EAAE,CAAC,SAAS,CAAC,CAAC,UAAU,EACzE,GAAG,EAAE,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,GACnB,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAEjB;AAED,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,CAAC,CAAC,UAAU,EACvD,GAAG,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,cAAc,CAAC,GAC5C,eAAe,CAAC,CAAC,CAAC,CAEpB;AAED,wBAAgB,cAAc,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,EACrF,OAAO,EAAE,CAAC,EACV,OAAO,EAAE,CAAC,GACT,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAgBhB;AAED,gEAAgE;AAChE,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,IAAI;KAC/D,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,eAAe,CAAC,MAAM,CAAC,CAAC,GACjD,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,GACrC,CAAC,CAAC,CAAC,CAAC,SAAS,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,GACxC,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAC3C,KAAK;CACV,CAAC"}
@@ -0,0 +1,26 @@
1
+ import { SERVICE_RE, METHOD_RE } from './envelope.js';
2
+ export function defineMethod(def) {
3
+ return def;
4
+ }
5
+ export function defineNotification(def) {
6
+ return { ...def, notification: true };
7
+ }
8
+ export function defineContract(service, methods) {
9
+ if (!SERVICE_RE.test(service))
10
+ throw new Error(`invalid service name "${service}" (must match ${SERVICE_RE.source})`);
11
+ for (const [name, def] of Object.entries(methods)) {
12
+ if (!METHOD_RE.test(name))
13
+ throw new Error(`invalid method name "${name}" (must match ${METHOD_RE.source})`);
14
+ const isNotification = def.notification === true;
15
+ if (isNotification) {
16
+ if ('result' in def)
17
+ throw new Error(`notification "${name}" must not include result`);
18
+ }
19
+ else {
20
+ if (!('result' in def) || def.result === undefined)
21
+ throw new Error(`method "${name}" must include result schema`);
22
+ }
23
+ }
24
+ return { service, methods };
25
+ }
26
+ //# sourceMappingURL=contract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAoBtD,MAAM,UAAU,YAAY,CAC1B,GAAoB;IAEpB,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,kBAAkB,CAChC,GAA6C;IAE7C,OAAO,EAAE,GAAG,GAAG,EAAE,YAAY,EAAE,IAAI,EAAwB,CAAC;AAC9D,CAAC;AAED,MAAM,UAAU,cAAc,CAC5B,OAAU,EACV,OAAU;IAEV,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC;QAC3B,MAAM,IAAI,KAAK,CAAC,yBAAyB,OAAO,iBAAiB,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC;IACzF,KAAK,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QAClD,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC;YACvB,MAAM,IAAI,KAAK,CAAC,wBAAwB,IAAI,iBAAiB,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC;QACpF,MAAM,cAAc,GAAI,GAA0C,CAAC,YAAY,KAAK,IAAI,CAAC;QACzF,IAAI,cAAc,EAAE,CAAC;YACnB,IAAI,QAAQ,IAAK,GAA0C;gBACzD,MAAM,IAAI,KAAK,CAAC,iBAAiB,IAAI,2BAA2B,CAAC,CAAC;QACtE,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,CAAC,QAAQ,IAAI,GAAG,CAAC,IAAK,GAA6C,CAAC,MAAM,KAAK,SAAS;gBAC3F,MAAM,IAAI,KAAK,CAAC,WAAW,IAAI,8BAA8B,CAAC,CAAC;QACnE,CAAC;IACH,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;AAC9B,CAAC"}
@@ -0,0 +1,47 @@
1
+ export declare const SERVICE_RE: RegExp;
2
+ export declare const METHOD_RE: RegExp;
3
+ export declare enum EnvelopeKind {
4
+ Request = "request",
5
+ Notification = "notification",
6
+ SuccessResponse = "success",
7
+ ErrorResponse = "error"
8
+ }
9
+ export type RpcId = string | number;
10
+ export interface RequestEnvelope {
11
+ kind: EnvelopeKind.Request;
12
+ service: string;
13
+ method: string;
14
+ fullMethod: string;
15
+ params: unknown;
16
+ id: RpcId;
17
+ raw: Record<string, unknown>;
18
+ }
19
+ export interface NotificationEnvelope {
20
+ kind: EnvelopeKind.Notification;
21
+ service: string;
22
+ method: string;
23
+ fullMethod: string;
24
+ params: unknown;
25
+ raw: Record<string, unknown>;
26
+ }
27
+ export interface SuccessResponseEnvelope {
28
+ kind: EnvelopeKind.SuccessResponse;
29
+ id: RpcId;
30
+ result: unknown;
31
+ }
32
+ export interface ErrorResponseEnvelope {
33
+ kind: EnvelopeKind.ErrorResponse;
34
+ id: RpcId | null;
35
+ error: {
36
+ code: number;
37
+ message: string;
38
+ data: unknown;
39
+ };
40
+ }
41
+ export type Envelope = RequestEnvelope | NotificationEnvelope | SuccessResponseEnvelope | ErrorResponseEnvelope;
42
+ export declare function parseEnvelope(value: unknown): Envelope;
43
+ export declare function buildSuccessResponse(id: RpcId, result: unknown): Record<string, unknown>;
44
+ export declare function buildErrorResponse(id: RpcId | null, code: number, message: string, data?: unknown): Record<string, unknown>;
45
+ export declare function buildRequest(fullMethod: string, params: unknown, id: RpcId): Record<string, unknown>;
46
+ export declare function buildNotification(fullMethod: string, params: unknown): Record<string, unknown>;
47
+ //# sourceMappingURL=envelope.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"envelope.d.ts","sourceRoot":"","sources":["../src/envelope.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,UAAU,QAAsB,CAAC;AAC9C,eAAO,MAAM,SAAS,QAAyB,CAAC;AAGhD,oBAAY,YAAY;IACvB,OAAO,YAAY;IACnB,YAAY,iBAAiB;IAC7B,eAAe,YAAY;IAC3B,aAAa,UAAU;CACvB;AAED,MAAM,MAAM,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;AAEpC,MAAM,WAAW,eAAe;IAC/B,IAAI,EAAE,YAAY,CAAC,OAAO,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,OAAO,CAAC;IAChB,EAAE,EAAE,KAAK,CAAC;IACV,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC7B;AAED,MAAM,WAAW,oBAAoB;IACpC,IAAI,EAAE,YAAY,CAAC,YAAY,CAAC;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,OAAO,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC7B;AAED,MAAM,WAAW,uBAAuB;IACvC,IAAI,EAAE,YAAY,CAAC,eAAe,CAAC;IACnC,EAAE,EAAE,KAAK,CAAC;IACV,MAAM,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,qBAAqB;IACrC,IAAI,EAAE,YAAY,CAAC,aAAa,CAAC;IACjC,EAAE,EAAE,KAAK,GAAG,IAAI,CAAC;IACjB,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,CAAA;KAAE,CAAC;CACxD;AAED,MAAM,MAAM,QAAQ,GACjB,eAAe,GACf,oBAAoB,GACpB,uBAAuB,GACvB,qBAAqB,CAAC;AASzB,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,QAAQ,CA4DtD;AAED,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAExF;AAED,wBAAgB,kBAAkB,CACjC,EAAE,EAAE,KAAK,GAAG,IAAI,EAChB,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,GAAE,OAAc,GACjD,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEzB;AAED,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEpG;AAED,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAE9F"}
@@ -0,0 +1,91 @@
1
+ export const SERVICE_RE = /^[a-z][a-z0-9-]*$/;
2
+ export const METHOD_RE = /^[a-z][a-zA-Z0-9-]*$/;
3
+ // Plain string enum (NOT `const enum` — incompatible with `isolatedModules: true`).
4
+ export var EnvelopeKind;
5
+ (function (EnvelopeKind) {
6
+ EnvelopeKind["Request"] = "request";
7
+ EnvelopeKind["Notification"] = "notification";
8
+ EnvelopeKind["SuccessResponse"] = "success";
9
+ EnvelopeKind["ErrorResponse"] = "error";
10
+ })(EnvelopeKind || (EnvelopeKind = {}));
11
+ class InvalidRequest extends Error {
12
+ code = -32600;
13
+ constructor(msg) {
14
+ super(`-32600 Invalid Request: ${msg}`);
15
+ }
16
+ }
17
+ export function parseEnvelope(value) {
18
+ if (Array.isArray(value))
19
+ throw new InvalidRequest('batch requests not supported');
20
+ if (typeof value !== 'object' || value === null)
21
+ throw new InvalidRequest('not an object');
22
+ const obj = value;
23
+ if (obj.jsonrpc !== '2.0')
24
+ throw new InvalidRequest('jsonrpc must equal "2.0"');
25
+ const hasMethod = typeof obj.method === 'string';
26
+ const hasResult = 'result' in obj;
27
+ const hasError = 'error' in obj;
28
+ const hasId = 'id' in obj && obj.id !== null && obj.id !== undefined;
29
+ if (hasMethod) {
30
+ const fullMethod = obj.method;
31
+ const dot = fullMethod.indexOf('.');
32
+ if (dot <= 0 || dot === fullMethod.length - 1)
33
+ throw new InvalidRequest('method must be "<service>.<method>"');
34
+ const service = fullMethod.slice(0, dot);
35
+ const method = fullMethod.slice(dot + 1);
36
+ if (!SERVICE_RE.test(service))
37
+ throw new InvalidRequest('invalid service segment');
38
+ if (!METHOD_RE.test(method))
39
+ throw new InvalidRequest('invalid method segment');
40
+ if (hasId) {
41
+ const id = obj.id;
42
+ if (typeof id !== 'string' && typeof id !== 'number')
43
+ throw new InvalidRequest('id must be string or number');
44
+ return {
45
+ kind: EnvelopeKind.Request,
46
+ service, method, fullMethod,
47
+ params: obj.params ?? {},
48
+ id, raw: obj,
49
+ };
50
+ }
51
+ return {
52
+ kind: EnvelopeKind.Notification,
53
+ service, method, fullMethod,
54
+ params: obj.params ?? {},
55
+ raw: obj,
56
+ };
57
+ }
58
+ if (hasResult && !hasError) {
59
+ if (!hasId)
60
+ throw new InvalidRequest('response must have id');
61
+ return {
62
+ kind: EnvelopeKind.SuccessResponse,
63
+ id: obj.id,
64
+ result: obj.result,
65
+ };
66
+ }
67
+ if (hasError && !hasResult) {
68
+ const err = obj.error;
69
+ if (!err || typeof err.code !== 'number' || typeof err.message !== 'string')
70
+ throw new InvalidRequest('error must have numeric code + string message');
71
+ return {
72
+ kind: EnvelopeKind.ErrorResponse,
73
+ id: (hasId ? obj.id : null),
74
+ error: { code: err.code, message: err.message, data: err.data ?? null },
75
+ };
76
+ }
77
+ throw new InvalidRequest('envelope must be request, notification, or response');
78
+ }
79
+ export function buildSuccessResponse(id, result) {
80
+ return { jsonrpc: '2.0', id, result };
81
+ }
82
+ export function buildErrorResponse(id, code, message, data = null) {
83
+ return { jsonrpc: '2.0', id, error: { code, message, data } };
84
+ }
85
+ export function buildRequest(fullMethod, params, id) {
86
+ return { jsonrpc: '2.0', method: fullMethod, params, id };
87
+ }
88
+ export function buildNotification(fullMethod, params) {
89
+ return { jsonrpc: '2.0', method: fullMethod, params };
90
+ }
91
+ //# sourceMappingURL=envelope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"envelope.js","sourceRoot":"","sources":["../src/envelope.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ export declare class RpcError extends Error {
2
+ readonly code: number;
3
+ readonly data: unknown;
4
+ constructor(code: number, message: string, data?: unknown);
5
+ }
6
+ export declare class ClamatorProtocolError extends Error {
7
+ constructor(message: string);
8
+ }
9
+ export declare class ClamatorTransportError extends Error {
10
+ readonly cause: unknown;
11
+ constructor(message: string, cause?: unknown);
12
+ }
13
+ export declare function exceptionToErrorData(err: unknown): Record<string, unknown>;
14
+ //# sourceMappingURL=error.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../src/error.ts"],"names":[],"mappings":"AAAA,qBAAa,QAAS,SAAQ,KAAK;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;gBACX,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,GAAE,OAAc;CAMhE;AAED,qBAAa,qBAAsB,SAAQ,KAAK;gBAClC,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,sBAAuB,SAAQ,KAAK;IAC/C,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;gBACZ,OAAO,EAAE,MAAM,EAAE,KAAK,GAAE,OAAc;CAKnD;AAID,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAgB1E"}
package/dist/error.js ADDED
@@ -0,0 +1,47 @@
1
+ export class RpcError extends Error {
2
+ code;
3
+ data;
4
+ constructor(code, message, data = null) {
5
+ super(message);
6
+ this.name = 'RpcError';
7
+ this.code = code;
8
+ this.data = data;
9
+ }
10
+ }
11
+ export class ClamatorProtocolError extends Error {
12
+ constructor(message) {
13
+ super(message);
14
+ this.name = 'ClamatorProtocolError';
15
+ }
16
+ }
17
+ export class ClamatorTransportError extends Error {
18
+ cause;
19
+ constructor(message, cause = null) {
20
+ super(message);
21
+ this.name = 'ClamatorTransportError';
22
+ this.cause = cause;
23
+ }
24
+ }
25
+ const SERIALIZABLE_TYPES = new Set(['string', 'number', 'boolean']);
26
+ export function exceptionToErrorData(err) {
27
+ const out = {};
28
+ if (err instanceof Error) {
29
+ out.name = err.name;
30
+ out.message = err.message;
31
+ for (const key of Object.getOwnPropertyNames(err)) {
32
+ if (key === 'stack' || key === 'message' || key === 'name')
33
+ continue;
34
+ const v = err[key];
35
+ if (v === null || SERIALIZABLE_TYPES.has(typeof v))
36
+ out[key] = v;
37
+ else if (Array.isArray(v) && v.every(x => x === null || SERIALIZABLE_TYPES.has(typeof x)))
38
+ out[key] = v;
39
+ }
40
+ }
41
+ else {
42
+ out.name = typeof err;
43
+ out.message = String(err);
44
+ }
45
+ return out;
46
+ }
47
+ //# sourceMappingURL=error.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error.js","sourceRoot":"","sources":["../src/error.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,QAAS,SAAQ,KAAK;IACxB,IAAI,CAAS;IACb,IAAI,CAAU;IACvB,YAAY,IAAY,EAAE,OAAe,EAAE,OAAgB,IAAI;QAC7D,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;QACvB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF;AAED,MAAM,OAAO,qBAAsB,SAAQ,KAAK;IAC9C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;IACtC,CAAC;CACF;AAED,MAAM,OAAO,sBAAuB,SAAQ,KAAK;IACtC,KAAK,CAAU;IACxB,YAAY,OAAe,EAAE,QAAiB,IAAI;QAChD,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,wBAAwB,CAAC;QACrC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;CACF;AAED,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC;AAEpE,MAAM,UAAU,oBAAoB,CAAC,GAAY;IAC/C,MAAM,GAAG,GAA4B,EAAE,CAAC;IACxC,IAAI,GAAG,YAAY,KAAK,EAAE,CAAC;QACzB,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;QACpB,GAAG,CAAC,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC;QAC1B,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,mBAAmB,CAAC,GAAG,CAAC,EAAE,CAAC;YAClD,IAAI,GAAG,KAAK,OAAO,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,MAAM;gBAAE,SAAS;YACrE,MAAM,CAAC,GAAI,GAA0C,CAAC,GAAG,CAAC,CAAC;YAC3D,IAAI,CAAC,KAAK,IAAI,IAAI,kBAAkB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;gBAAE,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;iBAC5D,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,IAAI,IAAI,kBAAkB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;gBAAE,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC1G,CAAC;IACH,CAAC;SAAM,CAAC;QACN,GAAG,CAAC,IAAI,GAAG,OAAO,GAAG,CAAC;QACtB,GAAG,CAAC,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IAC5B,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,7 @@
1
+ export { defineContract, defineMethod, defineNotification, type Contract, type MethodDef, type NotificationDef, type AnyMethodDef, type HandlersFor, } from './contract.js';
2
+ export { parseEnvelope, buildRequest, buildNotification, buildSuccessResponse, buildErrorResponse, EnvelopeKind, SERVICE_RE, METHOD_RE, type Envelope, type RequestEnvelope, type NotificationEnvelope, type SuccessResponseEnvelope, type ErrorResponseEnvelope, type RpcId, } from './envelope.js';
3
+ export { RpcError, ClamatorProtocolError, ClamatorTransportError, exceptionToErrorData, } from './error.js';
4
+ export type { Transport, Dispatcher, SendOptions } from './transport.js';
5
+ export { RpcServerCore, type ServerStopOptions } from './server-core.js';
6
+ export { RpcClientCore, type ClamatorClient, type RpcClientCoreOptions } from './client-core.js';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EAAE,YAAY,EAAE,kBAAkB,EAChD,KAAK,QAAQ,EAAE,KAAK,SAAS,EAAE,KAAK,eAAe,EAAE,KAAK,YAAY,EAAE,KAAK,WAAW,GACzF,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,aAAa,EAAE,YAAY,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,kBAAkB,EACxF,YAAY,EAAE,UAAU,EAAE,SAAS,EACnC,KAAK,QAAQ,EAAE,KAAK,eAAe,EAAE,KAAK,oBAAoB,EAC9D,KAAK,uBAAuB,EAAE,KAAK,qBAAqB,EAAE,KAAK,KAAK,GACrE,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,QAAQ,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,oBAAoB,GAC9E,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AACzE,OAAO,EAAE,aAAa,EAAE,KAAK,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACzE,OAAO,EAAE,aAAa,EAAE,KAAK,cAAc,EAAE,KAAK,oBAAoB,EAAE,MAAM,kBAAkB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ export { defineContract, defineMethod, defineNotification, } from './contract.js';
2
+ export { parseEnvelope, buildRequest, buildNotification, buildSuccessResponse, buildErrorResponse, EnvelopeKind, SERVICE_RE, METHOD_RE, } from './envelope.js';
3
+ export { RpcError, ClamatorProtocolError, ClamatorTransportError, exceptionToErrorData, } from './error.js';
4
+ export { RpcServerCore } from './server-core.js';
5
+ export { RpcClientCore } from './client-core.js';
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EAAE,YAAY,EAAE,kBAAkB,GAEjD,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,aAAa,EAAE,YAAY,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,kBAAkB,EACxF,YAAY,EAAE,UAAU,EAAE,SAAS,GAGpC,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,QAAQ,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,oBAAoB,GAC9E,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,aAAa,EAA0B,MAAM,kBAAkB,CAAC;AACzE,OAAO,EAAE,aAAa,EAAkD,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,17 @@
1
+ import type { Contract, AnyMethodDef, HandlersFor } from './contract.js';
2
+ import type { Transport } from './transport.js';
3
+ export interface ServerStopOptions {
4
+ graceMs?: number;
5
+ }
6
+ export declare class RpcServerCore {
7
+ private readonly transport;
8
+ private services;
9
+ private state;
10
+ private inflight;
11
+ constructor(transport: Transport);
12
+ registerService<M extends Record<string, AnyMethodDef>>(contract: Contract<string, M>, handlers: HandlersFor<M>): void;
13
+ private dispatcher;
14
+ start(): Promise<void>;
15
+ stop(opts?: ServerStopOptions): Promise<void>;
16
+ }
17
+ //# sourceMappingURL=server-core.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-core.d.ts","sourceRoot":"","sources":["../src/server-core.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACzE,OAAO,KAAK,EAAE,SAAS,EAAc,MAAM,gBAAgB,CAAC;AAS5D,MAAM,WAAW,iBAAiB;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,aAAa;IAKZ,OAAO,CAAC,QAAQ,CAAC,SAAS;IAJtC,OAAO,CAAC,QAAQ,CAAmC;IACnD,OAAO,CAAC,KAAK,CAA0C;IACvD,OAAO,CAAC,QAAQ,CAA+B;gBAElB,SAAS,EAAE,SAAS;IAEjD,eAAe,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,EACpD,QAAQ,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,EAC7B,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC,GACvB,IAAI;IASP,OAAO,CAAC,UAAU;IAgDZ,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAUtB,IAAI,CAAC,IAAI,GAAE,iBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC;CAaxD"}
@@ -0,0 +1,104 @@
1
+ import { EnvelopeKind, buildSuccessResponse, buildErrorResponse } from './envelope.js';
2
+ import { RpcError, exceptionToErrorData } from './error.js';
3
+ export class RpcServerCore {
4
+ transport;
5
+ services = new Map();
6
+ state = 'idle';
7
+ inflight = new Set();
8
+ constructor(transport) {
9
+ this.transport = transport;
10
+ }
11
+ registerService(contract, handlers) {
12
+ if (this.services.has(contract.service))
13
+ throw new Error(`service "${contract.service}" already registered on this server`);
14
+ this.services.set(contract.service, {
15
+ contract,
16
+ handlers: handlers,
17
+ });
18
+ }
19
+ dispatcher(serviceName) {
20
+ return async (env) => {
21
+ const entry = this.services.get(serviceName);
22
+ if (!entry) {
23
+ if (env.kind === EnvelopeKind.Notification)
24
+ return null;
25
+ const id = env.kind === EnvelopeKind.Request ? env.id : null;
26
+ return buildErrorResponse(id, -32601, 'Method not found');
27
+ }
28
+ if (env.kind !== EnvelopeKind.Request && env.kind !== EnvelopeKind.Notification)
29
+ return null;
30
+ const methodDef = entry.contract.methods[env.method];
31
+ const id = env.kind === EnvelopeKind.Request ? env.id : null;
32
+ if (!methodDef) {
33
+ return env.kind === EnvelopeKind.Notification ? null : buildErrorResponse(id, -32601, 'Method not found');
34
+ }
35
+ let parsed;
36
+ try {
37
+ parsed = methodDef.params.parse(env.params);
38
+ }
39
+ catch (e) {
40
+ if (env.kind === EnvelopeKind.Notification)
41
+ return null;
42
+ return buildErrorResponse(id, -32602, 'Invalid params', exceptionToErrorData(e));
43
+ }
44
+ const handler = entry.handlers[env.method];
45
+ if (!handler)
46
+ return env.kind === EnvelopeKind.Notification ? null : buildErrorResponse(id, -32601, 'Method not found');
47
+ const work = handler(parsed);
48
+ this.inflight.add(work);
49
+ let result;
50
+ try {
51
+ result = await work;
52
+ }
53
+ catch (e) {
54
+ this.inflight.delete(work);
55
+ if (env.kind === EnvelopeKind.Notification)
56
+ return null;
57
+ if (e instanceof RpcError)
58
+ return buildErrorResponse(id, e.code, e.message, e.data);
59
+ return buildErrorResponse(id, -32603, 'Internal error', exceptionToErrorData(e));
60
+ }
61
+ this.inflight.delete(work);
62
+ if (env.kind === EnvelopeKind.Notification)
63
+ return null;
64
+ const isNotificationDef = 'notification' in methodDef && methodDef.notification === true;
65
+ if (isNotificationDef)
66
+ return null;
67
+ try {
68
+ const validated = methodDef.result.parse(result);
69
+ return buildSuccessResponse(id, validated);
70
+ }
71
+ catch (e) {
72
+ return buildErrorResponse(id, -32603, 'Result validation failed', exceptionToErrorData(e));
73
+ }
74
+ };
75
+ }
76
+ async start() {
77
+ if (this.state === 'started')
78
+ return;
79
+ if (this.state === 'stopped')
80
+ throw new Error('server has been stopped');
81
+ for (const name of this.services.keys()) {
82
+ await this.transport.registerService(name, this.dispatcher(name));
83
+ }
84
+ await this.transport.start();
85
+ this.state = 'started';
86
+ }
87
+ async stop(opts = {}) {
88
+ if (this.state !== 'started') {
89
+ this.state = 'stopped';
90
+ return;
91
+ }
92
+ const grace = opts.graceMs ?? 5000;
93
+ const deadline = Date.now() + grace;
94
+ while (this.inflight.size > 0 && Date.now() < deadline) {
95
+ await Promise.race([
96
+ Promise.allSettled([...this.inflight]),
97
+ new Promise(r => setTimeout(r, 50)),
98
+ ]);
99
+ }
100
+ await this.transport.stop();
101
+ this.state = 'stopped';
102
+ }
103
+ }
104
+ //# sourceMappingURL=server-core.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-core.js","sourceRoot":"","sources":["../src/server-core.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import type { Envelope } from './envelope.js';
2
+ /** Adapter-side dispatch fn: returns Response envelope for requests, null for notifications. */
3
+ export type Dispatcher = (env: Envelope) => Promise<Record<string, unknown> | null>;
4
+ export interface SendOptions {
5
+ timeoutMs: number;
6
+ }
7
+ export interface Transport {
8
+ /** Register a handler for inbound traffic on a given service name. */
9
+ registerService(name: string, dispatch: Dispatcher): Promise<void>;
10
+ /** Send a request envelope; resolve with the response envelope. */
11
+ send(env: Record<string, unknown>, opts: SendOptions): Promise<Record<string, unknown>>;
12
+ /** Send a fire-and-forget notification envelope. */
13
+ notify(env: Record<string, unknown>): Promise<void>;
14
+ /** Spin up loops; idempotent. */
15
+ start(): Promise<void>;
16
+ /** Drain in-flight + close; idempotent. */
17
+ stop(): Promise<void>;
18
+ }
19
+ //# sourceMappingURL=transport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE9C,gGAAgG;AAChG,MAAM,MAAM,UAAU,GAAG,CAAC,GAAG,EAAE,QAAQ,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;AAEpF,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,SAAS;IACxB,sEAAsE;IACtE,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnE,mEAAmE;IACnE,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACxF,oDAAoD;IACpD,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,iCAAiC;IACjC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,2CAA2C;IAC3C,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=transport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport.js","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@clamator/protocol",
3
+ "version": "0.1.0",
4
+ "description": "Polyglot RPC protocol layer (pre-1.0; API may break in minor versions).",
5
+ "license": "Apache-2.0",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "module": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "LICENSE"
19
+ ],
20
+ "publishConfig": {
21
+ "access": "public"
22
+ },
23
+ "dependencies": {
24
+ "zod": "^3.23.0"
25
+ },
26
+ "devDependencies": {
27
+ "typescript": "^5.6.0",
28
+ "vitest": "^2.1.0"
29
+ },
30
+ "scripts": {
31
+ "build": "tsc -p tsconfig.json",
32
+ "clean": "rm -rf dist .tsbuildinfo",
33
+ "lint": "tsc -p tsconfig.json --noEmit",
34
+ "test": "vitest run"
35
+ }
36
+ }