@pyrpc/client 0.3.3 → 0.6.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # @pyrpc/client
2
2
 
3
- Universal TypeScript client for [pyRPC](https://pyrpc.dev). Type-safe RPC calls to your Python backend install, import, call.
3
+ Universal TypeScript client for [pyRPC](https://pyrpc.com). Type-safe RPC calls to your Python backend - install, import, call.
4
4
 
5
5
  ## Installation
6
6
 
@@ -12,26 +12,24 @@ pnpm add @pyrpc/client
12
12
  bun add @pyrpc/client
13
13
  ```
14
14
 
15
- The postinstall script in `@pyrpc/types` will prompt for your server URL and generate typed contracts automatically.
15
+ On install, `@pyrpc/client` runs a `postinstall` script that prompts you to choose a **distribution mode**:
16
16
 
17
- For CI, set the `PYRPC_URL` environment variable:
17
+ - **Workspace** types are written by the server-side `pyrpc dev` / `pyrpc codegen` commands.
18
+ - **Server** — types are fetched from a running server at `npx pyrpc sync` time.
18
19
 
19
- ```bash
20
- PYRPC_URL=https://api.example.com npm install @pyrpc/client
21
- ```
20
+ CI / non-TTY environments skip the prompt silently. Re-run with `npx pyrpc sync` to generate types later.
22
21
 
23
22
  ## Usage
24
23
 
25
24
  ```typescript
26
- import { createClient } from "@pyrpc/client";
27
- import type { Types } from "@pyrpc/types";
25
+ import { createClient, type Types } from "@pyrpc/client";
28
26
 
29
27
  const client = createClient<Types>({
30
28
  baseUrl: "https://api.example.com",
31
29
  });
32
30
 
33
31
  const user = await client.get_user(1);
34
- console.log(user.name);
32
+ console.log(user.name); // Fully typed - no manual type definitions needed
35
33
  ```
36
34
 
37
35
  The proxy-based API lets you call any remote procedure as a local method. Parameters are passed positionally or as a single object for named arguments.
@@ -50,14 +48,31 @@ try {
50
48
  }
51
49
  ```
52
50
 
51
+ ## CLI — `npx pyrpc sync`
52
+
53
+ After install, you can sync types from a server distribution:
54
+
55
+ ```bash
56
+ npx pyrpc sync
57
+ ```
58
+
59
+ Reads `pyrpc-client.json` and behaves according to the configured distribution:
60
+
61
+ - **workspace** — prints "Nothing to sync — server writes types directly."
62
+ - **server** — fetches schema from the configured `server_url` and regenerates `@pyrpc/types/src/index.ts`.
63
+
64
+ Run `npx pyrpc --help` for options.
65
+
53
66
  ## API
54
67
 
55
- ### `createClient<TTypes>(options?)`
68
+ ### `createClient<T>(options?)`
69
+
70
+ Creates a proxy client that forwards method calls to the server. The generic parameter `T` is your `Types` interface for full type safety.
56
71
 
57
- Creates a proxy client that forwards method calls to the server.
72
+ - `baseUrl` - Server root URL (defaults to `window.location.origin` in browsers)
73
+ - `headers` - Static or async `HeadersInit`
58
74
 
59
- - `baseUrl` Server root URL (defaults to `window.location.origin` in browsers)
60
- - `headers` — Static or async `HeadersInit`
75
+ **Note:** There is no `.rpc` property. Call methods directly on the client object.
61
76
 
62
77
  ## Keywords
63
78
 
package/cli.js ADDED
@@ -0,0 +1,194 @@
1
+ #!/usr/bin/env node
2
+ var fs = require('fs');
3
+ var path = require('path');
4
+ var http = require('http');
5
+ var https = require('https');
6
+
7
+ var TYPES_OUTPUT;
8
+ try {
9
+ var typesDir = path.dirname(require.resolve('@pyrpc/types/package.json'));
10
+ TYPES_OUTPUT = path.join(typesDir, 'src', 'index.ts');
11
+ } catch (e) {
12
+ TYPES_OUTPUT = path.join(__dirname, 'node_modules', '@pyrpc', 'types', 'src', 'index.ts');
13
+ }
14
+
15
+ function findConfig() {
16
+ var dir = process.cwd();
17
+ while (true) {
18
+ var cfgPath = path.join(dir, 'pyrpc-client.json');
19
+ if (fs.existsSync(cfgPath)) {
20
+ return cfgPath;
21
+ }
22
+ var parent = path.dirname(dir);
23
+ if (parent === dir) return null;
24
+ dir = parent;
25
+ }
26
+ }
27
+
28
+ var TYPE_MAP = {
29
+ int: 'number',
30
+ float: 'number',
31
+ str: 'string',
32
+ bool: 'boolean',
33
+ None: 'null',
34
+ NoneType: 'null',
35
+ Any: 'any',
36
+ };
37
+
38
+ function toTs(t) {
39
+ if (!t || t === 'None') return 'void';
40
+ var m = t.match(/^<class\s+'([^'>]+)'>$/);
41
+ if (m) {
42
+ var n = m[1];
43
+ if (TYPE_MAP[n]) return TYPE_MAP[n];
44
+ if (n[0] >= 'A' && n[0] <= 'Z') return n;
45
+ return 'any';
46
+ }
47
+ var s = t.replace(/^typing\./, '');
48
+ var o = s.match(/^Optional\[(.+)\]$/);
49
+ if (o) return toTs(o[1]) + ' | null';
50
+ var l = s.match(/^(?:List|list)\[(.+)\]$/);
51
+ if (l) return toTs(l[1]) + '[]';
52
+ var d = s.match(/^(?:Dict|dict)\[([^,]+),\s*(.+)\]$/);
53
+ if (d) return 'Record<' + toTs(d[1]) + ', ' + toTs(d[2]) + '>';
54
+ var u = s.match(/^Union\[(.+)\]$/);
55
+ if (u) return u[1].split(',').map(function(p) { return toTs(p.trim()); }).join(' | ');
56
+ var tup = s.match(/^(?:Tuple|tuple)\[([^\]]+)\]$/);
57
+ if (tup) return '[' + tup[1].split(',').map(function(p) { return toTs(p.trim()); }).join(', ') + ']';
58
+ return 'any';
59
+ }
60
+
61
+ function generate(schemas) {
62
+ var lines = [];
63
+ lines.push('// Auto-generated by @pyrpc/types');
64
+ lines.push('// Schema fetched from: ' + (process.env.PYRPC_URL || 'http://localhost:8000'));
65
+ lines.push('');
66
+ var names = Object.keys(schemas);
67
+ for (var i = 0; i < names.length; i++) {
68
+ var name = names[i];
69
+ var schema = schemas[name];
70
+ lines.push('export interface ' + name + 'Params {');
71
+ var params = schema.parameters || [];
72
+ for (var j = 0; j < params.length; j++) {
73
+ var p = params[j];
74
+ lines.push(' ' + p.name + ': ' + toTs(p.type) + ';');
75
+ }
76
+ lines.push('}');
77
+ lines.push('');
78
+ lines.push('export interface ' + name + 'Result {');
79
+ lines.push(' data: ' + toTs(schema.return_type) + ';');
80
+ lines.push('}');
81
+ lines.push('');
82
+ }
83
+ lines.push('export interface Types {');
84
+ for (var k = 0; k < names.length; k++) {
85
+ lines.push(' ' + names[k] + ': { params: ' + names[k] + 'Params; result: ' + names[k] + 'Result };');
86
+ }
87
+ lines.push('}');
88
+ lines.push('');
89
+ return lines.join('\n');
90
+ }
91
+
92
+ function fetchSchema(url) {
93
+ var endpoint = url.replace(/\/+$/, '');
94
+ if (endpoint.indexOf('/rpc') !== endpoint.length - 4) {
95
+ endpoint += '/rpc';
96
+ }
97
+ var mod = endpoint.indexOf('https') === 0 ? https : http;
98
+ return new Promise(function(resolve, reject) {
99
+ mod.get(endpoint, function(res) {
100
+ var data = '';
101
+ res.on('data', function(c) { data += c; });
102
+ res.on('end', function() {
103
+ if (res.statusCode !== 200) return reject(new Error('HTTP ' + res.statusCode));
104
+ try { resolve(JSON.parse(data)); }
105
+ catch (e) { reject(e); }
106
+ });
107
+ }).on('error', reject);
108
+ });
109
+ }
110
+
111
+ function printHelp() {
112
+ console.log('');
113
+ console.log(' pyRPC client CLI');
114
+ console.log('');
115
+ console.log(' Usage:');
116
+ console.log(' npx pyrpc Sync types from server (default)');
117
+ console.log(' npx pyrpc sync Same as above');
118
+ console.log(' npx pyrpc --help Show this help');
119
+ console.log('');
120
+ console.log(' The sync command reads pyrpc-client.json from your project');
121
+ console.log(' root and fetches the latest RPC schema from the server.');
122
+ console.log('');
123
+ console.log(' In workspace mode, the server writes types directly to your');
124
+ console.log(' project \u2014 no sync needed.');
125
+ console.log('');
126
+ }
127
+
128
+ function main() {
129
+ var args = process.argv.slice(2);
130
+
131
+ if (args[0] === '--help' || args[0] === '-h') {
132
+ printHelp();
133
+ return Promise.resolve();
134
+ }
135
+
136
+ if (args[0] && args[0] !== 'sync') {
137
+ console.log('Unknown command: ' + args[0]);
138
+ console.log('Usage: npx pyrpc sync');
139
+ return Promise.resolve();
140
+ }
141
+
142
+ var cfgPath = findConfig();
143
+ if (!cfgPath) {
144
+ console.log('');
145
+ console.log(' No pyrpc-client.json found.');
146
+ console.log(' Run npm install @pyrpc/client to set up,');
147
+ console.log(' or create pyrpc-client.json manually.');
148
+ console.log('');
149
+ return Promise.resolve();
150
+ }
151
+
152
+ var config;
153
+ try {
154
+ config = JSON.parse(fs.readFileSync(cfgPath, 'utf-8'));
155
+ } catch (e) {
156
+ console.log(' \u2717 Error reading ' + cfgPath);
157
+ return Promise.resolve();
158
+ }
159
+
160
+ if (config.distribution === 'workspace') {
161
+ console.log('');
162
+ console.log(' Nothing to sync \u2014 server writes types directly to your project.');
163
+ console.log(' Run pyrpc dev on your backend to regenerate types on change.');
164
+ console.log('');
165
+ return Promise.resolve();
166
+ }
167
+
168
+ if (config.distribution === 'server') {
169
+ var url = config.server_url;
170
+ if (!url) {
171
+ console.log(' \u2717 pyrpc-client.json is missing server_url');
172
+ return Promise.resolve();
173
+ }
174
+
175
+ return fetchSchema(url).then(function(schemas) {
176
+ var code = generate(schemas);
177
+ fs.mkdirSync(path.dirname(TYPES_OUTPUT), { recursive: true });
178
+ fs.writeFileSync(TYPES_OUTPUT, code, 'utf-8');
179
+ console.log(' \u2713 Types synced: ' + Object.keys(schemas).length + ' procedures');
180
+ console.log(' Import: import { createClient, type Types } from "@pyrpc/client"');
181
+ }).catch(function(err) {
182
+ console.log(' \u2717 Could not fetch schema from ' + url + ' (' + err.message + ')');
183
+ });
184
+ }
185
+
186
+ console.log(' \u2717 Unknown distribution: ' + config.distribution);
187
+ return Promise.resolve();
188
+ }
189
+
190
+ if (require.main === module) {
191
+ main().catch(function(e) {});
192
+ }
193
+
194
+ module.exports = { main, toTs, generate, fetchSchema, findConfig, printHelp };
package/dist/index.d.mts CHANGED
@@ -24,23 +24,22 @@ interface ClientOptions {
24
24
  headers?: HeadersInit | (() => HeadersInit | Promise<HeadersInit>);
25
25
  }
