@covenant-rpc/react 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,8 @@
1
+ import type { AsyncHook } from ".";
2
+ export type CallbackFunction<T> = () => T;
3
+ export declare function useDataStream<T>({ initialFetch, connect, disconnect }: {
4
+ initialFetch: () => Promise<T[]>;
5
+ connect: (callback: CallbackFunction<T>) => Promise<void>;
6
+ disconnect: () => void;
7
+ }): AsyncHook<T[]>;
8
+ //# sourceMappingURL=helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,GAAG,CAAC;AAkBnC,MAAM,MAAM,gBAAgB,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC;AAY1C,wBAAgB,aAAa,CAAC,CAAC,EAAE,EAAE,YAAY,EAAE,OAAO,EAAE,UAAU,EAAE,EAAE;IACtE,YAAY,EAAE,MAAM,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;IACjC,OAAO,EAAE,CAAC,QAAQ,EAAE,gBAAgB,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,UAAU,EAAE,MAAM,IAAI,CAAA;CACvB,GAAG,SAAS,CAAC,CAAC,EAAE,CAAC,CAsEjB"}
@@ -0,0 +1,73 @@
1
+ import { useEffect, useState } from "react";
2
+ // useDataStream is best described by an example. Let's say we have a chatFunction which has two methods:
3
+ //
4
+ // getCurrentMessages: () => Promise<Message[]>
5
+ // connectToUpdates: (onUpdate: () => Message): Promise<void>
6
+ // disconnect: () => void
7
+ //
8
+ // We want to fetch all messages as well as continue to listen to them. useDataStream should handle this
9
+ // as well as various race conditions
10
+ export function useDataStream({ initialFetch, connect, disconnect }) {
11
+ const [state, setState] = useState({
12
+ loading: true,
13
+ data: null,
14
+ error: null
15
+ });
16
+ useEffect(() => {
17
+ let isMounted = true;
18
+ let items = [];
19
+ let isConnecting = false;
20
+ const handleNewItem = (newItem) => {
21
+ if (!isMounted)
22
+ return;
23
+ // Add new item to the list
24
+ items = [...items, newItem];
25
+ setState({
26
+ loading: false,
27
+ data: items,
28
+ error: null
29
+ });
30
+ };
31
+ const initialize = async () => {
32
+ try {
33
+ // Fetch initial data first
34
+ const initialData = await initialFetch();
35
+ if (!isMounted)
36
+ return;
37
+ items = initialData;
38
+ setState({
39
+ loading: false,
40
+ data: items,
41
+ error: null
42
+ });
43
+ // Connect to updates
44
+ // Note: The type signature indicates CallbackFunction<T> = () => T,
45
+ // but the practical use case requires (newItem: T) => void
46
+ // Using type assertion to work with the expected runtime behavior
47
+ isConnecting = true;
48
+ await connect(handleNewItem);
49
+ }
50
+ catch (error) {
51
+ if (!isMounted)
52
+ return;
53
+ setState({
54
+ loading: false,
55
+ data: null,
56
+ error: {
57
+ code: 500,
58
+ message: error instanceof Error ? error.message : 'Unknown error'
59
+ }
60
+ });
61
+ }
62
+ };
63
+ initialize();
64
+ return () => {
65
+ isMounted = false;
66
+ if (isConnecting) {
67
+ disconnect();
68
+ }
69
+ };
70
+ }, [initialFetch, connect, disconnect]);
71
+ return state;
72
+ }
73
+ //# sourceMappingURL=helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.js","sourceRoot":"","sources":["../helpers.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAoB5C,yGAAyG;AACzG,EAAE;AACF,+CAA+C;AAC/C,8DAA8D;AAC9D,yBAAyB;AACzB,EAAE;AACF,wGAAwG;AACxG,qCAAqC;AAErC,MAAM,UAAU,aAAa,CAAI,EAAE,YAAY,EAAE,OAAO,EAAE,UAAU,EAInE;IACC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAiB;QACjD,OAAO,EAAE,IAAI;QACb,IAAI,EAAE,IAAI;QACV,KAAK,EAAE,IAAI;KACZ,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,SAAS,GAAG,IAAI,CAAC;QACrB,IAAI,KAAK,GAAQ,EAAE,CAAC;QACpB,IAAI,YAAY,GAAG,KAAK,CAAC;QAEzB,MAAM,aAAa,GAAG,CAAC,OAAU,EAAE,EAAE;YACnC,IAAI,CAAC,SAAS;gBAAE,OAAO;YAEvB,2BAA2B;YAC3B,KAAK,GAAG,CAAC,GAAG,KAAK,EAAE,OAAO,CAAC,CAAC;YAC5B,QAAQ,CAAC;gBACP,OAAO,EAAE,KAAK;gBACd,IAAI,EAAE,KAAK;gBACX,KAAK,EAAE,IAAI;aACZ,CAAC,CAAC;QACL,CAAC,CAAC;QAEF,MAAM,UAAU,GAAG,KAAK,IAAI,EAAE;YAC5B,IAAI,CAAC;gBACH,2BAA2B;gBAC3B,MAAM,WAAW,GAAG,MAAM,YAAY,EAAE,CAAC;gBAEzC,IAAI,CAAC,SAAS;oBAAE,OAAO;gBAEvB,KAAK,GAAG,WAAW,CAAC;gBACpB,QAAQ,CAAC;oBACP,OAAO,EAAE,KAAK;oBACd,IAAI,EAAE,KAAK;oBACX,KAAK,EAAE,IAAI;iBACZ,CAAC,CAAC;gBAEH,qBAAqB;gBACrB,oEAAoE;gBACpE,2DAA2D;gBAC3D,kEAAkE;gBAClE,YAAY,GAAG,IAAI,CAAC;gBACpB,MAAM,OAAO,CAAC,aAAoB,CAAC,CAAC;YAEtC,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC,SAAS;oBAAE,OAAO;gBAEvB,QAAQ,CAAC;oBACP,OAAO,EAAE,KAAK;oBACd,IAAI,EAAE,IAAI;oBACV,KAAK,EAAE;wBACL,IAAI,EAAE,GAAG;wBACT,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe;qBAClE;iBACF,CAAC,CAAC;YACL,CAAC;QACH,CAAC,CAAC;QAEF,UAAU,EAAE,CAAC;QAEb,OAAO,GAAG,EAAE;YACV,SAAS,GAAG,KAAK,CAAC;YAClB,IAAI,YAAY,EAAE,CAAC;gBACjB,UAAU,EAAE,CAAC;YACf,CAAC;QACH,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,YAAY,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC;IAExC,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,33 @@
1
+ import type { ProcedureMap, ChannelMap } from "@covenant-rpc/core";
2
+ import { CovenantClient, type MutationKey, type QueryKey } from "@covenant-rpc/client";
3
+ import type { InferProcedureInputs, InferProcedureOutputs } from "@covenant-rpc/core/procedure";
4
+ export interface ReactProcedureError {
5
+ code: number;
6
+ message: string;
7
+ }
8
+ export type AsyncHook<T> = {
9
+ loading: true;
10
+ data: null;
11
+ error: null;
12
+ } | {
13
+ loading: false;
14
+ data: T;
15
+ error: null;
16
+ } | {
17
+ loading: false;
18
+ data: null;
19
+ error: ReactProcedureError;
20
+ };
21
+ export declare class CovenantReactClient<P extends ProcedureMap, C extends ChannelMap> extends CovenantClient<P, C> {
22
+ private cache;
23
+ private listenes;
24
+ useQuery<Q extends QueryKey<P>>(procedureName: Q, inputs: InferProcedureInputs<P[Q]>): AsyncHook<InferProcedureOutputs<P[Q]>>;
25
+ useMutation<Q extends MutationKey<P>>(procedureName: Q, inputs: InferProcedureInputs<P[Q]>): AsyncHook<InferProcedureOutputs<P[Q]>>;
26
+ useListenedQuery<Q extends QueryKey<P>>(procedureName: Q, inputs: InferProcedureInputs<P[Q]>): AsyncHook<InferProcedureOutputs<P[Q]>>;
27
+ private createCachedQuery;
28
+ private getCacheKey;
29
+ private addCacheListener;
30
+ private removeCacheListener;
31
+ useCachedQuery<Q extends QueryKey<P>>(procedureName: Q, inputs: InferProcedureInputs<P[Q]>): AsyncHook<InferProcedureOutputs<P[Q]>>;
32
+ }
33
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,KAAK,WAAW,EAAE,KAAK,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACvF,OAAO,KAAK,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAIhG,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,SAAS,CAAC,CAAC,IAAI;IACzB,OAAO,EAAE,IAAI,CAAC;IACd,IAAI,EAAE,IAAI,CAAC;IACX,KAAK,EAAE,IAAI,CAAC;CACb,GAAG;IACF,OAAO,EAAE,KAAK,CAAC;IACf,IAAI,EAAE,CAAC,CAAC;IACR,KAAK,EAAE,IAAI,CAAC;CACb,GAAG;IACF,OAAO,EAAE,KAAK,CAAC;IACf,IAAI,EAAE,IAAI,CAAC;IACX,KAAK,EAAE,mBAAmB,CAAC;CAC5B,CAAA;AAKD,qBAAa,mBAAmB,CAAC,CAAC,SAAS,YAAY,EAAE,CAAC,SAAS,UAAU,CAAE,SAAQ,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC;IAEzG,OAAO,CAAC,KAAK,CAA0C;IACvD,OAAO,CAAC,QAAQ,CAAsC;IAEtD,QAAQ,CAAC,CAAC,SAAS,QAAQ,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,CAAC,EAAE,MAAM,EAAE,oBAAoB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAqC7H,WAAW,CAAC,CAAC,SAAS,WAAW,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,CAAC,EAAE,MAAM,EAAE,oBAAoB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAqCnI,gBAAgB,CAAC,CAAC,SAAS,QAAQ,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,CAAC,EAAE,MAAM,EAAE,oBAAoB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IA+BrI,OAAO,CAAC,iBAAiB;IAsBzB,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,gBAAgB;IAKxB,OAAO,CAAC,mBAAmB;IAU3B,cAAc,CAAC,CAAC,SAAS,QAAQ,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,CAAC,EAAE,MAAM,EAAE,oBAAoB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CA0BpI"}
package/dist/index.js ADDED
@@ -0,0 +1,154 @@
1
+ import { CovenantClient } from "@covenant-rpc/client";
2
+ import { useEffect, useState } from "react";
3
+ export class CovenantReactClient extends CovenantClient {
4
+ // this is any becuse we have no other choice. You just gotta trust me on this one.
5
+ cache = new Map();
6
+ listenes = new Map();
7
+ useQuery(procedureName, inputs) {
8
+ const [state, setState] = useState({
9
+ loading: true,
10
+ data: null,
11
+ error: null,
12
+ });
13
+ useEffect(() => {
14
+ const fn = async () => {
15
+ setState({
16
+ loading: true,
17
+ data: null,
18
+ error: null,
19
+ });
20
+ const response = await this.query(procedureName, inputs);
21
+ if (response.success) {
22
+ setState({
23
+ loading: false,
24
+ data: response.data,
25
+ error: null,
26
+ });
27
+ }
28
+ else {
29
+ setState({
30
+ loading: false,
31
+ data: null,
32
+ error: response.error,
33
+ });
34
+ }
35
+ };
36
+ fn();
37
+ }, [inputs]);
38
+ return state;
39
+ }
40
+ useMutation(procedureName, inputs) {
41
+ const [state, setState] = useState({
42
+ loading: true,
43
+ data: null,
44
+ error: null,
45
+ });
46
+ useEffect(() => {
47
+ const fn = async () => {
48
+ setState({
49
+ loading: true,
50
+ data: null,
51
+ error: null,
52
+ });
53
+ const response = await this.mutate(procedureName, inputs);
54
+ if (response.success) {
55
+ setState({
56
+ loading: false,
57
+ data: response.data,
58
+ error: null,
59
+ });
60
+ }
61
+ else {
62
+ setState({
63
+ loading: false,
64
+ data: null,
65
+ error: response.error,
66
+ });
67
+ }
68
+ };
69
+ fn();
70
+ }, [inputs]);
71
+ return state;
72
+ }
73
+ useListenedQuery(procedureName, inputs) {
74
+ const [state, setState] = useState({
75
+ loading: true,
76
+ data: null,
77
+ error: null,
78
+ });
79
+ useEffect(() => {
80
+ return this.listen(procedureName, inputs, ({ data, error }) => {
81
+ if (error !== null) {
82
+ setState({
83
+ loading: false,
84
+ error: error,
85
+ data: null,
86
+ });
87
+ }
88
+ else {
89
+ setState({
90
+ loading: false,
91
+ error: null,
92
+ data: data,
93
+ });
94
+ }
95
+ });
96
+ }, [inputs]);
97
+ return state;
98
+ }
99
+ createCachedQuery(procedureName, inputs) {
100
+ this.listen(procedureName, inputs, ({ data, error }) => {
101
+ const state = error === null ? {
102
+ loading: false,
103
+ error: null,
104
+ data: data
105
+ } : {
106
+ loading: false,
107
+ error: error,
108
+ data: null,
109
+ };
110
+ const key = this.getCacheKey(String(procedureName), inputs);
111
+ const listeners = this.listenes.get(key) ?? [];
112
+ this.cache.set(key, state);
113
+ for (const l of listeners) {
114
+ l(state);
115
+ }
116
+ });
117
+ }
118
+ getCacheKey(procedureName, inputs) {
119
+ return `${procedureName}-${JSON.stringify(inputs)}`;
120
+ }
121
+ addCacheListener(key, l) {
122
+ const current = this.listenes.get(key) ?? [];
123
+ current.push(l);
124
+ this.listenes.set(key, current);
125
+ }
126
+ removeCacheListener(listener) {
127
+ for (const [k, v] of this.listenes) {
128
+ if (v.find(l => l === listener) !== undefined) {
129
+ const n = v.filter(l => l !== listener);
130
+ this.listenes.set(k, n);
131
+ }
132
+ }
133
+ }
134
+ useCachedQuery(procedureName, inputs) {
135
+ const k = this.getCacheKey(String(procedureName), inputs);
136
+ const [state, setState] = useState(this.cache.has(k) ? this.cache.get(k) : {
137
+ loading: true,
138
+ data: null,
139
+ error: null,
140
+ });
141
+ useEffect(() => {
142
+ // we have to refetch the key in case inputs changed
143
+ const newKey = this.getCacheKey(String(procedureName), inputs);
144
+ if (!this.cache.has(k)) {
145
+ this.createCachedQuery(procedureName, inputs);
146
+ }
147
+ const l = (s) => setState(s);
148
+ this.addCacheListener(newKey, l);
149
+ return () => this.removeCacheListener(l);
150
+ }, [setState, inputs]);
151
+ return state;
152
+ }
153
+ }
154
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,8 +1,17 @@
1
1
  {
2
2
  "name": "@covenant-rpc/react",
3
- "module": "index.ts",
3
+ "module": "./dist/index.js",
4
4
  "type": "module",
5
- "version": "0.3.0",
5
+ "version": "0.5.0",
6
+ "exports": {
7
+ ".": {
8
+ "types": "./dist/index.d.ts",
9
+ "import": "./dist/index.js"
10
+ }
11
+ },
12
+ "scripts": {
13
+ "build": "bun ../../scripts/build-package.ts ."
14
+ },
6
15
  "publishConfig": {
7
16
  "access": "public"
8
17
  },
@@ -11,7 +20,11 @@
11
20
  },
