@irpclib/irpc 0.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.
@@ -0,0 +1,18 @@
1
+ import { IRPCCall } from "./call.js";
2
+
3
+ //#region src/batch.d.ts
4
+
5
+ /**
6
+ * Batches multiple IRPC calls together and executes them with a specified delay.
7
+ *
8
+ * This function collects IRPC calls in a queue and executes them all at once after
9
+ * a specified delay has passed. If multiple calls are made within the delay period,
10
+ * they will be grouped together in a single batch execution.
11
+ *
12
+ * @param call - The IRPC call to be added to the current batch
13
+ * @param handler - A function that will be called with all queued calls when the batch executes
14
+ * @param delay - The delay in milliseconds before executing the batch (default: 0)
15
+ */
16
+ declare function batch(call: IRPCCall, handler: (calls: IRPCCall[]) => void, delay?: number): void;
17
+ //#endregion
18
+ export { batch };
package/dist/batch.js ADDED
@@ -0,0 +1,23 @@
1
+ //#region src/batch.ts
2
+ const IRPC_QUEUE = /* @__PURE__ */ new Set();
3
+ /**
4
+ * Batches multiple IRPC calls together and executes them with a specified delay.
5
+ *
6
+ * This function collects IRPC calls in a queue and executes them all at once after
7
+ * a specified delay has passed. If multiple calls are made within the delay period,
8
+ * they will be grouped together in a single batch execution.
9
+ *
10
+ * @param call - The IRPC call to be added to the current batch
11
+ * @param handler - A function that will be called with all queued calls when the batch executes
12
+ * @param delay - The delay in milliseconds before executing the batch (default: 0)
13
+ */
14
+ function batch(call, handler, delay = 0) {
15
+ if (!IRPC_QUEUE.size) setTimeout(() => {
16
+ handler([...IRPC_QUEUE.values()]);
17
+ IRPC_QUEUE.clear();
18
+ }, delay);
19
+ IRPC_QUEUE.add(call);
20
+ }
21
+
22
+ //#endregion
23
+ export { batch };
package/dist/call.d.ts ADDED
@@ -0,0 +1,43 @@
1
+ import { IRPCPayload } from "./types.js";
2
+
3
+ //#region src/call.d.ts
4
+
5
+ /**
6
+ * Represents an RPC call with promise-like behavior for handling asynchronous operations.
7
+ * Each call has a unique identifier and manages its own resolution state.
8
+ */
9
+ declare class IRPCCall {
10
+ payload: IRPCPayload;
11
+ resolver: (value: unknown) => void;
12
+ rejector: (reason?: Error) => void;
13
+ /**
14
+ * Unique identifier for this RPC call, generated using shortId().
15
+ */
16
+ id: string;
17
+ /**
18
+ * Flag indicating whether this call has been resolved or rejected.
19
+ * Prevents multiple resolutions of the same call.
20
+ */
21
+ resolved: boolean;
22
+ /**
23
+ * Creates a new IRPCCall instance.
24
+ * @param payload - The RPC payload containing method and parameters
25
+ * @param resolver - Function to resolve the associated promise with a value
26
+ * @param rejector - Function to reject the associated promise with an error
27
+ */
28
+ constructor(payload: IRPCPayload, resolver: (value: unknown) => void, rejector: (reason?: Error) => void);
29
+ /**
30
+ * Resolves the RPC call with the provided value.
31
+ * If the call is already resolved, this method does nothing.
32
+ * @param value - The value to resolve the promise with
33
+ */
34
+ resolve(value: unknown): void;
35
+ /**
36
+ * Rejects the RPC call with the provided reason.
37
+ * If the call is already resolved, this method does nothing.
38
+ * @param reason - Optional error reason for rejecting the promise
39
+ */
40
+ reject(reason?: Error): void;
41
+ }
42
+ //#endregion
43
+ export { IRPCCall };
package/dist/call.js ADDED
@@ -0,0 +1,52 @@
1
+ import { shortId } from "./utils.js";
2
+
3
+ //#region src/call.ts
4
+ /**
5
+ * Represents an RPC call with promise-like behavior for handling asynchronous operations.
6
+ * Each call has a unique identifier and manages its own resolution state.
7
+ */
8
+ var IRPCCall = class {
9
+ /**
10
+ * Unique identifier for this RPC call, generated using shortId().
11
+ */
12
+ id = shortId();
13
+ /**
14
+ * Flag indicating whether this call has been resolved or rejected.
15
+ * Prevents multiple resolutions of the same call.
16
+ */
17
+ resolved = false;
18
+ /**
19
+ * Creates a new IRPCCall instance.
20
+ * @param payload - The RPC payload containing method and parameters
21
+ * @param resolver - Function to resolve the associated promise with a value
22
+ * @param rejector - Function to reject the associated promise with an error
23
+ */
24
+ constructor(payload, resolver, rejector) {
25
+ this.payload = payload;
26
+ this.resolver = resolver;
27
+ this.rejector = rejector;
28
+ }
29
+ /**
30
+ * Resolves the RPC call with the provided value.
31
+ * If the call is already resolved, this method does nothing.
32
+ * @param value - The value to resolve the promise with
33
+ */
34
+ resolve(value) {
35
+ if (this.resolved) return;
36
+ this.resolved = true;
37
+ this.resolver(value);
38
+ }
39
+ /**
40
+ * Rejects the RPC call with the provided reason.
41
+ * If the call is already resolved, this method does nothing.
42
+ * @param reason - Optional error reason for rejecting the promise
43
+ */
44
+ reject(reason) {
45
+ if (this.resolved) return;
46
+ this.resolved = true;
47
+ this.rejector(reason);
48
+ }
49
+ };
50
+
51
+ //#endregion
52
+ export { IRPCCall };
@@ -0,0 +1,40 @@
1
+ import { IRPCContext, IRPCContextProvider } from "./types.js";
2
+
3
+ //#region src/context.d.ts
4
+
5
+ /**
6
+ * Sets the global context store for the IRPC system.
7
+ * This store is used to manage context data across requests.
8
+ * @param store - The context store implementation to use
9
+ */
10
+ declare function setContextProvider(store: IRPCContextProvider): void;
11
+ /**
12
+ * Executes a function with the provided context.
13
+ * If a context store is available, it runs the function within that context.
14
+ * Otherwise, it executes the function directly.
15
+ * @param ctx - The context to run the function with
16
+ * @param fn - The function to execute
17
+ * @returns The result of the executed function
18
+ */
19
+ declare function withContext<R>(ctx: IRPCContext<string, unknown>, fn: () => R): R;
20
+ /**
21
+ * Creates a new context map with optional initial values.
22
+ * @param init - Optional initial key-value pairs for the context
23
+ * @returns A new Map instance representing the context
24
+ */
25
+ declare function createContext<K extends string, V>(init?: [K, V][]): Map<K, V>;
26
+ /**
27
+ * Sets a value in the current context.
28
+ * @param key - The key to set in the context
29
+ * @param value - The value to associate with the key
30
+ */
31
+ declare function setContext<V, K extends string = string>(key: K, value: V): void;
32
+ /**
33
+ * Gets a value from the current context.
34
+ * @param key - The key to retrieve from the context
35
+ * @param fallback - Optional fallback value if the key is not found
36
+ * @returns The value associated with the key, or the fallback value if not found
37
+ */
38
+ declare function getContext<V, K extends string = string>(key: K, fallback?: V): V | undefined;
39
+ //#endregion
40
+ export { createContext, getContext, setContext, setContextProvider, withContext };
@@ -0,0 +1,51 @@
1
+ //#region src/context.ts
2
+ let currentStore;
3
+ /**
4
+ * Sets the global context store for the IRPC system.
5
+ * This store is used to manage context data across requests.
6
+ * @param store - The context store implementation to use
7
+ */
8
+ function setContextProvider(store) {
9
+ currentStore = store;
10
+ }
11
+ /**
12
+ * Executes a function with the provided context.
13
+ * If a context store is available, it runs the function within that context.
14
+ * Otherwise, it executes the function directly.
15
+ * @param ctx - The context to run the function with
16
+ * @param fn - The function to execute
17
+ * @returns The result of the executed function
18
+ */
19
+ function withContext(ctx, fn) {
20
+ return currentStore?.run(ctx, fn) ?? fn();
21
+ }
22
+ /**
23
+ * Creates a new context map with optional initial values.
24
+ * @param init - Optional initial key-value pairs for the context
25
+ * @returns A new Map instance representing the context
26
+ */
27
+ function createContext(init) {
28
+ return new Map(init);
29
+ }
30
+ /**
31
+ * Sets a value in the current context.
32
+ * @param key - The key to set in the context
33
+ * @param value - The value to associate with the key
34
+ */
35
+ function setContext(key, value) {
36
+ (currentStore?.getStore())?.set(key, value);
37
+ }
38
+ /**
39
+ * Gets a value from the current context.
40
+ * @param key - The key to retrieve from the context
41
+ * @param fallback - Optional fallback value if the key is not found
42
+ * @returns The value associated with the key, or the fallback value if not found
43
+ */
44
+ function getContext(key, fallback) {
45
+ const result = (currentStore?.getStore())?.get(key);
46
+ if (typeof result === "undefined" && typeof fallback !== "undefined") return fallback;
47
+ return result;
48
+ }
49
+
50
+ //#endregion
51
+ export { createContext, getContext, setContext, setContextProvider, withContext };
@@ -0,0 +1,7 @@
1
+ import { IRPCArraySchema, IRPCAuthorizer, IRPCContext, IRPCContextProvider, IRPCData, IRPCDataSchema, IRPCFactory, IRPCHandler, IRPCHost, IRPCInputs, IRPCModule, IRPCNamespace, IRPCObject, IRPCObjectSchema, IRPCOutput, IRPCParseResult, IRPCPayload, IRPCPrimitive, IRPCPrimitiveSchema, IRPCRegistry, IRPCRequest, IRPCResponse, IRPCSchema, IRPCSpec, IRPCStore, IRPCTransport } from "./types.js";
2
+ import { IRPCCall } from "./call.js";
3
+ import { batch } from "./batch.js";
4
+ import { createContext, getContext, setContext, setContextProvider, withContext } from "./context.js";
5
+ import { createModule } from "./module.js";
6
+ import { shortId } from "./utils.js";
7
+ export { IRPCArraySchema, IRPCAuthorizer, IRPCCall, IRPCContext, IRPCContextProvider, IRPCData, IRPCDataSchema, IRPCFactory, IRPCHandler, IRPCHost, IRPCInputs, IRPCModule, IRPCNamespace, IRPCObject, IRPCObjectSchema, IRPCOutput, IRPCParseResult, IRPCPayload, IRPCPrimitive, IRPCPrimitiveSchema, IRPCRegistry, IRPCRequest, IRPCResponse, IRPCSchema, IRPCSpec, IRPCStore, IRPCTransport, batch, createContext, createModule, getContext, setContext, setContextProvider, shortId, withContext };
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ import { batch } from "./batch.js";
2
+ import { shortId } from "./utils.js";
3
+ import { IRPCCall } from "./call.js";
4
+ import { createContext, getContext, setContext, setContextProvider, withContext } from "./context.js";
5
+ import { IRPCTransport } from "./types.js";
6
+ import { createModule } from "./module.js";
7
+
8
+ export { IRPCCall, IRPCTransport, batch, createContext, createModule, getContext, setContext, setContextProvider, shortId, withContext };
@@ -0,0 +1,17 @@
1
+ import { IRPCFactory, IRPCModule } from "./types.js";
2
+
3
+ //#region src/module.d.ts
4
+
5
+ /**
6
+ * Creates an IRPC module with the given configuration.
7
+ *
8
+ * This function initializes a new IRPC module with a store for tracking RPC calls,
9
+ * a registry for mapping functions to their specifications, and various methods
10
+ * for managing and executing remote procedure calls.
11
+ *
12
+ * @param config - Optional partial configuration for the IRPC module, excluding 'submit' and 'transport'
13
+ * @returns An IRPC factory function with attached utility methods
14
+ */
15
+ declare function createModule(config?: Partial<Omit<IRPCModule, 'submit' | 'transport'>>): IRPCFactory;
16
+ //#endregion
17
+ export { createModule };
package/dist/module.js ADDED
@@ -0,0 +1,156 @@
1
+ import { batch } from "./batch.js";
2
+ import { IRPCCall } from "./call.js";
3
+
4
+ //#region src/module.ts
5
+ const DEFAULT_TIMEOUT = 2e4;
6
+ /**
7
+ * Creates an IRPC module with the given configuration.
8
+ *
9
+ * This function initializes a new IRPC module with a store for tracking RPC calls,
10
+ * a registry for mapping functions to their specifications, and various methods
11
+ * for managing and executing remote procedure calls.
12
+ *
13
+ * @param config - Optional partial configuration for the IRPC module, excluding 'submit' and 'transport'
14
+ * @returns An IRPC factory function with attached utility methods
15
+ */
16
+ function createModule(config) {
17
+ const store = /* @__PURE__ */ new Map();
18
+ const registry = /* @__PURE__ */ new WeakMap();
19
+ const module = {
20
+ name: "irpc",
21
+ version: "1.0.0",
22
+ timeout: DEFAULT_TIMEOUT,
23
+ ...config
24
+ };
25
+ /**
26
+ * Factory function that creates IRPC handlers based on specifications.
27
+ * Each handler can either execute a local function or make a remote call.
28
+ */
29
+ const factory = ((spec) => {
30
+ const host = { ...spec };
31
+ store.set(host.name, host);
32
+ const fn = ((...args) => {
33
+ if (typeof host.handler === "function") return host.handler(...args);
34
+ else return remoteCall(module, host, ...args);
35
+ });
36
+ registry.set(fn, host);
37
+ return fn;
38
+ });
39
+ /**
40
+ * Returns the namespace information of the module (name and version).
41
+ */
42
+ Object.defineProperty(factory, "namespace", { get: () => ({
43
+ name: module.name,
44
+ version: module.version
45
+ }) });
46
+ /**
47
+ * Associates a handler function with an IRPC specification.
48
+ * @param irpc - The IRPC function to construct
49
+ * @param handler - The actual implementation of the IRPC function
50
+ */
51
+ factory.construct = ((irpc, handler) => {
52
+ if (typeof irpc !== "function") throw new Error("Invalid IRPC.");
53
+ const host = registry.get(irpc);
54
+ if (!host) throw new Error("IRPC can not be found.");
55
+ host.handler = handler;
56
+ });
57
+ /**
58
+ * Sets the transport mechanism for the module.
59
+ * @param transport - The transport layer to use for remote calls
60
+ */
61
+ factory.use = ((transport) => {
62
+ if (typeof transport?.send !== "function") throw new Error("Invalid transport.");
63
+ module.transport = transport;
64
+ });
65
+ /**
66
+ * Retrieves an IRPC specification by name.
67
+ * @param name - The name of the IRPC to retrieve
68
+ * @returns The IRPC specification or undefined if not found
69
+ */
70
+ factory.get = ((name) => {
71
+ return store.get(name);
72
+ });
73
+ /**
74
+ * Updates the module configuration.
75
+ * @param config - Configuration properties to update
76
+ */
77
+ factory.configure = ((config$1) => {
78
+ Object.assign(module, { ...config$1 });
79
+ });
80
+ /**
81
+ * Retrieves information about an IRPC by its request details.
82
+ * @param req - Request containing the name of the IRPC to look up
83
+ * @returns The IRPC specification or undefined if not found
84
+ */
85
+ factory.info = ((req) => {
86
+ return store.get(req.name);
87
+ });
88
+ /**
89
+ * Resolves and executes an IRPC call with the provided arguments.
90
+ * @param req - Request containing the name and arguments for the IRPC call
91
+ * @returns The result of executing the IRPC handler
92
+ */
93
+ factory.resolve = ((req) => {
94
+ const host = store.get(req.name);
95
+ if (!host) throw new Error("IRPC can not be found.");
96
+ if (!host.handler) throw new Error("IRPC handler can not be found.");
97
+ return host.handler(...req.args);
98
+ });
99
+ /**
100
+ * Submits a batch of IRPC calls for execution via the transport layer.
101
+ * @param calls - Array of IRPC calls to submit
102
+ */
103
+ module.submit = ((calls) => {
104
+ try {
105
+ const promise = module.transport.send(calls);
106
+ if (promise instanceof Promise) promise.catch((reason) => {
107
+ calls.forEach((call) => {
108
+ call.reject(reason);
109
+ });
110
+ });
111
+ } catch (error) {
112
+ calls.forEach((call) => {
113
+ call.reject(error);
114
+ });
115
+ }
116
+ });
117
+ return factory;
118
+ }
119
+ /**
120
+ * Executes a remote procedure call through the configured transport layer.
121
+ *
122
+ * This function creates an IRPC call payload and sends it through the module's transport mechanism.
123
+ * It handles timeouts and promise resolution/rejection based on the response.
124
+ *
125
+ * @param module - The IRPC module containing transport and configuration
126
+ * @param spec - The IRPC specification defining the remote procedure
127
+ * @param args - Arguments to pass to the remote procedure
128
+ * @returns A promise that resolves with the remote call result or rejects with an error
129
+ * @throws Error if no transport is configured or if the call times out
130
+ */
131
+ function remoteCall(module, spec, ...args) {
132
+ const payload = {
133
+ name: spec.name,
134
+ args
135
+ };
136
+ return new Promise((resolve, reject) => {
137
+ if (!module.transport) {
138
+ reject(/* @__PURE__ */ new Error("IRPC transport can not be found."));
139
+ return;
140
+ }
141
+ const timeout = module.timeout ? setTimeout(() => {
142
+ call.reject(/* @__PURE__ */ new Error("IRPC timeout."));
143
+ }, module.timeout) : void 0;
144
+ const call = new IRPCCall(payload, (value) => {
145
+ resolve(value);
146
+ clearTimeout(timeout);
147
+ }, (reason) => {
148
+ reject(reason);
149
+ clearTimeout(timeout);
150
+ });
151
+ batch(call, module.submit);
152
+ });
153
+ }
154
+
155
+ //#endregion
156
+ export { createModule };
@@ -0,0 +1,224 @@
1
+ import { IRPCCall } from "./call.js";
2
+ import { ZodArray, ZodBoolean, ZodNull, ZodNumber, ZodObject, ZodSafeParseResult, ZodString, ZodUndefined } from "zod/v4";
3
+
4
+ //#region src/types.d.ts
5
+
6
+ /**
7
+ * A registry that maps IRPCHandlers to their corresponding IRPCHosts.
8
+ * Uses WeakMap to avoid memory leaks by not preventing garbage collection of handlers.
9
+ */
10
+ type IRPCRegistry = WeakMap<IRPCHandler, IRPCHost<IRPCInputs, IRPCOutput>>;
11
+ /**
12
+ * A store that maps string identifiers to IRPCHosts.
13
+ * Used to keep track of available RPC hosts by their names.
14
+ */
15
+ type IRPCStore = Map<string, IRPCHost<IRPCInputs, IRPCOutput>>;
16
+ /**
17
+ * A function that authorizes RPC requests.
18
+ * Takes a Request object and returns a boolean (sync or async) indicating authorization status.
19
+ */
20
+ type IRPCAuthorizer = (req: Request) => Promise<boolean> | boolean;
21
+ /**
22
+ * Represents primitive data types that can be used in IRPC communications.
23
+ * Includes string, number, boolean, null, and undefined.
24
+ */
25
+ type IRPCPrimitive = string | number | boolean | null | undefined;
26
+ /**
27
+ * Represents an object structure where keys are strings and values are IRPCData.
28
+ * Used for structured data in RPC communications.
29
+ */
30
+ type IRPCObject = {
31
+ [key: string]: IRPCData;
32
+ };
33
+ /**
34
+ * Represents all possible data types in IRPC, including primitives, objects, and arrays.
35
+ * This is a recursive type that allows nested structures.
36
+ */
37
+ type IRPCData = IRPCPrimitive | IRPCObject | IRPCData[];
38
+ /**
39
+ * Union type of all primitive Zod schema types used for validation.
40
+ */
41
+ type IRPCPrimitiveSchema = ZodString | ZodNumber | ZodBoolean | ZodNull | ZodUndefined;
42
+ /**
43
+ * Zod object schema type used for validating structured data.
44
+ */
45
+ type IRPCObjectSchema = ZodObject;
46
+ /**
47
+ * Zod array schema that can contain primitive schemas or object schemas.
48
+ */
49
+ type IRPCArraySchema = ZodArray<IRPCPrimitiveSchema | IRPCObjectSchema>;
50
+ /**
51
+ * Union type of all possible Zod schema types used in IRPC for input/output validation.
52
+ */
53
+ type IRPCDataSchema = IRPCPrimitiveSchema | IRPCObjectSchema | IRPCArraySchema;
54
+ /**
55
+ * Type representing the result of a Zod schema validation operation.
56
+ */
57
+ type IRPCParseResult = ZodSafeParseResult<IRPCDataSchema>;
58
+ /**
59
+ * Represents an array of input schemas for an RPC function.
60
+ */
61
+ type IRPCInputs = IRPCDataSchema[];
62
+ /**
63
+ * Represents the output schema for an RPC function.
64
+ */
65
+ type IRPCOutput = IRPCDataSchema;
66
+ /**
67
+ * Defines the basic information about an RPC namespace.
68
+ */
69
+ type IRPCNamespace = {
70
+ /** The name of the namespace */
71
+ name: string;
72
+ /** The version of the namespace */
73
+ version: string;
74
+ /** Optional description of the namespace */
75
+ description?: string;
76
+ };
77
+ /**
78
+ * Defines an RPC module which extends a namespace with execution capabilities.
79
+ */
80
+ type IRPCModule = IRPCNamespace & {
81
+ /** Optional function to submit RPC calls */
82
+ submit?: (calls: IRPCCall[]) => Promise<IRPCResponse[]>;
83
+ /** Optional timeout for RPC calls */
84
+ timeout?: number;
85
+ /** Optional transport mechanism for RPC communications */
86
+ transport?: IRPCTransport;
87
+ };
88
+ /**
89
+ * Represents the payload of an RPC call with its name and arguments.
90
+ */
91
+ type IRPCPayload = {
92
+ /** The name of the RPC function to call */
93
+ name: string;
94
+ /** The arguments to pass to the RPC function */
95
+ args: IRPCData[];
96
+ };
97
+ /**
98
+ * Defines the schema for input and output validation of an RPC function.
99
+ */
100
+ type IRPCSchema<I extends IRPCInputs, O extends IRPCOutput> = {
101
+ /** Optional input validation schemas */
102
+ input?: I;
103
+ /** Optional output validation schema */
104
+ output?: O;
105
+ };
106
+ /**
107
+ * Type definition for an RPC handler function.
108
+ * Takes IRPCData arguments and returns a Promise resolving to IRPCData.
109
+ */
110
+ type IRPCHandler = (...args: IRPCData[]) => Promise<IRPCData>;
111
+ /**
112
+ * Specification for an RPC function including its name, schema, and description.
113
+ */
114
+ type IRPCSpec<I extends IRPCInputs, O extends IRPCOutput> = {
115
+ /** The name of the RPC function */
116
+ name: string;
117
+ /** Optional schema for input/output validation */
118
+ schema?: IRPCSchema<I, O>;
119
+ /** Optional description of the RPC function */
120
+ description?: string;
121
+ };
122
+ /**
123
+ * Host definition for an RPC function that combines the specification with execution details.
124
+ */
125
+ type IRPCHost<I extends IRPCInputs, O extends IRPCOutput> = IRPCSpec<I, O> & {
126
+ /** The actual handler function that implements the RPC */
127
+ handler: IRPCHandler;
128
+ };
129
+ /**
130
+ * Factory interface for creating and managing RPC functions.
131
+ */
132
+ interface IRPCFactory {
133
+ /**
134
+ * Creates a new RPC function with the given specification.
135
+ * @param spec The specification for the RPC function
136
+ */
137
+ <F, I extends IRPCInputs = IRPCInputs, O extends IRPCOutput = IRPCOutput>(spec: IRPCSpec<I, O>): F;
138
+ /** The namespace associated with this factory */
139
+ get namespace(): IRPCNamespace;
140
+ /**
141
+ * Sets the transport mechanism for this factory.
142
+ * @param transport The transport to use
143
+ */
144
+ use(transport: IRPCTransport): IRPCFactory;
145
+ /**
146
+ * Gets the specification for an RPC function by name.
147
+ * @param name The name of the RPC function
148
+ */
149
+ get<I extends IRPCInputs, O extends IRPCOutput>(name: string): IRPCSpec<I, O>;
150
+ /**
151
+ * Configures the module settings for this factory.
152
+ * @param config Partial configuration for the module
153
+ */
154
+ configure(config: Partial<IRPCModule>): IRPCFactory;
155
+ /**
156
+ * Constructs an RPC function with its handler.
157
+ * @param irpc The RPC function
158
+ * @param handler The handler that implements the RPC function
159
+ */
160
+ construct<F>(irpc: F, handler: F): IRPCFactory;
161
+ /**
162
+ * Gets information about an RPC request.
163
+ * @param req The RPC request
164
+ */
165
+ info<I extends IRPCInputs, O extends IRPCOutput>(req: IRPCRequest): IRPCSpec<I, O>;
166
+ /**
167
+ * Resolves an RPC request.
168
+ * @param req The RPC request to resolve
169
+ */
170
+ resolve<R>(req: IRPCRequest): Promise<R>;
171
+ }
172
+ /**
173
+ * Represents an incoming RPC request.
174
+ */
175
+ type IRPCRequest = {
176
+ /** Unique identifier for the request */
177
+ id: string;
178
+ /** Name of the RPC function being called */
179
+ name: string;
180
+ /** Arguments for the RPC function */
181
+ args: unknown[];
182
+ };
183
+ /**
184
+ * Represents an RPC response.
185
+ */
186
+ type IRPCResponse = {
187
+ /** Unique identifier matching the request */
188
+ id: string;
189
+ /** Name of the RPC function that was called */
190
+ name: string;
191
+ /** Error message if the call failed */
192
+ error?: string;
193
+ /** Result of the RPC call if successful */
194
+ result?: unknown;
195
+ };
196
+ /**
197
+ * Abstract base class for RPC transport mechanisms.
198
+ */
199
+ declare abstract class IRPCTransport {
200
+ /**
201
+ * Sends RPC calls and returns the responses.
202
+ * @param calls Array of RPC calls to send
203
+ */
204
+ abstract send(calls: IRPCCall[]): Promise<IRPCResponse[]>;
205
+ }
206
+ /**
207
+ * Context storage mechanism for RPC operations.
208
+ */
209
+ type IRPCContext<K, V> = Map<K, V>;
210
+ /**
211
+ * Interface for managing RPC context stores.
212
+ */
213
+ type IRPCContextProvider = {
214
+ /**
215
+ * Runs a function within a specific context.
216
+ * @param ctx The context to run within
217
+ * @param fn The function to execute
218
+ */
219
+ run<R, K, V>(ctx: IRPCContext<K, V>, fn: () => R): R;
220
+ /** Gets the current context store */
221
+ getStore<K, V>(): IRPCContext<K, V>;
222
+ };
223
+ //#endregion
224
+ export { IRPCArraySchema, IRPCAuthorizer, IRPCContext, IRPCContextProvider, IRPCData, IRPCDataSchema, IRPCFactory, IRPCHandler, IRPCHost, IRPCInputs, IRPCModule, IRPCNamespace, IRPCObject, IRPCObjectSchema, IRPCOutput, IRPCParseResult, IRPCPayload, IRPCPrimitive, IRPCPrimitiveSchema, IRPCRegistry, IRPCRequest, IRPCResponse, IRPCSchema, IRPCSpec, IRPCStore, IRPCTransport };
package/dist/types.js ADDED
@@ -0,0 +1,8 @@
1
+ //#region src/types.ts
2
+ /**
3
+ * Abstract base class for RPC transport mechanisms.
4
+ */
5
+ var IRPCTransport = class {};
6
+
7
+ //#endregion
8
+ export { IRPCTransport };
@@ -0,0 +1,17 @@
1
+ //#region src/utils.d.ts
2
+ /**
3
+ * Generates a short unique identifier string.
4
+ *
5
+ * This function creates a unique ID by combining:
6
+ * - A timestamp in base-36 format
7
+ * - A sequence number in base-36 format (padded to 3 characters)
8
+ * - A random string in base-36 format (4 characters)
9
+ *
10
+ * The sequence number ensures uniqueness when multiple IDs are generated within the same millisecond.
11
+ * The random part adds additional entropy to reduce predictability.
12
+ *
13
+ * @returns A unique string identifier
14
+ */
15
+ declare function shortId(): string;
16
+ //#endregion
17
+ export { shortId };
package/dist/utils.js ADDED
@@ -0,0 +1,26 @@
1
+ //#region src/utils.ts
2
+ let lastTimestamp = 0;
3
+ let sequence = 0;
4
+ /**
5
+ * Generates a short unique identifier string.
6
+ *
7
+ * This function creates a unique ID by combining:
8
+ * - A timestamp in base-36 format
9
+ * - A sequence number in base-36 format (padded to 3 characters)
10
+ * - A random string in base-36 format (4 characters)
11
+ *
12
+ * The sequence number ensures uniqueness when multiple IDs are generated within the same millisecond.
13
+ * The random part adds additional entropy to reduce predictability.
14
+ *
15
+ * @returns A unique string identifier
16
+ */
17
+ function shortId() {
18
+ const timestamp = Date.now();
19
+ if (timestamp === lastTimestamp) sequence++;
20
+ else sequence = 0;
21
+ lastTimestamp = timestamp;
22
+ return `${timestamp.toString(36)}${sequence.toString(36).padStart(3, "0")}${Math.random().toString(36).substring(2, 6)}`;
23
+ }
24
+
25
+ //#endregion
26
+ export { shortId };
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "type": "module",
3
+ "name": "@irpclib/irpc",
4
+ "version": "0.0.1",
5
+ "types": "./dist/index.d.ts",
6
+ "module": "./dist/index.js",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.js"
11
+ }
12
+ },
13
+ "files": [
14
+ "dist"
15
+ ],
16
+ "directories": {
17
+ "dist": "./dist"
18
+ },
19
+ "publishConfig": {
20
+ "access": "public"
21
+ },
22
+ "devDependencies": {
23
+ "@biomejs/biome": "2.3.3",
24
+ "@vitest/coverage-v8": "^3.2.4",
25
+ "@vitest/ui": "^3.2.4",
26
+ "esbuild-raw-plugin": "^0.3.1",
27
+ "prettier": "^3.6.2",
28
+ "publint": "^0.3.15",
29
+ "rimraf": "^6.0.1",
30
+ "tsdown": "^0.15.9",
31
+ "vite": "^7.1.12",
32
+ "vitest": "^3.2.4",
33
+ "zod": "^4.1.5",
34
+ "@types/bun": "1.3.1"
35
+ },
36
+ "peerDependencies": {
37
+ "typescript": "^5.9.0"
38
+ },
39
+ "optionalDependencies": {
40
+ "zod": "^4.1.5"
41
+ },
42
+ "scripts": {
43
+ "dev": "tsdown --watch",
44
+ "clean": "rimraf dist",
45
+ "build": "tsdown && publint",
46
+ "format": "prettier --write .",
47
+ "test": "rimraf coverage && vitest --run",
48
+ "test:preview": "rimraf coverage && vitest --run && vite preview --outDir coverage",
49
+ "prepublish": "bun run format && bun run clean && tsdown && publint"
50
+ },
51
+ "license": "MIT"
52
+ }
package/readme.md ADDED
@@ -0,0 +1,166 @@
1
+ # IRPC - Isomorphic Remote Procedure Call
2
+
3
+ IRPC (Isomorphic Remote Procedure Call) is a revolutionary approach to distributed computing that eliminates the cognitive overhead of network communication. It enables developers to invoke remote functions with the same ergonomics as local function calls, abstracting away the transport layer entirely.
4
+
5
+ Unlike traditional approaches like REST APIs, GraphQL, or gRPC, IRPC removes the need to think about endpoints, serialization, or transport protocols. You focus on business logic while IRPC handles the communication complexity transparently.
6
+
7
+ ## Learn More
8
+
9
+ For detailed documentation, visit [https://irpc.anchorlib.dev](https://irpc.anchorlib.dev)
10
+
11
+ ## Quick Start
12
+
13
+ ### Create a Module
14
+
15
+ ```ts
16
+ import { createModule } from '@irpclib/irpc';
17
+
18
+ const irpc = createModule({ name: 'my-module', version: '1.0.0' });
19
+ ```
20
+
21
+ ### Define Functions
22
+
23
+ ```ts
24
+ export const greet = irpc<(name: string) => Promise<string>>({
25
+ name: 'greet'
26
+ });
27
+ ```
28
+
29
+ ### Set up Transport (Client-side)
30
+
31
+ ```ts
32
+ import { HTTPTransport } from '@irpclib/http';
33
+
34
+ export const transport = new HTTPTransport(
35
+ {
36
+ baseURL: 'http://localhost:3000',
37
+ endpoint: '/rpc',
38
+ },
39
+ irpc
40
+ );
41
+ ```
42
+
43
+ ### Implement Handlers (Server-side)
44
+
45
+ ```ts
46
+ irpc.construct(greet, async (name) => {
47
+ return `Hello, ${name}!`;
48
+ });
49
+ ```
50
+
51
+ ### Server Setup
52
+
53
+ ```ts
54
+ import { setContextStore } from '@irpclib/irpc';
55
+ import { AsyncLocalStorage } from 'async_hooks';
56
+
57
+ setContextStore(new AsyncLocalStorage());
58
+
59
+ Bun.serve({
60
+ routes: {
61
+ [transport.endpoint]: {
62
+ GET: () => new Response('OK'),
63
+ POST: (req) => transport.respond(req),
64
+ }
65
+ },
66
+ });
67
+ ```
68
+
69
+ ### Client Usage
70
+
71
+ ```ts
72
+ import { greet } from './my-module';
73
+
74
+ const message = await greet('World');
75
+ console.log(message); // "Hello, World!"
76
+ ```
77
+
78
+ ## Key Features
79
+
80
+ - **Isomorphic Design**: Call functions identically on client and server
81
+ - **Zero Boilerplate**: No REST endpoints, no GraphQL schemas, no complex serialization
82
+ - **Transport Agnostic**: Switch between HTTP, WebSockets, and other transports without changing your business logic
83
+ - **End-to-End Type Safety**: Compile-time validation from client to server
84
+ - **Performance Optimized**: Intelligent batching and connection reuse
85
+
86
+ ## Core Components
87
+
88
+ ### Module
89
+
90
+ A module is a container for your IRPC functions. It manages the registry of functions and their implementations.
91
+
92
+ ```ts
93
+ const irpc = createModule({ name: 'fs', version: '1.0.0' });
94
+ ```
95
+
96
+ ### Function
97
+
98
+ Functions are the core building blocks of IRPC. They define the interface for remote calls and can be implemented on the server side.
99
+
100
+ ```ts
101
+ export const readFile = irpc<(path: string) => Promise<string>>('readFile');
102
+ ```
103
+
104
+ ### Transport
105
+
106
+ Transports handle the actual communication between client and server. IRPC is transport-agnostic, allowing you to switch between different communication protocols.
107
+
108
+ ```ts
109
+ export const transport = new IRPCHttpTransport(
110
+ {
111
+ baseURL: 'http://localhost:3000',
112
+ endpoint: '/rpc',
113
+ timeout: 1000,
114
+ headers: {
115
+ 'Content-Type': 'application/json',
116
+ },
117
+ },
118
+ irpc
119
+ );
120
+ ```
121
+
122
+ The transport automatically registers itself with the IRPC module during construction, so there's no need to manually register it.
123
+
124
+ ## Usage
125
+
126
+ ### Client
127
+
128
+ On the client side, you simply import and call your functions as if they were local:
129
+
130
+ ```ts
131
+ import { readFile } from './fs';
132
+
133
+ console.log(await readFile('/path/to/file'));
134
+ ```
135
+
136
+ ### Server
137
+
138
+ On the server side, you implement the actual functionality for your functions:
139
+
140
+ ```ts
141
+ import { transport } from './fs';
142
+ import { setContextStore } from '@irpclib/irpc';
143
+
144
+ setContextStore(new AsyncLocalStorage());
145
+
146
+ Bun.serve({
147
+ routes: {
148
+ [transport.endpoint]: {
149
+ GET: () => {
150
+ return new Response('Ok!');
151
+ },
152
+ POST: (req) => transport.respond(req),
153
+ }
154
+ },
155
+ });
156
+ ```
157
+
158
+ #### Handler
159
+
160
+ Handlers are the actual implementations of your remote functions:
161
+
162
+ ```ts
163
+ irpc.construct(readFile, async (path) => {
164
+ // Implementation goes here
165
+ });
166
+ ```