ephaptic 0.1.3__tar.gz → 0.2.0__tar.gz

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.
Files changed (25) hide show
  1. {ephaptic-0.1.3 → ephaptic-0.2.0}/PKG-INFO +34 -9
  2. {ephaptic-0.1.3 → ephaptic-0.2.0}/README.md +30 -8
  3. ephaptic-0.2.0/ephaptic/cli/__init__.py +3 -0
  4. ephaptic-0.2.0/ephaptic/cli/__main__.py +106 -0
  5. {ephaptic-0.1.3 → ephaptic-0.2.0}/ephaptic/ephaptic.py +34 -3
  6. {ephaptic-0.1.3 → ephaptic-0.2.0}/ephaptic.egg-info/PKG-INFO +34 -9
  7. {ephaptic-0.1.3 → ephaptic-0.2.0}/ephaptic.egg-info/SOURCES.txt +3 -0
  8. ephaptic-0.2.0/ephaptic.egg-info/entry_points.txt +2 -0
  9. ephaptic-0.2.0/ephaptic.egg-info/requires.txt +8 -0
  10. {ephaptic-0.1.3 → ephaptic-0.2.0}/pyproject.toml +8 -2
  11. ephaptic-0.1.3/ephaptic.egg-info/requires.txt +0 -5
  12. {ephaptic-0.1.3 → ephaptic-0.2.0}/LICENSE +0 -0
  13. {ephaptic-0.1.3 → ephaptic-0.2.0}/ephaptic/__init__.py +0 -0
  14. {ephaptic-0.1.3 → ephaptic-0.2.0}/ephaptic/adapters/__init__.py +0 -0
  15. {ephaptic-0.1.3 → ephaptic-0.2.0}/ephaptic/adapters/fastapi_.py +0 -0
  16. {ephaptic-0.1.3 → ephaptic-0.2.0}/ephaptic/adapters/quart_.py +0 -0
  17. {ephaptic-0.1.3 → ephaptic-0.2.0}/ephaptic/client/__init__.py +0 -0
  18. {ephaptic-0.1.3 → ephaptic-0.2.0}/ephaptic/client/client.py +0 -0
  19. {ephaptic-0.1.3 → ephaptic-0.2.0}/ephaptic/localproxy.py +0 -0
  20. {ephaptic-0.1.3 → ephaptic-0.2.0}/ephaptic/transports/__init__.py +0 -0
  21. {ephaptic-0.1.3 → ephaptic-0.2.0}/ephaptic/transports/fastapi_ws.py +0 -0
  22. {ephaptic-0.1.3 → ephaptic-0.2.0}/ephaptic/transports/websocket.py +0 -0
  23. {ephaptic-0.1.3 → ephaptic-0.2.0}/ephaptic.egg-info/dependency_links.txt +0 -0
  24. {ephaptic-0.1.3 → ephaptic-0.2.0}/ephaptic.egg-info/top_level.txt +0 -0
  25. {ephaptic-0.1.3 → ephaptic-0.2.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ephaptic
3
- Version: 0.1.3
3
+ Version: 0.2.0
4
4
  Summary: The Python client/server package for ephaptic.
5
5
  Author-email: uukelele <robustrobot11@gmail.com>
6
6
  License: MIT License
@@ -33,6 +33,9 @@ Description-Content-Type: text/markdown
33
33
  License-File: LICENSE
34
34
  Requires-Dist: msgpack>=1.0.0
35
35
  Requires-Dist: websockets>=12.0
36
+ Requires-Dist: pydantic>=2.0
37
+ Requires-Dist: typer[standard]>=0.20.0
38
+ Requires-Dist: python-dotenv
36
39
  Provides-Extra: server
37
40
  Requires-Dist: redis; extra == "server"
38
41
  Dynamic: license-file
@@ -58,8 +61,6 @@ Dynamic: license-file
58
61
  </a>
59
62
 
60
63
 
61
-
62
-
63
64
  </div>
64
65
 
65
66
  ## What is `ephaptic`?
@@ -73,7 +74,7 @@ Dynamic: license-file
73
74
 
74
75
  Nah, just kidding. It's an RPC framework.
75
76
 
76
- > **ephaptic** — Call your backend straight from your frontend. No JSON. No latency. No middleware.
77
+ > **ephaptic** — Call your backend straight from your frontend. No JSON. Low latency. Invisible middleware.
77
78
 
78
79
  ## Getting Started
79
80
 
@@ -83,7 +84,9 @@ Nah, just kidding. It's an RPC framework.
83
84
 
84
85
  - Oh, and the client can also listen to events broadcasted by the server. No, like literally. You just need to add an `eventListener`. Did I mention? Events can be sent to specific targets, specific users - not just anyone online.
85
86
 
86
- What are you waiting for? **Let's go.**
87
+ - Saved the best for last: it's type-safe. Don't believe me? Try it out for yourself. Simply type hint return values and parameters on the backend, and watch those very Python types transform into interfaces and types on the TypeScript frontend. Plus, you can use Pydantic - which means, for those of you who are FastAPI users, this is going to be great.
88
+
89
+ What are you waiting for? **Let's go.**
87
90
 
88
91
  <details>
89
92
  <summary>Python</summary>
@@ -125,13 +128,13 @@ Now, how do you expose your function to the frontend?
125
128
 
126
129
  ```python
127
130
  @ephaptic.expose
128
- async def add(num1, num2):
131
+ async def add(num1: int, num2: int) -> int:
129
132
  return num1 + num2
130
133
  ```
131
134
 
132
135
  Yep, it's really that simple.
133
136
 
134
- But what if your code throws an error? No sweat, it just throws up on the frontend with the same details.
137
+ But what if your code throws an error? No sweat, it just throws up on the frontend, with the error name.
135
138
 
136
139
  And, want to say something to the frontend?
137
140
 
@@ -139,6 +142,13 @@ And, want to say something to the frontend?
139
142
  await ephaptic.to(user1, user2).notification("Hello, world!", priority="high")
140
143
  ```
141
144
 
145
+ To create a schema of your RPC endpoints:
146
+
147
+ ```
148
+ $ ephaptic src.app:app -o schema.json
149
+ ```
150
+
151
+ Pydantic is entirely supported. It's validated for arguments, it's auto-serialized when you return a pydantic model, and your models receive type definitions in the schema.
142
152
 
143
153
  </details>
144
154
 
@@ -148,7 +158,7 @@ await ephaptic.to(user1, user2).notification("Hello, world!", priority="high")
148
158
  #### To use with a framework / Vite:
149
159
 
150
160
  ```
151
- npm install @ephaptic/client
161
+ $ npm install @ephaptic/client
152
162
  ```
153
163
 
154
164
  Then:
@@ -175,13 +185,28 @@ You can even send auth objects to the server for identity loading.
175
185
  const client = connect({ url: '...', auth: { token: window.localStorage.getItem('jwtToken') } })
176
186
  ```
177
187
 
188
+ And you can load types, too.
189
+
190
+ ```
191
+ $ npm i --save-dev @ephaptic/type-gen
192
+ $ npx @ephaptic/type-gen ./schema.json -o schema.d.ts
193
+ ```
194
+
195
+ ```typescript
196
+ import { connect } from "@ephaptic/client";
197
+ import { type EphapticService } from './schema';
198
+
199
+ const client = connect(...) as unknown as EphapticService;
200
+ ```
201
+
202
+
178
203
  #### Or, to use in your browser:
179
204
 
180
205
  ```html
181
206
  <script type="module">
182
207
  import { connect } from 'https://cdn.jsdelivr.net/npm/@ephaptic/client@latest/+esm';
183
208
 
184
- const client = connect();
209
+ const client = connect(...);
185
210
  </script>
186
211
  ```
187
212
 
@@ -19,8 +19,6 @@
19
19
  </a>
20
20
 
21
21
 
22
-
23
-
24
22
  </div>
25
23
 
26
24
  ## What is `ephaptic`?
@@ -34,7 +32,7 @@
34
32
 
35
33
  Nah, just kidding. It's an RPC framework.
36
34
 
37
- > **ephaptic** — Call your backend straight from your frontend. No JSON. No latency. No middleware.
35
+ > **ephaptic** — Call your backend straight from your frontend. No JSON. Low latency. Invisible middleware.
38
36
 
39
37
  ## Getting Started
40
38
 
@@ -44,7 +42,9 @@ Nah, just kidding. It's an RPC framework.
44
42
 
45
43
  - Oh, and the client can also listen to events broadcasted by the server. No, like literally. You just need to add an `eventListener`. Did I mention? Events can be sent to specific targets, specific users - not just anyone online.
46
44
 
47
- What are you waiting for? **Let's go.**
45
+ - Saved the best for last: it's type-safe. Don't believe me? Try it out for yourself. Simply type hint return values and parameters on the backend, and watch those very Python types transform into interfaces and types on the TypeScript frontend. Plus, you can use Pydantic - which means, for those of you who are FastAPI users, this is going to be great.
46
+
47
+ What are you waiting for? **Let's go.**
48
48
 
49
49
  <details>
50
50
  <summary>Python</summary>
@@ -86,13 +86,13 @@ Now, how do you expose your function to the frontend?
86
86
 
87
87
  ```python
88
88
  @ephaptic.expose
89
- async def add(num1, num2):
89
+ async def add(num1: int, num2: int) -> int:
90
90
  return num1 + num2
91
91
  ```
92
92
 
93
93
  Yep, it's really that simple.
94
94
 
95
- But what if your code throws an error? No sweat, it just throws up on the frontend with the same details.
95
+ But what if your code throws an error? No sweat, it just throws up on the frontend, with the error name.
96
96
 
97
97
  And, want to say something to the frontend?
98
98
 
@@ -100,6 +100,13 @@ And, want to say something to the frontend?
100
100
  await ephaptic.to(user1, user2).notification("Hello, world!", priority="high")
101
101
  ```
102
102
 
103
+ To create a schema of your RPC endpoints:
104
+
105
+ ```
106
+ $ ephaptic src.app:app -o schema.json
107
+ ```
108
+
109
+ Pydantic is entirely supported. It's validated for arguments, it's auto-serialized when you return a pydantic model, and your models receive type definitions in the schema.
103
110
 
104
111
  </details>
105
112
 
@@ -109,7 +116,7 @@ await ephaptic.to(user1, user2).notification("Hello, world!", priority="high")
109
116
  #### To use with a framework / Vite:
110
117
 
111
118
  ```
112
- npm install @ephaptic/client
119
+ $ npm install @ephaptic/client
113
120
  ```
114
121
 
115
122
  Then:
@@ -136,13 +143,28 @@ You can even send auth objects to the server for identity loading.
136
143
  const client = connect({ url: '...', auth: { token: window.localStorage.getItem('jwtToken') } })
137
144
  ```
138
145
 
146
+ And you can load types, too.
147
+
148
+ ```
149
+ $ npm i --save-dev @ephaptic/type-gen
150
+ $ npx @ephaptic/type-gen ./schema.json -o schema.d.ts
151
+ ```
152
+
153
+ ```typescript
154
+ import { connect } from "@ephaptic/client";
155
+ import { type EphapticService } from './schema';
156
+
157
+ const client = connect(...) as unknown as EphapticService;
158
+ ```
159
+
160
+
139
161
  #### Or, to use in your browser:
140
162
 
141
163
  ```html
142
164
  <script type="module">
143
165
  import { connect } from 'https://cdn.jsdelivr.net/npm/@ephaptic/client@latest/+esm';
144
166
 
145
- const client = connect();
167
+ const client = connect(...);
146
168
  </script>
147
169
  ```
148
170
 
@@ -0,0 +1,3 @@
1
+ from .__main__ import app
2
+
3
+ __all__ = ["app"]
@@ -0,0 +1,106 @@
1
+ import sys, os, json, inspect, importlib, typing, typer
2
+
3
+ from pathlib import Path
4
+ from pydantic import TypeAdapter
5
+ from pydantic.json_schema import models_json_schema
6
+
7
+ from ephaptic import Ephaptic
8
+
9
+ app = typer.Typer(help="Ephaptic CLI tool.")
10
+
11
+ def load_ephaptic(import_name: str) -> Ephaptic:
12
+ try:
13
+ from dotenv import load_dotenv; load_dotenv()
14
+ except: ...
15
+
16
+ sys.path.insert(0, os.getcwd())
17
+
18
+ if ":" not in import_name:
19
+ typer.secho(f"Warning: Import name did not specify app name. Defaulting to `app`.", fg=typer.colors.YELLOW)
20
+ import_name += ":app" # default: expect app to be named `app` inside the file
21
+
22
+ module_name, var_name = import_name.split(":", 1)
23
+
24
+ try:
25
+ typer.secho(f"Attempting to import `{var_name}` from `{module_name}`...")
26
+ module = importlib.import_module(module_name)
27
+ except ImportError as e:
28
+ typer.secho(f"Error: Can't import '{module_name}'.\n{e}", fg=typer.colors.RED)
29
+ raise typer.Exit(1)
30
+
31
+ try:
32
+ instance = getattr(module, var_name)
33
+ except AttributeError:
34
+ typer.secho(f"Error: Variable '{var_name}' not found in module '{module_name}'.", fg=typer.colors.RED)
35
+ raise typer.Exit(1)
36
+
37
+ if not isinstance(instance, Ephaptic):
38
+ typer.secho(f"Error: '{var_name}' is not an Ephaptic instance. It is type: {type(instance)}", fg=typer.colors.RED)
39
+ raise typer.Exit(1)
40
+
41
+ return instance
42
+
43
+ def create_schema(adapter: TypeAdapter, definitions: dict) -> dict:
44
+ schema = adapter.json_schema(ref_template='#/definitions/{model}')
45
+
46
+ if '$defs' in schema:
47
+ definitions.update(schema.pop('$defs'))
48
+
49
+ if schema.get('type') == 'object' and 'title' in schema:
50
+ model = schema['title']
51
+ definitions[model] = schema
52
+ return { '$ref': f'#/definitions/{model}' }
53
+
54
+ return schema
55
+
56
+ @app.command()
57
+ def generate(
58
+ app: str = typer.Argument('app:app', help="The import string. (Default: `app:app`)"),
59
+ output: Path = typer.Option('schema.json', '--output', '-o', help="Output path for the JSON schema.")
60
+ ):
61
+ ephaptic = load_ephaptic(app)
62
+
63
+ typer.secho(f"Found {len(ephaptic._exposed_functions)} functions.", fg=typer.colors.GREEN)
64
+
65
+ schema_output = {
66
+ "methods": {},
67
+ "definitions": {},
68
+ }
69
+
70
+ for name, func in ephaptic._exposed_functions.items():
71
+ typer.secho(f" - {name}")
72
+
73
+ hints = typing.get_type_hints(func)
74
+ sig = inspect.signature(func)
75
+
76
+ method_schema = {
77
+ "args": {},
78
+ "return": None
79
+ }
80
+
81
+ for param_name in sig.parameters:
82
+ hint = hints.get(param_name, typing.Any)
83
+ adapter = TypeAdapter(hint)
84
+
85
+ method_schema["args"][param_name] = create_schema(
86
+ adapter,
87
+ schema_output["definitions"],
88
+ )
89
+
90
+ return_hint = hints.get("return", typing.Any)
91
+ if return_hint is not type(None):
92
+ adapter = TypeAdapter(return_hint)
93
+ method_schema["return"] = create_schema(
94
+ adapter,
95
+ schema_output["definitions"],
96
+ )
97
+
98
+ schema_output["methods"][name] = method_schema
99
+
100
+ with open(output, "w") as f:
101
+ json.dump(schema_output, f, indent=2)
102
+
103
+ typer.secho(f"Schema generated to `{output}`.", fg=typer.colors.GREEN, bold=True)
104
+
105
+ if __name__ == "__main__":
106
+ app()
@@ -1,12 +1,14 @@
1
1
  import asyncio
2
2
  import msgpack
3
3
  import redis.asyncio as redis
4
+ import pydantic
4
5
 
5
6
  from contextvars import ContextVar
6
7
  from .localproxy import LocalProxy
7
8
 
8
9
  from .transports import Transport
9
10
 
11
+ import typing
10
12
  from typing import Optional, Callable, Any, List, Set, Dict
11
13
  import inspect
12
14
 
@@ -91,12 +93,11 @@ class Ephaptic:
91
93
 
92
94
  @classmethod
93
95
  def from_app(cls, app, path="/_ephaptic", redis_url=None):
94
- # `app` could be Flask, Quart, FastAPI, etc.
96
+ # `app` could be ~Flask~, Quart, FastAPI, etc.
95
97
  instance = cls()
96
98
 
97
99
  if redis_url:
98
100
  manager.init_redis(redis_url)
99
- # TODO: framework-specific hooks for the background listener.
100
101
 
101
102
  module = app.__class__.__module__.split(".")[0]
102
103
 
@@ -177,11 +178,41 @@ class Ephaptic:
177
178
 
178
179
  if func_name in self._exposed_functions:
179
180
  target_func = self._exposed_functions[func_name]
181
+ sig = inspect.signature(target_func)
182
+ try:
183
+ bound = sig.bind(*args, **kwargs)
184
+ bound.apply_defaults()
185
+ except TypeError as e:
186
+ await transport.send(msgpack.dumps({"id": call_id, "error": str(e)}))
187
+ continue
188
+
189
+ hints = typing.get_type_hints(target_func)
190
+
191
+ errors = []
192
+
193
+ for name, val in bound.arguments.items():
194
+ hint = hints.get(name)
195
+
196
+ if hint and inspect.isclass(hint) and issubclass(hint, pydantic.BaseModel):
197
+ try:
198
+ bound.arguments[name] = hint.model_validate(val)
199
+ except pydantic.ValidationError as e:
200
+ errors.extend(e.errors())
201
+
202
+ if errors:
203
+ await transport.send(msgpack.dumps({
204
+ "id": call_id,
205
+ "error": errors,
206
+ }))
207
+ continue
208
+
180
209
  token_transport = _active_transport_ctx.set(transport)
181
210
  token_user = _active_user_ctx.set(current_uid)
182
211
 
183
212
  try:
184
- result = await self._async(target_func)(*args, **kwargs)
213
+ result = await self._async(target_func)(**bound.arguments)
214
+ if isinstance(result, pydantic.BaseModel):
215
+ result = result.model_dump()
185
216
  await transport.send(msgpack.dumps({"id": call_id, "result": result}))
186
217
  except Exception as e:
187
218
  await transport.send(msgpack.dumps({"id": call_id, "error": str(e)}))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ephaptic
3
- Version: 0.1.3
3
+ Version: 0.2.0
4
4
  Summary: The Python client/server package for ephaptic.
5
5
  Author-email: uukelele <robustrobot11@gmail.com>
6
6
  License: MIT License
@@ -33,6 +33,9 @@ Description-Content-Type: text/markdown
33
33
  License-File: LICENSE
34
34
  Requires-Dist: msgpack>=1.0.0
35
35
  Requires-Dist: websockets>=12.0
36
+ Requires-Dist: pydantic>=2.0
37
+ Requires-Dist: typer[standard]>=0.20.0
38
+ Requires-Dist: python-dotenv
36
39
  Provides-Extra: server
37
40
  Requires-Dist: redis; extra == "server"
38
41
  Dynamic: license-file
@@ -58,8 +61,6 @@ Dynamic: license-file
58
61
  </a>
59
62
 
60
63
 
61
-
62
-
63
64
  </div>
64
65
 
65
66
  ## What is `ephaptic`?
@@ -73,7 +74,7 @@ Dynamic: license-file
73
74
 
74
75
  Nah, just kidding. It's an RPC framework.
75
76
 
76
- > **ephaptic** — Call your backend straight from your frontend. No JSON. No latency. No middleware.
77
+ > **ephaptic** — Call your backend straight from your frontend. No JSON. Low latency. Invisible middleware.
77
78
 
78
79
  ## Getting Started
79
80
 
@@ -83,7 +84,9 @@ Nah, just kidding. It's an RPC framework.
83
84
 
84
85
  - Oh, and the client can also listen to events broadcasted by the server. No, like literally. You just need to add an `eventListener`. Did I mention? Events can be sent to specific targets, specific users - not just anyone online.
85
86
 
86
- What are you waiting for? **Let's go.**
87
+ - Saved the best for last: it's type-safe. Don't believe me? Try it out for yourself. Simply type hint return values and parameters on the backend, and watch those very Python types transform into interfaces and types on the TypeScript frontend. Plus, you can use Pydantic - which means, for those of you who are FastAPI users, this is going to be great.
88
+
89
+ What are you waiting for? **Let's go.**
87
90
 
88
91
  <details>
89
92
  <summary>Python</summary>
@@ -125,13 +128,13 @@ Now, how do you expose your function to the frontend?
125
128
 
126
129
  ```python
127
130
  @ephaptic.expose
128
- async def add(num1, num2):
131
+ async def add(num1: int, num2: int) -> int:
129
132
  return num1 + num2
130
133
  ```
131
134
 
132
135
  Yep, it's really that simple.
133
136
 
134
- But what if your code throws an error? No sweat, it just throws up on the frontend with the same details.
137
+ But what if your code throws an error? No sweat, it just throws up on the frontend, with the error name.
135
138
 
136
139
  And, want to say something to the frontend?
137
140
 
@@ -139,6 +142,13 @@ And, want to say something to the frontend?
139
142
  await ephaptic.to(user1, user2).notification("Hello, world!", priority="high")
140
143
  ```
141
144
 
145
+ To create a schema of your RPC endpoints:
146
+
147
+ ```
148
+ $ ephaptic src.app:app -o schema.json
149
+ ```
150
+
151
+ Pydantic is entirely supported. It's validated for arguments, it's auto-serialized when you return a pydantic model, and your models receive type definitions in the schema.
142
152
 
143
153
  </details>
144
154
 
@@ -148,7 +158,7 @@ await ephaptic.to(user1, user2).notification("Hello, world!", priority="high")
148
158
  #### To use with a framework / Vite:
149
159
 
150
160
  ```
151
- npm install @ephaptic/client
161
+ $ npm install @ephaptic/client
152
162
  ```
153
163
 
154
164
  Then:
@@ -175,13 +185,28 @@ You can even send auth objects to the server for identity loading.
175
185
  const client = connect({ url: '...', auth: { token: window.localStorage.getItem('jwtToken') } })
176
186
  ```
177
187
 
188
+ And you can load types, too.
189
+
190
+ ```
191
+ $ npm i --save-dev @ephaptic/type-gen
192
+ $ npx @ephaptic/type-gen ./schema.json -o schema.d.ts
193
+ ```
194
+
195
+ ```typescript
196
+ import { connect } from "@ephaptic/client";
197
+ import { type EphapticService } from './schema';
198
+
199
+ const client = connect(...) as unknown as EphapticService;
200
+ ```
201
+
202
+
178
203
  #### Or, to use in your browser:
179
204
 
180
205
  ```html
181
206
  <script type="module">
182
207
  import { connect } from 'https://cdn.jsdelivr.net/npm/@ephaptic/client@latest/+esm';
183
208
 
184
- const client = connect();
209
+ const client = connect(...);
185
210
  </script>
186
211
  ```
187
212
 
@@ -7,11 +7,14 @@ ephaptic/localproxy.py
7
7
  ephaptic.egg-info/PKG-INFO
8
8
  ephaptic.egg-info/SOURCES.txt
9
9
  ephaptic.egg-info/dependency_links.txt
10
+ ephaptic.egg-info/entry_points.txt
10
11
  ephaptic.egg-info/requires.txt
11
12
  ephaptic.egg-info/top_level.txt
12
13
  ephaptic/adapters/__init__.py
13
14
  ephaptic/adapters/fastapi_.py
14
15
  ephaptic/adapters/quart_.py
16
+ ephaptic/cli/__init__.py
17
+ ephaptic/cli/__main__.py
15
18
  ephaptic/client/__init__.py
16
19
  ephaptic/client/client.py
17
20
  ephaptic/transports/__init__.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ ephaptic = ephaptic.cli:app
@@ -0,0 +1,8 @@
1
+ msgpack>=1.0.0
2
+ websockets>=12.0
3
+ pydantic>=2.0
4
+ typer[standard]>=0.20.0
5
+ python-dotenv
6
+
7
+ [server]
8
+ redis
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ephaptic"
7
- version = "0.1.3"
7
+ version = "0.2.0"
8
8
  authors = [
9
9
  { name="uukelele", email="robustrobot11@gmail.com" },
10
10
  ]
@@ -16,6 +16,9 @@ requires-python = ">=3.10"
16
16
  dependencies = [
17
17
  "msgpack>=1.0.0",
18
18
  "websockets>=12.0",
19
+ "pydantic>=2.0",
20
+ "typer[standard]>=0.20.0",
21
+ "python-dotenv",
19
22
  ]
20
23
 
21
24
  [project.optional-dependencies]
@@ -24,4 +27,7 @@ server = ["redis"]
24
27
  [project.urls]
25
28
  Homepage = "https://github.com/ephaptic/ephaptic"
26
29
  Repository = "https://github.com/ephaptic/ephaptic"
27
- "Issue Tracker" = "https://github.com/ephaptic/ephaptic/issues"
30
+ "Issue Tracker" = "https://github.com/ephaptic/ephaptic/issues"
31
+
32
+ [project.scripts]
33
+ ephaptic = "ephaptic.cli:app"
@@ -1,5 +0,0 @@
1
- msgpack>=1.0.0
2
- websockets>=12.0
3
-
4
- [server]
5
- redis
File without changes
File without changes
File without changes