@pyrpc/client 0.1.0-alpha.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/README.md +39 -0
- package/dist/index.d.mts +51 -0
- package/dist/index.d.ts +51 -0
- package/dist/index.js +127 -0
- package/dist/index.mjs +98 -0
- package/package.json +32 -0
package/README.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# @pyrpc/client
|
|
2
|
+
|
|
3
|
+
The universal TypeScript client for pyRPC.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @pyrpc/client
|
|
9
|
+
# or
|
|
10
|
+
pnpm add @pyrpc/client
|
|
11
|
+
# or
|
|
12
|
+
yarn add @pyrpc/client
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { createClient } from '@pyrpc/client';
|
|
19
|
+
import type { AppRouter } from './server-types'; // Generated by pyrpc-codegen
|
|
20
|
+
|
|
21
|
+
const client = createClient<AppRouter>({
|
|
22
|
+
baseUrl: 'http://localhost:8000',
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// Call remote procedures as local methods
|
|
26
|
+
const user = await client.get_user({ id: 1 });
|
|
27
|
+
console.log(user.name);
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Dynamic Headers (Auth)
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
const client = createClient({
|
|
34
|
+
baseUrl: 'http://localhost:8000',
|
|
35
|
+
headers: () => ({
|
|
36
|
+
Authorization: `Bearer ${localStorage.getItem('token')}`,
|
|
37
|
+
}),
|
|
38
|
+
});
|
|
39
|
+
```
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
interface RpcRequest {
|
|
2
|
+
id: string;
|
|
3
|
+
method: string;
|
|
4
|
+
params: any[] | Record<string, any>;
|
|
5
|
+
}
|
|
6
|
+
interface RpcResponse<T = any> {
|
|
7
|
+
id: string;
|
|
8
|
+
result: T | null;
|
|
9
|
+
error: {
|
|
10
|
+
code: number;
|
|
11
|
+
message: string;
|
|
12
|
+
data?: any;
|
|
13
|
+
} | null;
|
|
14
|
+
}
|
|
15
|
+
interface ClientOptions {
|
|
16
|
+
/**
|
|
17
|
+
* The base URL of the pyRPC server.
|
|
18
|
+
* If omitted in a browser environment, it defaults to the current origin + '/rpc'.
|
|
19
|
+
*/
|
|
20
|
+
baseUrl?: string;
|
|
21
|
+
/**
|
|
22
|
+
* Optional custom headers to send with each request.
|
|
23
|
+
*/
|
|
24
|
+
headers?: HeadersInit | (() => HeadersInit | Promise<HeadersInit>);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
declare class PyRPCClient {
|
|
28
|
+
private baseUrl;
|
|
29
|
+
private options;
|
|
30
|
+
constructor(options?: ClientOptions);
|
|
31
|
+
/**
|
|
32
|
+
* Internal method to perform the fetch request.
|
|
33
|
+
*/
|
|
34
|
+
private request;
|
|
35
|
+
/**
|
|
36
|
+
* Creates a proxy that allows calling remote procedures as if they were local methods.
|
|
37
|
+
*/
|
|
38
|
+
get rpc(): any;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Modern factory API for pyRPC.
|
|
42
|
+
*/
|
|
43
|
+
declare function createClient<TTypes = any>(options?: ClientOptions): PyRPCClient & TTypes;
|
|
44
|
+
|
|
45
|
+
declare class PyRPCError extends Error {
|
|
46
|
+
readonly code: number;
|
|
47
|
+
readonly data?: any;
|
|
48
|
+
constructor(code: number, message: string, data?: any);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export { type ClientOptions, PyRPCClient, PyRPCError, type RpcRequest, type RpcResponse, createClient };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
interface RpcRequest {
|
|
2
|
+
id: string;
|
|
3
|
+
method: string;
|
|
4
|
+
params: any[] | Record<string, any>;
|
|
5
|
+
}
|
|
6
|
+
interface RpcResponse<T = any> {
|
|
7
|
+
id: string;
|
|
8
|
+
result: T | null;
|
|
9
|
+
error: {
|
|
10
|
+
code: number;
|
|
11
|
+
message: string;
|
|
12
|
+
data?: any;
|
|
13
|
+
} | null;
|
|
14
|
+
}
|
|
15
|
+
interface ClientOptions {
|
|
16
|
+
/**
|
|
17
|
+
* The base URL of the pyRPC server.
|
|
18
|
+
* If omitted in a browser environment, it defaults to the current origin + '/rpc'.
|
|
19
|
+
*/
|
|
20
|
+
baseUrl?: string;
|
|
21
|
+
/**
|
|
22
|
+
* Optional custom headers to send with each request.
|
|
23
|
+
*/
|
|
24
|
+
headers?: HeadersInit | (() => HeadersInit | Promise<HeadersInit>);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
declare class PyRPCClient {
|
|
28
|
+
private baseUrl;
|
|
29
|
+
private options;
|
|
30
|
+
constructor(options?: ClientOptions);
|
|
31
|
+
/**
|
|
32
|
+
* Internal method to perform the fetch request.
|
|
33
|
+
*/
|
|
34
|
+
private request;
|
|
35
|
+
/**
|
|
36
|
+
* Creates a proxy that allows calling remote procedures as if they were local methods.
|
|
37
|
+
*/
|
|
38
|
+
get rpc(): any;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Modern factory API for pyRPC.
|
|
42
|
+
*/
|
|
43
|
+
declare function createClient<TTypes = any>(options?: ClientOptions): PyRPCClient & TTypes;
|
|
44
|
+
|
|
45
|
+
declare class PyRPCError extends Error {
|
|
46
|
+
readonly code: number;
|
|
47
|
+
readonly data?: any;
|
|
48
|
+
constructor(code: number, message: string, data?: any);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export { type ClientOptions, PyRPCClient, PyRPCError, type RpcRequest, type RpcResponse, createClient };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
PyRPCClient: () => PyRPCClient,
|
|
24
|
+
PyRPCError: () => PyRPCError,
|
|
25
|
+
createClient: () => createClient
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(index_exports);
|
|
28
|
+
|
|
29
|
+
// src/error.ts
|
|
30
|
+
var PyRPCError = class _PyRPCError extends Error {
|
|
31
|
+
code;
|
|
32
|
+
data;
|
|
33
|
+
constructor(code, message, data) {
|
|
34
|
+
super(message);
|
|
35
|
+
this.name = "PyRPCError";
|
|
36
|
+
this.code = code;
|
|
37
|
+
this.data = data;
|
|
38
|
+
Object.setPrototypeOf(this, _PyRPCError.prototype);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// src/client.ts
|
|
43
|
+
var NO_BASE_URL_ERROR = `
|
|
44
|
+
No baseUrl detected.
|
|
45
|
+
|
|
46
|
+
pyRPC could not automatically determine your server location.
|
|
47
|
+
|
|
48
|
+
If your frontend and backend are deployed separately, provide:
|
|
49
|
+
|
|
50
|
+
createClient({
|
|
51
|
+
baseUrl: "https://api.example.com"
|
|
52
|
+
})
|
|
53
|
+
`;
|
|
54
|
+
var PyRPCClient = class {
|
|
55
|
+
baseUrl;
|
|
56
|
+
options;
|
|
57
|
+
constructor(options = {}) {
|
|
58
|
+
let baseUrl = options.baseUrl;
|
|
59
|
+
if (!baseUrl) {
|
|
60
|
+
if (typeof window !== "undefined" && window.location) {
|
|
61
|
+
baseUrl = window.location.origin;
|
|
62
|
+
} else {
|
|
63
|
+
throw new Error(NO_BASE_URL_ERROR);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
this.baseUrl = baseUrl.replace(/\/$/, "");
|
|
67
|
+
this.options = options;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Internal method to perform the fetch request.
|
|
71
|
+
*/
|
|
72
|
+
async request(method, params) {
|
|
73
|
+
const id = Math.random().toString(36).substring(7);
|
|
74
|
+
const body = { id, method, params };
|
|
75
|
+
const baseHeaders = {
|
|
76
|
+
"Content-Type": "application/json"
|
|
77
|
+
};
|
|
78
|
+
let userHeaders = {};
|
|
79
|
+
if (this.options.headers) {
|
|
80
|
+
userHeaders = typeof this.options.headers === "function" ? await this.options.headers() : this.options.headers;
|
|
81
|
+
}
|
|
82
|
+
const headers = { ...baseHeaders, ...Object.fromEntries(new Headers(userHeaders).entries()) };
|
|
83
|
+
const response = await fetch(`${this.baseUrl}/rpc`, {
|
|
84
|
+
method: "POST",
|
|
85
|
+
headers,
|
|
86
|
+
body: JSON.stringify(body)
|
|
87
|
+
});
|
|
88
|
+
if (!response.ok) {
|
|
89
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
90
|
+
}
|
|
91
|
+
const data = await response.json();
|
|
92
|
+
if (data.error) {
|
|
93
|
+
throw new PyRPCError(data.error.code, data.error.message, data.error.data);
|
|
94
|
+
}
|
|
95
|
+
return data.result;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Creates a proxy that allows calling remote procedures as if they were local methods.
|
|
99
|
+
*/
|
|
100
|
+
get rpc() {
|
|
101
|
+
return new Proxy({}, {
|
|
102
|
+
get: (_, method) => {
|
|
103
|
+
return (...args) => {
|
|
104
|
+
const params = args.length === 1 && typeof args[0] === "object" && !Array.isArray(args[0]) ? args[0] : args;
|
|
105
|
+
return this.request(method, params);
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
function createClient(options = {}) {
|
|
112
|
+
const client = new PyRPCClient(options);
|
|
113
|
+
return new Proxy(client, {
|
|
114
|
+
get(target, prop, receiver) {
|
|
115
|
+
if (prop in target) {
|
|
116
|
+
return Reflect.get(target, prop, receiver);
|
|
117
|
+
}
|
|
118
|
+
return target.rpc[prop];
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
123
|
+
0 && (module.exports = {
|
|
124
|
+
PyRPCClient,
|
|
125
|
+
PyRPCError,
|
|
126
|
+
createClient
|
|
127
|
+
});
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// src/error.ts
|
|
2
|
+
var PyRPCError = class _PyRPCError extends Error {
|
|
3
|
+
code;
|
|
4
|
+
data;
|
|
5
|
+
constructor(code, message, data) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = "PyRPCError";
|
|
8
|
+
this.code = code;
|
|
9
|
+
this.data = data;
|
|
10
|
+
Object.setPrototypeOf(this, _PyRPCError.prototype);
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
// src/client.ts
|
|
15
|
+
var NO_BASE_URL_ERROR = `
|
|
16
|
+
No baseUrl detected.
|
|
17
|
+
|
|
18
|
+
pyRPC could not automatically determine your server location.
|
|
19
|
+
|
|
20
|
+
If your frontend and backend are deployed separately, provide:
|
|
21
|
+
|
|
22
|
+
createClient({
|
|
23
|
+
baseUrl: "https://api.example.com"
|
|
24
|
+
})
|
|
25
|
+
`;
|
|
26
|
+
var PyRPCClient = class {
|
|
27
|
+
baseUrl;
|
|
28
|
+
options;
|
|
29
|
+
constructor(options = {}) {
|
|
30
|
+
let baseUrl = options.baseUrl;
|
|
31
|
+
if (!baseUrl) {
|
|
32
|
+
if (typeof window !== "undefined" && window.location) {
|
|
33
|
+
baseUrl = window.location.origin;
|
|
34
|
+
} else {
|
|
35
|
+
throw new Error(NO_BASE_URL_ERROR);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
this.baseUrl = baseUrl.replace(/\/$/, "");
|
|
39
|
+
this.options = options;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Internal method to perform the fetch request.
|
|
43
|
+
*/
|
|
44
|
+
async request(method, params) {
|
|
45
|
+
const id = Math.random().toString(36).substring(7);
|
|
46
|
+
const body = { id, method, params };
|
|
47
|
+
const baseHeaders = {
|
|
48
|
+
"Content-Type": "application/json"
|
|
49
|
+
};
|
|
50
|
+
let userHeaders = {};
|
|
51
|
+
if (this.options.headers) {
|
|
52
|
+
userHeaders = typeof this.options.headers === "function" ? await this.options.headers() : this.options.headers;
|
|
53
|
+
}
|
|
54
|
+
const headers = { ...baseHeaders, ...Object.fromEntries(new Headers(userHeaders).entries()) };
|
|
55
|
+
const response = await fetch(`${this.baseUrl}/rpc`, {
|
|
56
|
+
method: "POST",
|
|
57
|
+
headers,
|
|
58
|
+
body: JSON.stringify(body)
|
|
59
|
+
});
|
|
60
|
+
if (!response.ok) {
|
|
61
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
62
|
+
}
|
|
63
|
+
const data = await response.json();
|
|
64
|
+
if (data.error) {
|
|
65
|
+
throw new PyRPCError(data.error.code, data.error.message, data.error.data);
|
|
66
|
+
}
|
|
67
|
+
return data.result;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Creates a proxy that allows calling remote procedures as if they were local methods.
|
|
71
|
+
*/
|
|
72
|
+
get rpc() {
|
|
73
|
+
return new Proxy({}, {
|
|
74
|
+
get: (_, method) => {
|
|
75
|
+
return (...args) => {
|
|
76
|
+
const params = args.length === 1 && typeof args[0] === "object" && !Array.isArray(args[0]) ? args[0] : args;
|
|
77
|
+
return this.request(method, params);
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
function createClient(options = {}) {
|
|
84
|
+
const client = new PyRPCClient(options);
|
|
85
|
+
return new Proxy(client, {
|
|
86
|
+
get(target, prop, receiver) {
|
|
87
|
+
if (prop in target) {
|
|
88
|
+
return Reflect.get(target, prop, receiver);
|
|
89
|
+
}
|
|
90
|
+
return target.rpc[prop];
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
export {
|
|
95
|
+
PyRPCClient,
|
|
96
|
+
PyRPCError,
|
|
97
|
+
createClient
|
|
98
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pyrpc/client",
|
|
3
|
+
"version": "0.1.0-alpha.1",
|
|
4
|
+
"description": "Universal TypeScript client for pyRPC",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsup src/index.ts --format cjs,esm --dts",
|
|
13
|
+
"dev": "tsup src/index.ts --format cjs,esm --watch --dts",
|
|
14
|
+
"lint": "eslint src/**/*.ts",
|
|
15
|
+
"test": "vitest run"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"rpc",
|
|
19
|
+
"pyrpc",
|
|
20
|
+
"typescript",
|
|
21
|
+
"client",
|
|
22
|
+
"api"
|
|
23
|
+
],
|
|
24
|
+
"author": "",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@types/node": "^20.19.39",
|
|
28
|
+
"tsup": "^8.5.1",
|
|
29
|
+
"typescript": "^5.9.3",
|
|
30
|
+
"vitest": "^3.0.0"
|
|
31
|
+
}
|
|
32
|
+
}
|