26
26
 
27
- declare class PyRPCClient {
28
- private url;
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
27
  /**
41
- * Modern factory API for pyRPC.
28
+ * Creates a typed pyRPC client.
29
+ *
30
+ * Pass your generated `Types` interface as the generic parameter
31
+ * to get full type safety and auto-complete for all RPC procedures.
32
+ *
33
+ * @example
34
+ * ```typescript
35
+ * import { createClient } from "@pyrpc/client"
36
+ * import type { Types } from "@pyrpc/types"
37
+ *
38
+ * const api = createClient<Types>({ baseUrl: "http://localhost:8000" })
39
+ * const user = await api.get_user("John")
40
+ * ```
42
41
  */
43
- declare function createClient<TTypes = any>(options?: ClientOptions): TTypes;
42
+ declare function createClient<T = any>(options?: ClientOptions): T;
44
43
 
45
44
  declare class PyRPCError extends Error {
46
45
  readonly code: number;
@@ -48,4 +47,4 @@ declare class PyRPCError extends Error {
48
47
  constructor(code: number, message: string, data?: any);
49
48
  }
50
49
 
51
- export { type ClientOptions, PyRPCClient, PyRPCError, type RpcRequest, type RpcResponse, createClient };
50
+ export { type ClientOptions, PyRPCError, type RpcRequest, type RpcResponse, createClient };
package/dist/index.d.ts CHANGED
@@ -24,23 +24,22 @@ interface ClientOptions {
24
24
  headers?: HeadersInit | (() => HeadersInit | Promise<HeadersInit>);
25
25
  }
26
26
 
27
- declare class PyRPCClient {
28
- private url;
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
27
  /**
41
- * Modern factory API for pyRPC.
28
+ * Creates a typed pyRPC client.
29
+ *
30
+ * Pass your generated `Types` interface as the generic parameter
31
+ * to get full type safety and auto-complete for all RPC procedures.
32
+ *
33
+ * @example
34
+ * ```typescript
35
+ * import { createClient } from "@pyrpc/client"
36
+ * import type { Types } from "@pyrpc/types"
37
+ *
38
+ * const api = createClient<Types>({ baseUrl: "http://localhost:8000" })
39
+ * const user = await api.get_user("John")
40
+ * ```
42
41
  */
43
- declare function createClient<TTypes = any>(options?: ClientOptions): TTypes;
42
+ declare function createClient<T = any>(options?: ClientOptions): T;
44
43
 
45
44
  declare class PyRPCError extends Error {
46
45
  readonly code: number;
@@ -48,4 +47,4 @@ declare class PyRPCError extends Error {
48
47
  constructor(code: number, message: string, data?: any);
49
48
  }
50
49
 
51
- export { type ClientOptions, PyRPCClient, PyRPCError, type RpcRequest, type RpcResponse, createClient };
50
+ export { type ClientOptions, PyRPCError, type RpcRequest, type RpcResponse, createClient };
package/dist/index.js CHANGED
@@ -20,7 +20,6 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
- PyRPCClient: () => PyRPCClient,
24
23
  PyRPCError: () => PyRPCError,
25
24
  createClient: () => createClient
26
25
  });
@@ -67,9 +66,6 @@ var PyRPCClient = class {
67
66
  this.url = clean.replace(/\/rpc$/i, "") + "/rpc";
68
67
  this.options = options;
69
68
  }
70
- /**
71
- * Internal method to perform the fetch request.
72
- */
73
69
  async request(method, params) {
74
70
  const id = Math.random().toString(36).substring(7);
75
71
  const body = { id, method, params };
@@ -95,41 +91,21 @@ var PyRPCClient = class {
95
91
  }
96
92
  return data.result;
97
93
  }
98
- /**
99
- * Creates a proxy that allows calling remote procedures as if they were local methods.
100
- */
101
- get rpc() {
102
- return new Proxy({}, {
103
- get: (_, method) => {
104
- return (...args) => {
105
- const params = args.length === 1 && typeof args[0] === "object" && !Array.isArray(args[0]) ? args[0] : args;
106
- return this.request(method, params);
107
- };
108
- }
109
- });
110
- }
111
94
  };
95
+ function normalizeArgs(args) {
96
+ return args.length === 1 && typeof args[0] === "object" && !Array.isArray(args[0]) ? args[0] : args;
97
+ }
112
98
  function createClient(options = {}) {
113
99
  const client = new PyRPCClient(options);
114
- return new Proxy(client, {
115
- get(target, prop, receiver) {
116
- if (prop === "rpc") {
117
- return new Proxy({}, {
118
- get(_, method) {
119
- throw new Error(
120
- `Use client.${String(method)}() instead of client.rpc.${String(method)}(). The .rpc prefix was removed for a cleaner API.`
121
- );
122
- }
123
- });
124
- }
125
- if (prop in target) return Reflect.get(target, prop, receiver);
126
- return target.rpc[prop];
100
+ return new Proxy({}, {
101
+ get(_target, prop) {
102
+ if (typeof prop !== "string") return void 0;
103
+ return (...args) => client.request(prop, normalizeArgs(args));
127
104
  }
128
105
  });
129
106
  }
130
107
  // Annotate the CommonJS export names for ESM import in node:
131
108
  0 && (module.exports = {
132
- PyRPCClient,
133
109
  PyRPCError,
134
110
  createClient
135
111
  });
package/dist/index.mjs CHANGED
@@ -39,9 +39,6 @@ var PyRPCClient = class {
39
39
  this.url = clean.replace(/\/rpc$/i, "") + "/rpc";
40
40
  this.options = options;
41
41
  }
42
- /**
43
- * Internal method to perform the fetch request.
44
- */
45
42
  async request(method, params) {
46
43
  const id = Math.random().toString(36).substring(7);
47
44
  const body = { id, method, params };
@@ -67,40 +64,20 @@ var PyRPCClient = class {
67
64
  }
68
65
  return data.result;
69
66
  }
70
- /**
71
- * Creates a proxy that allows calling remote procedures as if they were local methods.
72
- */
73
- get rpc() {
74
- return new Proxy({}, {
75
- get: (_, method) => {
76
- return (...args) => {
77
- const params = args.length === 1 && typeof args[0] === "object" && !Array.isArray(args[0]) ? args[0] : args;
78
- return this.request(method, params);
79
- };
80
- }
81
- });
82
- }
83
67
  };
68
+ function normalizeArgs(args) {
69
+ return args.length === 1 && typeof args[0] === "object" && !Array.isArray(args[0]) ? args[0] : args;
70
+ }
84
71
  function createClient(options = {}) {
85
72
  const client = new PyRPCClient(options);
86
- return new Proxy(client, {
87
- get(target, prop, receiver) {
88
- if (prop === "rpc") {
89
- return new Proxy({}, {
90
- get(_, method) {
91
- throw new Error(
92
- `Use client.${String(method)}() instead of client.rpc.${String(method)}(). The .rpc prefix was removed for a cleaner API.`
93
- );
94
- }
95
- });
96
- }
97
- if (prop in target) return Reflect.get(target, prop, receiver);
98
- return target.rpc[prop];
73
+ return new Proxy({}, {
74
+ get(_target, prop) {
75
+ if (typeof prop !== "string") return void 0;
76
+ return (...args) => client.request(prop, normalizeArgs(args));
99
77
  }
100
78
  });
101
79
  }
102
80
  export {
103
- PyRPCClient,
104
81
  PyRPCError,
105
82
  createClient
106
83
  };
package/package.json CHANGED
@@ -1,14 +1,20 @@
1
1
  {
2
2
  "name": "@pyrpc/client",
3
- "version": "0.3.3",
3
+ "version": "0.6.1",
4
4
  "description": "Universal TypeScript client for pyRPC",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
7
7
  "types": "./dist/index.d.ts",
8
+ "bin": {
9
+ "pyrpc": "./cli.js"
10
+ },
8
11
  "files": [
9
- "dist"
12
+ "dist",
13
+ "cli.js",
14
+ "postinstall.js"
10
15
  ],
11
16
  "scripts": {
17
+ "postinstall": "node postinstall.js",
12
18
  "build": "tsup src/index.ts --format cjs,esm --dts",
13
19
  "dev": "tsup src/index.ts --format cjs,esm --watch --dts",
14
20
  "lint": "eslint src/**/*.ts",
@@ -24,7 +30,10 @@
24
30
  "author": "",
25
31
  "license": "MIT",
26
32
  "dependencies": {
27
- "@pyrpc/types": "^0.3.0"
33
+ "@pyrpc/types": "^0.6.0"
34
+ },
35
+ "peerDependencies": {
36
+ "@pyrpc/types": "^0.6.0"
28
37
  },
29
38
  "devDependencies": {
30
39
  "@types/node": "^20.19.39",
package/postinstall.js ADDED
@@ -0,0 +1,183 @@
1
+ #!/usr/bin/env node
2
+ var fs = require('fs');
3
+ var path = require('path');
4
+ var readline = require('readline');
5
+ var http = require('http');
6
+ var https = require('https');
7
+
8
+ var TYPES_DIR;
9
+ try {
10
+ TYPES_DIR = path.dirname(require.resolve('@pyrpc/types/package.json'));
11
+ } catch (e) {
12
+ TYPES_DIR = path.join(__dirname, 'node_modules', '@pyrpc', 'types');
13
+ }
14
+
15
+ var TYPES_FILE = path.join(TYPES_DIR, 'src', 'index.ts');
16
+
17
+ function findProjectRoot() {
18
+ var dir = process.cwd();
19
+ while (true) {
20
+ var pkgPath = path.join(dir, 'package.json');
21
+ if (fs.existsSync(pkgPath) && !pkgPath.replace(/\\/g, '/').includes('/node_modules/')) {
22
+ return dir;
23
+ }
24
+ var parent = path.dirname(dir);
25
+ if (parent === dir) return process.cwd();
26
+ dir = parent;
27
+ }
28
+ }
29
+
30
+ function getConfigPath() {
31
+ return path.join(findProjectRoot(), 'pyrpc-client.json');
32
+ }
33
+
34
+ var TYPE_MAP = {
35
+ int: 'number',
36
+ float: 'number',
37
+ str: 'string',
38
+ bool: 'boolean',
39
+ None: 'null',
40
+ NoneType: 'null',
41
+ Any: 'any',
42
+ };
43
+
44
+ function toTs(t) {
45
+ if (!t || t === 'None') return 'void';
46
+ var m = t.match(/^<class\s+'([^'>]+)'>$/);
47
+ if (m) {
48
+ var n = m[1];
49
+ if (TYPE_MAP[n]) return TYPE_MAP[n];
50
+ if (n[0] >= 'A' && n[0] <= 'Z') return n;
51
+ return 'any';
52
+ }
53
+ var s = t.replace(/^typing\./, '');
54
+ var o = s.match(/^Optional\[(.+)\]$/);
55
+ if (o) return toTs(o[1]) + ' | null';
56
+ var l = s.match(/^(?:List|list)\[(.+)\]$/);
57
+ if (l) return toTs(l[1]) + '[]';
58
+ var d = s.match(/^(?:Dict|dict)\[([^,]+),\s*(.+)\]$/);
59
+ if (d) return 'Record<' + toTs(d[1]) + ', ' + toTs(d[2]) + '>';
60
+ var u = s.match(/^Union\[(.+)\]$/);
61
+ if (u) return u[1].split(',').map(function(p) { return toTs(p.trim()); }).join(' | ');
62
+ var tup = s.match(/^(?:Tuple|tuple)\[([^\]]+)\]$/);
63
+ if (tup) return '[' + tup[1].split(',').map(function(p) { return toTs(p.trim()); }).join(', ') + ']';
64
+ return 'any';
65
+ }
66
+
67
+ function generate(schemas) {
68
+ var lines = [];
69
+ lines.push('// Auto-generated by @pyrpc/types');
70
+ lines.push('// Schema fetched from: ' + (process.env.PYRPC_URL || 'http://localhost:8000'));
71
+ lines.push('');
72
+ var names = Object.keys(schemas);
73
+ for (var i = 0; i < names.length; i++) {
74
+ var name = names[i];
75
+ var schema = schemas[name];
76
+ lines.push('export interface ' + name + 'Params {');
77
+ var params = schema.parameters || [];
78
+ for (var j = 0; j < params.length; j++) {
79
+ var p = params[j];
80
+ lines.push(' ' + p.name + ': ' + toTs(p.type) + ';');
81
+ }
82
+ lines.push('}');
83
+ lines.push('');
84
+ lines.push('export interface ' + name + 'Result {');
85
+ lines.push(' data: ' + toTs(schema.return_type) + ';');
86
+ lines.push('}');
87
+ lines.push('');
88
+ }
89
+ lines.push('export interface Types {');
90
+ for (var k = 0; k < names.length; k++) {
91
+ lines.push(' ' + names[k] + ': { params: ' + names[k] + 'Params; result: ' + names[k] + 'Result };');
92
+ }
93
+ lines.push('}');
94
+ lines.push('');
95
+ return lines.join('\n');
96
+ }
97
+
98
+ function fetchSchema(url) {
99
+ var endpoint = url.replace(/\/+$/, '');
100
+ if (endpoint.indexOf('/rpc') !== endpoint.length - 4) {
101
+ endpoint += '/rpc';
102
+ }
103
+ var mod = endpoint.indexOf('https') === 0 ? https : http;
104
+ return new Promise(function(resolve, reject) {
105
+ mod.get(endpoint, function(res) {
106
+ var data = '';
107
+ res.on('data', function(c) { data += c; });
108
+ res.on('end', function() {
109
+ if (res.statusCode !== 200) return reject(new Error('HTTP ' + res.statusCode));
110
+ try { resolve(JSON.parse(data)); }
111
+ catch (e) { reject(e); }
112
+ });
113
+ }).on('error', reject);
114
+ });
115
+ }
116
+
117
+ function main() {
118
+ if (fs.existsSync(getConfigPath())) {
119
+ return Promise.resolve();
120
+ }
121
+
122
+ var isInteractive = process.stdin.isTTY;
123
+ var isCI = process.env.CI;
124
+
125
+ if (!isInteractive || isCI) {
126
+ return Promise.resolve();
127
+ }
128
+
129
+ console.log('');
130
+ console.log(' \u2728 Welcome to pyRPC!');
131
+ console.log(' Let\'s set up your client configuration.');
132
+ console.log('');
133
+
134
+ var rl = readline.createInterface({ input: process.stdin, output: process.stdout });
135
+ return new Promise(function(resolve) {
136
+ rl.question('How are types distributed to the client?\n 1) workspace (default) - server writes types directly to your project\n 2) server - client fetches types via HTTP\nEnter choice [1]: ', function(answer) {
137
+ rl.close();
138
+ resolve(answer.trim() === '2' ? 'server' : 'workspace');
139
+ });
140
+ }).then(function(distribution) {
141
+ if (distribution === 'workspace') {
142
+ var config = { distribution: 'workspace' };
143
+ fs.writeFileSync(getConfigPath(), JSON.stringify(config, null, 2) + '\n');
144
+ console.log(' \u2713 pyrpc-client.json created (workspace mode)');
145
+ console.log(' Your server\'s pyrpc dev command will write types directly.');
146
+ console.log(' Import: import { createClient, type Types } from "@pyrpc/client"');
147
+ return;
148
+ }
149
+
150
+ var rl2 = readline.createInterface({ input: process.stdin, output: process.stdout });
151
+ return new Promise(function(resolve) {
152
+ rl2.question('pyRPC backend URL (default: http://localhost:8000): ', function(answer) {
153
+ rl2.close();
154
+ resolve(answer.trim() || 'http://localhost:8000');
155
+ });
156
+ }).then(function(url) {
157
+ return fetchSchema(url).then(function(schemas) {
158
+ var code = generate(schemas);
159
+ fs.mkdirSync(path.dirname(TYPES_FILE), { recursive: true });
160
+ fs.writeFileSync(TYPES_FILE, code, 'utf-8');
161
+ console.log(' \u2713 @pyrpc/types: generated for ' + Object.keys(schemas).length + ' procedures');
162
+
163
+ var config = { distribution: 'server', server_url: url };
164
+ fs.writeFileSync(getConfigPath(), JSON.stringify(config, null, 2) + '\n');
165
+ console.log(' \u2713 pyrpc-client.json created (server mode)');
166
+ console.log(' Import: import { createClient, type Types } from "@pyrpc/client"');
167
+ }).catch(function(err) {
168
+ console.log(' \u2717 @pyrpc/types: could not connect (' + err.message + ')');
169
+ console.log(' Run later: npx pyrpc sync');
170
+
171
+ var config = { distribution: 'server', server_url: url };
172
+ fs.writeFileSync(getConfigPath(), JSON.stringify(config, null, 2) + '\n');
173
+ console.log(' \u2713 pyrpc-client.json created (server mode)');
174
+ });
175
+ });
176
+ });
177
+ }
178
+
179
+ if (require.main === module) {
180
+ main().catch(function(e) {});
181
+ }
182
+
183
+ module.exports = { main, toTs, generate, fetchSchema, findProjectRoot, getConfigPath, TYPES_FILE };