12
21
  "dependencies": {
13
22
  "react": "^19.1.1",
14
- "@covenant-rpc/core": "^0.3.0",
15
- "@covenant-rpc/client": "^0.3.0"
16
- }
23
+ "@covenant-rpc/core": "^0.5.0",
24
+ "@covenant-rpc/client": "^0.5.0"
25
+ },
26
+ "types": "./dist/index.d.ts",
27
+ "files": [
28
+ "dist"
29
+ ]
17
30
  }
package/CLAUDE.md DELETED
@@ -1,107 +0,0 @@
1
- ---
2
-
3
- Default to using Bun instead of Node.js.
4
-
5
- - Use `bun <file>` instead of `node <file>` or `ts-node <file>`
6
- - Use `bun test` instead of `jest` or `vitest`
7
- - Use `bun build <file.html|file.ts|file.css>` instead of `webpack` or `esbuild`
8
- - Use `bun install` instead of `npm install` or `yarn install` or `pnpm install`
9
- - Use `bun run <script>` instead of `npm run <script>` or `yarn run <script>` or `pnpm run <script>`
10
- - Bun automatically loads .env, so don't use dotenv.
11
-
12
- ## APIs
13
-
14
- - `Bun.serve()` supports WebSockets, HTTPS, and routes. Don't use `express`.
15
- - `bun:sqlite` for SQLite. Don't use `better-sqlite3`.
16
- - `Bun.redis` for Redis. Don't use `ioredis`.
17
- - `Bun.sql` for Postgres. Don't use `pg` or `postgres.js`.
18
- - `WebSocket` is built-in. Don't use `ws`.
19
- - Prefer `Bun.file` over `node:fs`'s readFile/writeFile
20
- - Bun.$`ls` instead of execa.
21
-
22
- ## Testing
23
-
24
- Use `bun test` to run tests.
25
-
26
- ```ts#index.test.ts
27
- import { test, expect } from "bun:test";
28
-
29
- test("hello world", () => {
30
- expect(1).toBe(1);
31
- });
32
- ```
33
-
34
- ## Frontend
35
-
36
- Use HTML imports with `Bun.serve()`. Don't use `vite`. HTML imports fully support React, CSS, Tailwind.
37
-
38
- Server:
39
-
40
- ```ts#index.ts
41
- import index from "./index.html"
42
-
43
- Bun.serve({
44
- routes: {
45
- "/": index,
46
- "/api/users/:id": {
47
- GET: (req) => {
48
- return new Response(JSON.stringify({ id: req.params.id }));
49
- },
50
- },
51
- },
52
- // optional websocket support
53
- websocket: {
54
- open: (ws) => {
55
- ws.send("Hello, world!");
56
- },
57
- message: (ws, message) => {
58
- ws.send(message);
59
- },
60
- close: (ws) => {
61
- // handle close
62
- }
63
- },
64
- development: {
65
- hmr: true,
66
- console: true,
67
- }
68
- })
69
- ```
70
-
71
- HTML files can import .tsx, .jsx or .js files directly and Bun's bundler will transpile & bundle automatically. `<link>` tags can point to stylesheets and Bun's CSS bundler will bundle.
72
-
73
- ```html#index.html
74
- <html>
75
- <body>
76
- <h1>Hello, world!</h1>
77
- <script type="module" src="./frontend.tsx"></script>
78
- </body>
79
- </html>
80
- ```
81
-
82
- With the following `frontend.tsx`:
83
-
84
- ```tsx#frontend.tsx
85
- import React from "react";
86
-
87
- // import .css files directly and it works
88
- import './index.css';
89
-
90
- import { createRoot } from "react-dom/client";
91
-
92
- const root = createRoot(document.body);
93
-
94
- export default function Frontend() {
95
- return <h1>Hello, world!</h1>;
96
- }
97
-
98
- root.render(<Frontend />);
99
- ```
100
-
101
- Then, run index.ts
102
-
103
- ```sh
104
- bun --hot ./index.ts
105
- ```
106
-
107
- For more information, read the Bun API docs in `node_modules/bun-types/docs/**.md`.
package/helpers.ts DELETED
@@ -1,105 +0,0 @@
1
- import type { AsyncHook } from ".";
2
- import { useEffect, useState } from "react";
3
- // definition for AsyncHook:
4
- // export type AsyncHook<T> = {
5
- // loading: true,
6
- // data: null,
7
- // error: null,
8
- // } | {
9
- // loading: false,
10
- // data: T,
11
- // error: null,
12
- // } | {
13
- // loading: false,
14
- // data: null,
15
- // error: ReactProcedureError,
16
- // }
17
-
18
-
19
- export type CallbackFunction<T> = () => T;
20
-
21
-
22
- // useDataStream is best described by an example. Let's say we have a chatFunction which has two methods:
23
- //
24
- // getCurrentMessages: () => Promise<Message[]>
25
- // connectToUpdates: (onUpdate: () => Message): Promise<void>
26
- // disconnect: () => void
27
- //
28
- // We want to fetch all messages as well as continue to listen to them. useDataStream should handle this
29
- // as well as various race conditions
30
-
31
- export function useDataStream<T>({ initialFetch, connect, disconnect }: {
32
- initialFetch: () => Promise<T[]>,
33
- connect: (callback: CallbackFunction<T>) => Promise<void>,
34
- disconnect: () => void
35
- }): AsyncHook<T[]> {
36
- const [state, setState] = useState<AsyncHook<T[]>>({
37
- loading: true,
38
- data: null,
39
- error: null
40
- });
41
-
42
- useEffect(() => {
43
- let isMounted = true;
44
- let items: T[] = [];
45
- let isConnecting = false;
46
-
47
- const handleNewItem = (newItem: T) => {
48
- if (!isMounted) return;
49
-
50
- // Add new item to the list
51
- items = [...items, newItem];
52
- setState({
53
- loading: false,
54
- data: items,
55
- error: null
56
- });
57
- };
58
-
59
- const initialize = async () => {
60
- try {
61
- // Fetch initial data first
62
- const initialData = await initialFetch();
63
-
64
- if (!isMounted) return;
65
-
66
- items = initialData;
67
- setState({
68
- loading: false,
69
- data: items,
70
- error: null
71
- });
72
-
73
- // Connect to updates
74
- // Note: The type signature indicates CallbackFunction<T> = () => T,
75
- // but the practical use case requires (newItem: T) => void
76
- // Using type assertion to work with the expected runtime behavior
77
- isConnecting = true;
78
- await connect(handleNewItem as any);
79
-
80
- } catch (error) {
81
- if (!isMounted) return;
82
-
83
- setState({
84
- loading: false,
85
- data: null,
86
- error: {
87
- code: 500,
88
- message: error instanceof Error ? error.message : 'Unknown error'
89
- }
90
- });
91
- }
92
- };
93
-
94
- initialize();
95
-
96
- return () => {
97
- isMounted = false;
98
- if (isConnecting) {
99
- disconnect();
100
- }
101
- };
102
- }, [initialFetch, connect, disconnect]);
103
-
104
- return state;
105
- }
package/index.ts DELETED
@@ -1,209 +0,0 @@
1
- import type { ProcedureMap, ChannelMap } from "@covenant-rpc/core";
2
- import { CovenantClient, type MutationKey, type QueryKey } from "@covenant-rpc/client";
3
- import type { InferProcedureInputs, InferProcedureOutputs } from "@covenant-rpc/core/procedure";
4
- import { useEffect, useState } from "react";
5
-
6
-
7
- export interface ReactProcedureError {
8
- code: number;
9
- message: string;
10
- }
11
-
12
- export type AsyncHook<T> = {
13
- loading: true,
14
- data: null,
15
- error: null,
16
- } | {
17
- loading: false,
18
- data: T,
19
- error: null,
20
- } | {
21
- loading: false,
22
- data: null,
23
- error: ReactProcedureError,
24
- }
25
-
26
- type Listener = (k: AsyncHook<any>) => void;
27
-
28
-
29
- export class CovenantReactClient<P extends ProcedureMap, C extends ChannelMap> extends CovenantClient<P, C> {
30
- // this is any becuse we have no other choice. You just gotta trust me on this one.
31
- private cache: Map<string, AsyncHook<any>> = new Map();
32
- private listenes: Map<string, Listener[]> = new Map();
33
-
34
- useQuery<Q extends QueryKey<P>>(procedureName: Q, inputs: InferProcedureInputs<P[Q]>): AsyncHook<InferProcedureOutputs<P[Q]>> {
35
- const [state, setState] = useState<AsyncHook<InferProcedureOutputs<P[Q]>>>({
36
- loading: true,
37
- data: null,
38
- error: null,
39
- });
40
-
41
- useEffect(() => {
42
- const fn = async () => {
43
- setState({
44
- loading: true,
45
- data: null,
46
- error: null,
47
- });
48
-
49
- const response = await this.query(procedureName, inputs);
50
-
51
- if (response.success) {
52
- setState({
53
- loading: false,
54
- data: response.data,
55
- error: null,
56
- });
57
- } else {
58
- setState({
59
- loading: false,
60
- data: null,
61
- error: response.error,
62
- });
63
- }
64
- }
65
- fn();
66
- }, [inputs]);
67
-
68
- return state;
69
- }
70
-
71
- useMutation<Q extends MutationKey<P>>(procedureName: Q, inputs: InferProcedureInputs<P[Q]>): AsyncHook<InferProcedureOutputs<P[Q]>> {
72
- const [state, setState] = useState<AsyncHook<InferProcedureOutputs<P[Q]>>>({
73
- loading: true,
74
- data: null,
75
- error: null,
76
- });
77
-
78
- useEffect(() => {
79
- const fn = async () => {
80
- setState({
81
- loading: true,
82
- data: null,
83
- error: null,
84
- });
85
-
86
- const response = await this.mutate(procedureName, inputs);
87
-
88
- if (response.success) {
89
- setState({
90
- loading: false,
91
- data: response.data,
92
- error: null,
93
- });
94
- } else {
95
- setState({
96
- loading: false,
97
- data: null,
98
- error: response.error,
99
- });
100
- }
101
- }
102
- fn();
103
- }, [inputs]);
104
-
105
- return state;
106
- }
107
-
108
- useListenedQuery<Q extends QueryKey<P>>(procedureName: Q, inputs: InferProcedureInputs<P[Q]>): AsyncHook<InferProcedureOutputs<P[Q]>> {
109
- const [state, setState] = useState<AsyncHook<InferProcedureOutputs<P[Q]>>>({
110
- loading: true,
111
- data: null,
112
- error: null,
113
- });
114
-
115
-
116
- useEffect(() => {
117
- return this.listen(procedureName, inputs, ({ data, error }) => {
118
-
119
- if (error !== null) {
120
- setState({
121
- loading: false,
122
- error: error,
123
- data: null,
124
- });
125
- } else {
126
- setState({
127
- loading: false,
128
- error: null,
129
- data: data,
130
- });
131
- }
132
- });
133
- }, [inputs])
134
-
135
-
136
- return state;
137
- }
138
-
139
- private createCachedQuery<Q extends QueryKey<P>>(procedureName: Q, inputs: InferProcedureInputs<P[Q]>) {
140
- this.listen(procedureName, inputs, ({ data, error }) => {
141
- const state: AsyncHook<InferProcedureOutputs<P[Q]>> = error === null ? {
142
- loading: false,
143
- error: null,
144
- data: data
145
- } : {
146
- loading: false,
147
- error: error,
148
- data: null,
149
- }
150
- const key = this.getCacheKey(String(procedureName), inputs);
151
-
152
- const listeners = this.listenes.get(key) ?? [];
153
- this.cache.set(key, state);
154
-
155
- for (const l of listeners) {
156
- l(state);
157
- }
158
- })
159
- }
160
-
161
- private getCacheKey(procedureName: string, inputs: any) {
162
- return `${procedureName}-${JSON.stringify(inputs)}`;
163
- }
164
-
165
- private addCacheListener(key: string, l: Listener) {
166
- const current = this.listenes.get(key) ?? [];
167
- current.push(l);
168
- this.listenes.set(key, current);
169
- }
170
- private removeCacheListener(listener: Listener) {
171
- for (const [k, v] of this.listenes) {
172
- if (v.find(l => l === listener) !== undefined) {
173
- const n = v.filter(l => l !== listener);
174
- this.listenes.set(k, n);
175
- }
176
- }
177
- }
178
-
179
-
180
- useCachedQuery<Q extends QueryKey<P>>(procedureName: Q, inputs: InferProcedureInputs<P[Q]>): AsyncHook<InferProcedureOutputs<P[Q]>> {
181
- const k = this.getCacheKey(String(procedureName), inputs);
182
- const [state, setState] = useState<AsyncHook<InferProcedureOutputs<P[Q]>>>(this.cache.has(k) ? this.cache.get(k)! : {
183
- loading: true,
184
- data: null,
185
- error: null,
186
- });
187
-
188
- useEffect(() => {
189
- // we have to refetch the key in case inputs changed
190
- const newKey = this.getCacheKey(String(procedureName), inputs);
191
-
192
- if (!this.cache.has(k)) {
193
- this.createCachedQuery(procedureName, inputs);
194
- }
195
- const l: Listener = (s) => setState(s);
196
-
197
- this.addCacheListener(newKey, l);
198
-
199
- return () => this.removeCacheListener(l);
200
-
201
- }, [setState, inputs]);
202
-
203
-
204
- return state;
205
- }
206
- }
207
-
208
-
209
-
package/tsconfig.json DELETED
@@ -1,29 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- // Environment setup & latest features
4
- "lib": ["ESNext"],
5
- "target": "ESNext",
6
- "module": "Preserve",
7
- "moduleDetection": "force",
8
- "jsx": "react-jsx",
9
- "allowJs": true,
10
-
11
- // Bundler mode
12
- "moduleResolution": "bundler",
13
- "allowImportingTsExtensions": true,
14
- "verbatimModuleSyntax": true,
15
- "noEmit": true,
16
-
17
- // Best practices
18
- "strict": true,
19
- "skipLibCheck": true,
20
- "noFallthroughCasesInSwitch": true,
21
- "noUncheckedIndexedAccess": true,
22
- "noImplicitOverride": true,
23
-
24
- // Some stricter flags (disabled by default)
25
- "noUnusedLocals": false,
26
- "noUnusedParameters": false,
27
- "noPropertyAccessFromIndexSignature": false
28
- }
29
- }