From b5c14ba76f439a3e5d9f2d19d72588811f804924 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADtor=20Vasconcellos?= Date: Mon, 7 Oct 2024 05:19:06 -0300 Subject: [PATCH] Rework the frontend code - Remove unused frontend packages - Improve typescript typing - Fix some small logic bugs - Small improvement to error handling - Change some repository links --- .github/workflows/ci.yml | 113 ++---- .prettierignore | 1 + Cargo.toml | 3 +- httpz/Cargo.toml | 6 +- httpz/README.md | 2 +- package.json | 16 +- packages/client/package.json | 4 +- packages/client/src/bindings.ts | 71 +++- packages/client/src/client.ts | 113 ------ packages/client/src/error.ts | 30 +- packages/client/src/full.ts | 1 - packages/client/src/index.ts | 10 +- packages/client/src/transport.ts | 179 ---------- packages/client/src/typescript.ts | 4 +- packages/client/src/v2.ts | 11 - packages/client/src/v2/client.ts | 70 ++-- packages/client/src/v2/error.ts | 10 - packages/client/src/v2/links/httpLink.ts | 31 +- packages/client/src/v2/links/link.ts | 4 +- packages/client/src/v2/links/wsLink.ts | 47 ++- packages/client/src/v2/observable.ts | 9 +- packages/react/package.json | 12 +- packages/react/src/index.tsx | 333 ----------------- packages/react/src/v2.tsx | 162 ++++----- packages/tauri/package.json | 10 +- packages/tauri/src/index.ts | 67 ---- packages/tauri/src/v2.ts | 76 ++-- pnpm-lock.yaml | 435 ++++++----------------- src/router.rs | 2 +- tsconfig.json | 3 + 30 files changed, 469 insertions(+), 1366 deletions(-) create mode 100644 .prettierignore delete mode 100644 packages/client/src/client.ts delete mode 100644 packages/client/src/full.ts delete mode 100644 packages/client/src/transport.ts delete mode 100644 packages/client/src/v2.ts delete mode 100644 packages/client/src/v2/error.ts delete mode 100644 packages/react/src/index.tsx delete mode 100644 packages/tauri/src/index.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fb3b2c13..afe0f568 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,108 +12,63 @@ on: workflow_dispatch: jobs: - test: - name: Test + typescript: + name: TypeScript CI runs-on: ubuntu-latest - outputs: - filter: ${{ steps.filter.outputs.workflows }} steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Install NodeJS - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: - node-version: '16' + node-version: '20' - name: Install pnpm - uses: pnpm/action-setup@v2.2.4 + uses: pnpm/action-setup@v4 with: version: latest - - name: Install system dependencies # Pull this step from `oscartbeaumont/specta` - run: sudo apt-get update && sudo apt install libwebkit2gtk-4.1-dev build-essential curl wget file libssl-dev libayatana-appindicator3-dev librsvg2-dev - - - name: Install Rust toolchain - run: rustup toolchain install stable --profile minimal - - - name: Rust cache - uses: Swatinem/rust-cache@v2 - - - name: Install npm dependencies + - name: Install pnpm dependencies run: pnpm i --frozen-lockfile - # TODO: Enabling this breaks the CI build with SolidJS Typescript errors and I don't get it. - # - name: Build npm packages - # run: pnpm build + - name: Check format with Prettier + run: pnpm prettier --check . - - name: Build Rust crate - run: cargo build --all-features + - name: Typecheck with TypeScript + run: pnpm typecheck - # - name: Generate test bindings for typecheck - # run: cargo test -p example tests::test_rspc_router -- --exact # TODO: Move this into a the first unit test - - # - name: Typecheck - # run: pnpm typecheck - - # - name: Test Rust crate - # run: cargo test --all --exclude create-rspc-app --all-features - - - uses: dorny/paths-filter@v2 - id: filter - with: - filters: | - workflows: - - 'packages/**' - - build-publish: - name: Publish to NPM + rust: + name: Rust CI runs-on: ubuntu-latest - needs: test - if: (github.event_name == 'workflow_dispatch' || (needs.test.outputs.filter == 'true' && github.event_name == 'push')) && github.repository == 'spacedriveapp/rspc' steps: - - uses: actions/checkout@v3 + - name: Checkout repository + uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: pnpm/action-setup@v2.2.4 - with: - version: latest - - - uses: actions/setup-node@v3 - with: - node-version: '16' - cache: 'pnpm' - - - name: Setup npmrc - run: echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > .npmrc - - - name: Install dependencies - run: pnpm i --frozen-lockfile + - name: Install system dependencies + run: |- + sudo apt-get -yqq update + sudo apt-get install -yqq libwebkit2gtk-4.1-dev build-essential curl wget file libxdo-dev libssl-dev libayatana-appindicator3-dev librsvg2-dev - - name: Build - run: pnpm build - - - name: Configure main version - if: github.ref == 'refs/heads/main' - run: pnpm set-package-versions - env: - RELEASE_COMMIT_SHA: ${{ github.sha }} + - name: Install Rust toolchain + run: rustup toolchain install stable --profile minimal - - name: Publish main to npm - if: github.ref == 'refs/heads/main' - run: pnpm publish -r ${TAG} --no-git-checks --filter "@oscartbeaumont-sd/rspc-*" --access public - env: - TAG: ${{ (github.ref == 'refs/heads/main' && '--tag=main') || '' }} + - name: Build Rust crate + run: cargo build --all-features - # - name: Configure release version - # if: github.ref_type == 'tag' - # run: pnpm set-package-versions + - name: Check Rust format + run: cargo fmt -- --check - # - name: Publish release to npm - # if: github.ref_type == 'tag' - # run: pnpm publish -r ${TAG} --no-git-checks --filter "@oscartbeaumont-sd/rspc-*" --access public - # env: - # TAG: ${{ (contains(github.ref_name, '-beta.') && '--tag=beta') || ''}} + - name: Run Clippy + uses: giraffate/clippy-action@v1 + with: + reporter: github-pr-review + tool_name: 'Clippy' + filter_mode: diff_context + github_token: ${{ secrets.GITHUB_TOKEN }} + clippy_flags: --workspace --all-features --locked + fail_on_error: true diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..bd5535a6 --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +pnpm-lock.yaml diff --git a/Cargo.toml b/Cargo.toml index b539fe53..cd377192 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,8 +7,7 @@ description = "A blazing fast and easy to use TRPC server for Rust." edition = "2021" license = "MIT" -documentation = "https://docs.rs/rspc/latest/rspc" -repository = "https://github.com/oscartbeaumont/rspc" +repository = "https://github.com/spacedriveapp/rspc" categories = ["asynchronous", "web-programming"] include = ["/LICENCE", "/README.md", "/src"] diff --git a/httpz/Cargo.toml b/httpz/Cargo.toml index ba80eb8a..7299af16 100644 --- a/httpz/Cargo.toml +++ b/httpz/Cargo.toml @@ -7,10 +7,8 @@ description = "Code once, support every Rust webserver!" edition = "2021" license = "MIT" -documentation = "https://docs.rs/httpz" -homepage = "https://github.com/oscartbeaumont/httpz" -readme = "README.md" -repository = "https://github.com/oscartbeaumont/httpz" +readme = "README.md" +repository = "https://github.com/spacedriveapp/rspc/tree/main/httpz" categories = [ "asynchronous", diff --git a/httpz/README.md b/httpz/README.md index dbafd441..38cf7160 100644 --- a/httpz/README.md +++ b/httpz/README.md @@ -45,4 +45,4 @@ httpz is primarily designed to make life easier for library authors. It allows a Libraries using httpz: -- [rspc](https://github.com/oscartbeaumont/rspc) +- [rspc](https://github.com/spacedriveapp/rspc) diff --git a/package.json b/package.json index 1a41b0c9..e86bf56a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { - "name": "@oscartbeaumont-sd/rspc-workspace", - "version": "0.0.0", + "name": "@spacedrive/rspc-workspace", + "version": "1.0.0", "description": "A blazingly fast and easy to use TRPC-like server for Rust.", "keywords": [], "author": "Oscar Beaumont", @@ -8,15 +8,15 @@ "private": true, "scripts": { "format": "prettier --write .", - "test": "pnpm -r --parallel --filter=!@oscartbeaumont-sd/rspc-docs --filter=!@oscartbeaumont-sd/rspc-config --filter=!@oscartbeaumont-sd/rspc-examples-* --filter=!rspc-vscode exec pnpm test", - "typecheck": "pnpm -r --filter=!rspc-vscode exec tsc --noEmit", - "client": "pnpm --filter @oscartbeaumont-sd/rspc-client -- ", - "react": "pnpm --filter @oscartbeaumont-sd/rspc-react -- ", - "tauri": "pnpm --filter @oscartbeaumont-sd/rspc-tauri -- " + "test": "echo 'No tests yet.' >&2 && exit 1", + "typecheck": "pnpm -r exec tsc --noEmit", + "client": "pnpm --filter @spacedrive/rspc-client -- ", + "react": "pnpm --filter @spacedrive/rspc-react -- ", + "tauri": "pnpm --filter @spacedrive/rspc-tauri -- " }, "devDependencies": { "typescript": "^5.6.2", "@ianvs/prettier-plugin-sort-imports": "^4.3.1" }, - "packageManager": "pnpm@9.11.0" + "packageManager": "pnpm@9.12.0" } diff --git a/packages/client/package.json b/packages/client/package.json index 8e0fc289..64518c48 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { - "name": "@oscartbeaumont-sd/rspc-client", - "version": "0.1.3", + "name": "@spacedrive/rspc-client", + "version": "0.2.0", "description": "A blazingly fast and easy to use TRPC-like server for Rust.", "keywords": [], "author": "Oscar Beaumont", diff --git a/packages/client/src/bindings.ts b/packages/client/src/bindings.ts index fadb7334..b7b67b03 100644 --- a/packages/client/src/bindings.ts +++ b/packages/client/src/bindings.ts @@ -2,43 +2,82 @@ // Checkout the unit test 'export_internal_types' to see where this files comes from! /** - * TODO + * Represents a unique identifier for a request. * * @internal */ export type RequestId = null | number | string -export type SubscriptionStop = { input: RequestId } +export type SubscriptionStop = { readonly input: RequestId } -export type NewOrOldInput = [RequestId, unknown] | unknown +export type NewOrOldInput = readonly [RequestId, unknown] | unknown /** - * TODO + * Represents a request object with various methods. * * @internal */ -export type Request = ( - | { method: 'query'; params: { path: string; input: unknown } } - | { method: 'mutation'; params: { path: string; input: unknown } } - | { method: 'subscription'; params: { path: string; input: NewOrOldInput } } +export type Request = Readonly< + | { + method: 'query' + params: { path: string; input: unknown } + } + | { + method: 'mutation' + params: { path: string; input: unknown } + } + | { + method: 'subscription' + params: { path: string; input: NewOrOldInput } + } | { method: 'subscriptionStop'; params: SubscriptionStop | null } -) & { jsonrpc?: string | null; id?: RequestId } +> & { jsonrpc?: string | null; id?: RequestId } /** * Represents a Typescript procedure file which is generated by the Rust code. - * This is codegenerated Typescript file is how we can validate the types on the frontend match Rust. + * This code-generated Typescript file is how we can validate the types on the frontend match Rust. * * @internal */ -export type ProcedureDef = { key: string; input: any; result: any } +export type ProcedureDef = Readonly<{ + key: string + input: unknown + result: unknown +}> /** - * TODO + * Represents the inner structure of a request object. * * @internal */ -export type RequestInner = - | { method: 'query'; params: { path: string; input: unknown } } - | { method: 'mutation'; params: { path: string; input: unknown } } - | { method: 'subscription'; params: { path: string; input: NewOrOldInput } } +export type RequestInner = Readonly< + | { + method: 'query' + params: { path: string; input: unknown } + } + | { + method: 'mutation' + params: { path: string; input: unknown } + } + | { + method: 'subscription' + params: { path: string; input: NewOrOldInput } + } | { method: 'subscriptionStop'; params: SubscriptionStop | null } +> + +export type Response = Readonly<{ + id: string | number + result: + | { + type: 'event' | 'response' + data: unknown + } + | { + type: 'error' + data: { + message: string + code: number + } + } +}> diff --git a/packages/client/src/client.ts b/packages/client/src/client.ts deleted file mode 100644 index 0ba60b78..00000000 --- a/packages/client/src/client.ts +++ /dev/null @@ -1,113 +0,0 @@ -// TODO: Redo this entire system when links are introduced -import type { - _inferInfiniteQueryProcedureHandlerInput, - _inferProcedureHandlerInput, - inferMutationResult, - inferProcedures, - inferQueryResult, - inferSubscriptionResult, - ProceduresDef, - ProceduresLike, -} from '.' -import type { Transport } from './transport' - -import { RSPCError } from '.' -import { randomId } from './transport' - -// TODO -export interface SubscriptionOptions { - onStarted?: () => void - onData: (data: TOutput) => void - onError?: (err: RSPCError) => void -} - -// TODO -export interface ClientArgs { - transport: Transport - onError?: (err: RSPCError) => void | Promise -} - -// TODO -export function createClient( - args: ClientArgs -): Client> { - return new Client(args) -} - -// TODO -export class Client { - private transport: Transport - private subscriptionMap = new Map void>() - private onError?: (err: RSPCError) => void | Promise - - constructor(args: ClientArgs) { - this.transport = args.transport - this.transport.clientSubscriptionCallback = (id, value) => { - const func = this.subscriptionMap?.get(id) - if (func !== undefined) func(value) - } - this.subscriptionMap = new Map() - this.onError = args.onError - } - - async query( - keyAndInput: [key: K, ...input: _inferProcedureHandlerInput] - ): Promise> { - try { - return await this.transport.doRequest('query', keyAndInput[0], keyAndInput[1]) - } catch (err) { - if (this.onError) { - this.onError(err as RSPCError) - } - throw err - } - } - - async mutation( - keyAndInput: [key: K, ...input: _inferProcedureHandlerInput] - ): Promise> { - try { - return await this.transport.doRequest('mutation', keyAndInput[0], keyAndInput[1]) - } catch (err) { - if (this.onError) { - this.onError(err as RSPCError) - } - throw err - } - } - - // TODO: Redesign this, i'm sure it probably has race conditions but it works for now - addSubscription< - K extends TProcedures['subscriptions']['key'] & string, - TData = inferSubscriptionResult, - >( - keyAndInput: [K, ..._inferProcedureHandlerInput], - opts: SubscriptionOptions - ): () => void { - try { - let subscriptionId = randomId() - - const cleanup = () => { - this.subscriptionMap?.delete(subscriptionId) - if (subscriptionId) { - this.transport.doRequest('subscriptionStop', '', subscriptionId) - } - } - - this.transport.doRequest('subscription', keyAndInput[0], [subscriptionId, keyAndInput[1]]) - - if (opts.onStarted) opts.onStarted() - this.subscriptionMap?.set(subscriptionId, opts.onData) - - return () => { - cleanup() - } - } catch (err) { - if (this.onError) { - this.onError(err as RSPCError) - } - - return () => {} - } - } -} diff --git a/packages/client/src/error.ts b/packages/client/src/error.ts index 95659d86..1fd1d369 100644 --- a/packages/client/src/error.ts +++ b/packages/client/src/error.ts @@ -1,9 +1,33 @@ -export class RSPCError { - code: number - message: string +/** + * Custom error class for RSPC errors. + * + * @class RSPCError + * @implements {Error} + */ +export class RSPCError implements Error { + readonly name: string = 'RSPCError' + readonly code: number + readonly message: string + readonly stack?: string + /** + * Creates an instance of RSPCError. + * + * @param {number} code - The error code. + * @param {string} message - The error message. + */ constructor(code: number, message: string) { this.code = code this.message = message + this.stack = new Error().stack + } + + /** + * Returns a string representation of the error. + * + * @returns {string} The string representation of the error. + */ + toString(): string { + return `${this.name} (code: ${this.code}): ${this.message}` } } diff --git a/packages/client/src/full.ts b/packages/client/src/full.ts deleted file mode 100644 index 0b8141c1..00000000 --- a/packages/client/src/full.ts +++ /dev/null @@ -1 +0,0 @@ -export {} // TODO: Coming soon! diff --git a/packages/client/src/index.ts b/packages/client/src/index.ts index c0352605..75816de2 100644 --- a/packages/client/src/index.ts +++ b/packages/client/src/index.ts @@ -1,4 +1,10 @@ -export * from './client' -export * from './transport' export * from './error' export * from './typescript' +export * from './v2/client' +export * from './v2/observable' +export * from './v2/links/link' +export * from './v2/links/httpLink' +export * from './v2/links/wsLink' +export * from './v2/links/loggerLink' + +export type { Request as RspcRequest, Response as RspcResponse } from './bindings' diff --git a/packages/client/src/transport.ts b/packages/client/src/transport.ts deleted file mode 100644 index 0457a7ce..00000000 --- a/packages/client/src/transport.ts +++ /dev/null @@ -1,179 +0,0 @@ -import type { OperationType } from '.' - -import { RSPCError } from '.' - -// TODO: Redo this entire system when links are introduced -// TODO: Make this file work off Typescript types which are exported from Rust to ensure internal type-safety! - -// TODO -export interface Transport { - clientSubscriptionCallback?: (id: string, key: string, value: any) => void - - doRequest(operation: OperationType, key: string, input: any): Promise -} - -// TODO -export class FetchTransport implements Transport { - private url: string - clientSubscriptionCallback?: (id: string, key: string, value: any) => void - private fetch: typeof globalThis.fetch - - constructor(url: string, fetch?: typeof globalThis.fetch) { - this.url = url - this.fetch = fetch || globalThis.fetch.bind(globalThis) - } - - async doRequest(operation: OperationType, key: string, input: any): Promise { - if (operation === 'subscription' || operation === 'subscriptionStop') { - throw new Error( - `Subscribing to '${key}' failed as the HTTP transport does not support subscriptions! Maybe try using the websocket transport?` - ) - } - - let method = 'GET' - let body = undefined as any - let headers = new Headers() - - const params = new URLSearchParams() - if (operation === 'query') { - if (input !== undefined) { - params.append('input', JSON.stringify(input)) - } - } else if (operation === 'mutation') { - method = 'POST' - body = JSON.stringify(input || {}) - headers.set('Content-Type', 'application/json') - } - const paramsStr = params.toString() - const resp = await this.fetch( - `${this.url}/${key}${paramsStr.length > 0 ? `?${paramsStr}` : ''}`, - { - method, - body, - headers, - } - ) - - const respBody = await resp.json() - const { type, data } = respBody.result - if (type === 'error') { - const { code, message } = data - throw new RSPCError(code, message) - } - return data - } -} - -export const randomId = () => Math.random().toString(36).slice(2) - -const timeouts = [1000, 2000, 5000, 10000] // In milliseconds - -export class WebsocketTransport implements Transport { - private url: string - private ws: WebSocket - private requestMap = new Map void>() - clientSubscriptionCallback?: (id: string, value: any) => void - - constructor(url: string) { - this.url = url - this.ws = new WebSocket(url) - this.attachEventListeners() - } - - attachEventListeners() { - this.ws.addEventListener('message', event => { - const { id, result } = JSON.parse(event.data) - if (result.type === 'event') { - if (this.clientSubscriptionCallback) this.clientSubscriptionCallback(id, result.data) - } else if (result.type === 'response') { - if (this.requestMap.has(id)) { - this.requestMap.get(id)?.({ type: 'response', result: result.data }) - this.requestMap.delete(id) - } - } else if (result.type === 'error') { - const { message, code } = result.data - if (this.requestMap.has(id)) { - this.requestMap.get(id)?.({ type: 'error', message, code }) - this.requestMap.delete(id) - } - } else { - console.error(`Received event of unknown type '${result.type}'`) - } - }) - - this.ws.addEventListener('close', _ => { - this.reconnect() - }) - } - - async reconnect(timeoutIndex = 0) { - let timeout = - // @ts-expect-error // TODO: Fix this - (timeouts[timeoutIndex] ?? timeouts[timeouts.length - 1]) + - (Math.floor(Math.random() * 5000 /* 5 Seconds */) + 1) - - setTimeout(() => { - let ws = new WebSocket(this.url) - new Promise(function (resolve, reject) { - ws.addEventListener('open', () => resolve(null)) - ws.addEventListener('close', reject) - }) - .then(() => { - this.ws = ws - this.attachEventListeners() - }) - .catch(_ => this.reconnect(timeoutIndex++)) - }, timeout) - } - - async doRequest(operation: OperationType, key: string, input: any): Promise { - if (this.ws.readyState == 0) { - let resolve: () => void - const promise = new Promise(res => { - resolve = () => res(undefined) - }) - // @ts-ignore - this.ws.addEventListener('open', resolve) - await promise - } - - const id = randomId() - let resolve: (data: any) => void - const promise = new Promise(res => { - resolve = res - }) - - // @ts-ignore - this.requestMap.set(id, resolve) - - this.ws.send( - JSON.stringify({ - id, - method: operation, - params: { - path: key, - input, - }, - }) - ) - - const body = (await promise) as any - if (body.type === 'error') { - const { code, message } = body - throw new RSPCError(code, message) - } else if (body.type === 'response') { - return body.result - } else { - throw new Error(`RSPC Websocket doRequest received invalid body type '${body?.type}'`) - } - } -} - -// TODO -export class NoOpTransport implements Transport { - constructor() {} - - async doRequest(_: OperationType, __: string, ___: string): Promise { - return new Promise(() => {}) - } -} diff --git a/packages/client/src/typescript.ts b/packages/client/src/typescript.ts index 19c52b3b..7895604b 100644 --- a/packages/client/src/typescript.ts +++ b/packages/client/src/typescript.ts @@ -2,7 +2,7 @@ export type OperationType = 'query' | 'mutation' | 'subscription' | 'subscriptionStop' // TODO -export type ProcedureDef = { key: string; input: any; result: any } +export type ProcedureDef = { key: string; input: unknown; result: unknown } /** * This type represents the Typescript bindings which are generated from the router by Rust. @@ -110,7 +110,7 @@ export type inferSubscriptionResult< // TODO export type inferInfiniteQueries = Exclude< - Extract['queries'], { input: { cursor: any } }>, + Extract['queries'], { input: { cursor: unknown } }>, { input: never } > diff --git a/packages/client/src/v2.ts b/packages/client/src/v2.ts deleted file mode 100644 index 7d57aebb..00000000 --- a/packages/client/src/v2.ts +++ /dev/null @@ -1,11 +0,0 @@ -// The beta v2 client. This ia a rewrite and will replace the original client in the v1.0.0 release. - -export * from './v2/client' -export * from './v2/error' -export * from './v2/observable' -export * from './v2/links/link' -export * from './v2/links/httpLink' -export * from './v2/links/wsLink' -export * from './v2/links/loggerLink' - -export type { Request as RspcRequest } from './bindings' diff --git a/packages/client/src/v2/client.ts b/packages/client/src/v2/client.ts index e43fda4c..573ef527 100644 --- a/packages/client/src/v2/client.ts +++ b/packages/client/src/v2/client.ts @@ -5,20 +5,17 @@ import type { inferProcedureResult, inferQueryResult, ProceduresDef, -} from '..' -import type { Link, LinkResult, Operation, OperationContext } from '../v2' +} from '../typescript' +import type { Link, LinkResult, Operation, OperationContext } from './links/link' -import { AlphaRSPCError } from '../v2' +import { RSPCError } from '../error' -// TODO export interface SubscriptionOptions { - // onStarted?: () => void; onData: (data: TOutput) => void - // TODO: Probs remove `| Error` here - onError?: (err: AlphaRSPCError | Error) => void + onError?: (err: RSPCError) => void } -type KeyAndInput = [string] | [string, any] +export type KeyAndInput = [string, ...unknown[]] type OperationOpts = { signal?: AbortSignal @@ -26,10 +23,9 @@ type OperationOpts = { // skipBatch?: boolean; // TODO: Make this work + add this to React } -// TODO interface ClientArgs { links: Link[] - onError?: (err: AlphaRSPCError) => void | Promise + onError?: (err: RSPCError) => void | Promise } export function initRspc

(args: ClientArgs) { @@ -38,11 +34,10 @@ export function initRspc

(args: ClientArgs) { const generateRandomId = () => Math.random().toString(36).slice(2) -// TODO: This will replace old client export class AlphaClient

{ private links: Link[] - private onError?: (err: AlphaRSPCError) => void | Promise - private mapQueryKey?: (keyAndInput: KeyAndInput) => KeyAndInput // TODO: Do something so a single React.context can handle multiple of these + private onError?: (err: RSPCError) => void | Promise + mapQueryKey?: (keyAndInput: KeyAndInput) => KeyAndInput // TODO: Do something so a single React.context can handle multiple of these constructor(args: ClientArgs) { if (args.links.length === 0) { @@ -58,7 +53,7 @@ export class AlphaClient

{ opts?: OperationOpts ): Promise> { try { - const keyAndInput2 = this.mapQueryKey ? this.mapQueryKey(keyAndInput as any) : keyAndInput + const keyAndInput2 = this.mapQueryKey?.(keyAndInput) ?? keyAndInput const result = exec( { @@ -66,7 +61,7 @@ export class AlphaClient

{ type: 'query', input: keyAndInput2[1], path: keyAndInput2[0], - context: opts?.context || {}, + context: opts?.context ?? {}, }, this.links ) @@ -74,9 +69,7 @@ export class AlphaClient

{ return await new Promise(result.exec) } catch (err) { - if (this.onError) { - this.onError(err as AlphaRSPCError) - } + if (this.onError && err instanceof RSPCError) await this.onError(err) throw err } } @@ -86,7 +79,7 @@ export class AlphaClient

{ opts?: OperationOpts ): Promise> { try { - const keyAndInput2 = this.mapQueryKey ? this.mapQueryKey(keyAndInput as any) : keyAndInput + const keyAndInput2 = this.mapQueryKey?.(keyAndInput) ?? keyAndInput const result = exec( { @@ -94,7 +87,7 @@ export class AlphaClient

{ type: 'mutation', input: keyAndInput2[1], path: keyAndInput2[0], - context: opts?.context || {}, + context: opts?.context ?? {}, }, this.links ) @@ -102,23 +95,20 @@ export class AlphaClient

{ return await new Promise(result.exec) } catch (err) { - if (this.onError) { - this.onError(err as AlphaRSPCError) - } + if (this.onError && err instanceof RSPCError) await this.onError(err) throw err } } - // TODO: Handle resubscribing if the subscription crashes similar to what Tanstack Query does - addSubscription< + async addSubscription< K extends P['subscriptions']['key'] & string, TData = inferProcedureResult, >( keyAndInput: [K, ..._inferProcedureHandlerInput], opts: SubscriptionOptions & { context?: OperationContext } - ): () => void { + ): Promise<() => void> { try { - const keyAndInput2 = this.mapQueryKey ? this.mapQueryKey(keyAndInput as any) : keyAndInput + const keyAndInput2 = this.mapQueryKey?.(keyAndInput) ?? keyAndInput const result = exec( { @@ -126,34 +116,38 @@ export class AlphaClient

{ type: 'subscription', input: keyAndInput2[1], path: keyAndInput2[0], - context: opts?.context || {}, + context: opts.context ?? {}, }, this.links ) result.exec( - data => opts?.onData(data), - err => opts?.onError?.(err) + data => opts.onData(data as TData), + err => { + if (err instanceof RSPCError) opts.onError?.(err) + console.error(err) + } ) return result.abort } catch (err) { - if (this.onError) { - this.onError(err as AlphaRSPCError) + if (err instanceof RSPCError) { + if (this.onError) await this.onError(err) + return () => {} } - return () => {} + + throw err } } - // TODO: Remove this once middleware system is in place dangerouslyHookIntoInternals(opts?: { mapQueryKey?: (keyAndInput: KeyAndInput) => KeyAndInput }): AlphaClient { this.mapQueryKey = opts?.mapQueryKey - return this as any + return this as unknown as AlphaClient } } -function exec(op: Operation, links: Link[]) { +function exec(op: Operation, links: Link[]): LinkResult { if (!links[0]) throw new Error('No links provided') let prevLinkResult: LinkResult = { @@ -165,8 +159,8 @@ function exec(op: Operation, links: Link[]) { abort: () => {}, } - for (var linkIndex = 0; linkIndex < links.length; linkIndex++) { - const link = links[links.length - linkIndex - 1] + for (let linkIndex = links.length - 1; linkIndex >= 0; linkIndex--) { + const link = links[linkIndex] if (!link) continue const result = link({ op, diff --git a/packages/client/src/v2/error.ts b/packages/client/src/v2/error.ts deleted file mode 100644 index 4828aa0b..00000000 --- a/packages/client/src/v2/error.ts +++ /dev/null @@ -1,10 +0,0 @@ -export class AlphaRSPCError extends Error { - code: number - message: string - - constructor(code: number, message: string) { - super() - this.code = code - this.message = message - } -} diff --git a/packages/client/src/v2/links/httpLink.ts b/packages/client/src/v2/links/httpLink.ts index 1710a878..a792833f 100644 --- a/packages/client/src/v2/links/httpLink.ts +++ b/packages/client/src/v2/links/httpLink.ts @@ -1,6 +1,6 @@ import type { Link, Operation } from './link' -import { AlphaRSPCError } from '../error' +import { RSPCError } from '../../error' type HTTPHeaders = Record @@ -36,7 +36,7 @@ export function httpLink(opts: HttpLinkOpts): Link { exec: async (resolve, reject) => { if (op.type === 'subscription' || op.type === 'subscriptionStop') { reject( - // TODO: Move to `AlphaRSPCError` type?? + // TODO: Move to `RSPCError` type?? new Error( `Subscribing to '${op.path}' failed as the HTTP transport does not support subscriptions! Maybe try using the websocket transport?` ) @@ -45,7 +45,7 @@ export function httpLink(opts: HttpLinkOpts): Link { } let method = 'GET' - let body = undefined as any + let body: BodyInit | null = null let headers = new Headers() const defaultHeaders = @@ -83,15 +83,15 @@ export function httpLink(opts: HttpLinkOpts): Link { signal: abort.signal, } ) + // TODO: validate response const respBody = await resp.json() const { type, data } = respBody.result if (type === 'error') { const { code, message } = data - reject(new AlphaRSPCError(code, message)) - return + reject(new RSPCError(code, message)) + } else { + resolve(data) } - - resolve(data) }, execBatch: async () => {}, abort() { @@ -110,8 +110,8 @@ type HttpBatchLinkOpts = BaseHttpLinkOpts & { type BatchedItem = { op: Operation - resolve: (result: any) => void - reject: (error: Error | AlphaRSPCError) => void + resolve: (result: unknown) => void + reject: (error: Error | RSPCError) => void abort: AbortController } @@ -156,8 +156,13 @@ export function httpBatchLink(opts: HttpBatchLinkOpts): Link { ), }) - // TODO: Get this type instead of using `any` - const body: any[] = await resp.json() + // TODO: Validate response + const body: unknown = await resp.json() + if (!Array.isArray(body)) { + console.error('rspc: batch response not an array!') + return + } + if (body.length !== batch.length) { console.error('rspc: batch response length mismatch!') return @@ -172,7 +177,7 @@ export function httpBatchLink(opts: HttpBatchLinkOpts): Link { if (item.result.type === 'response') { batch[i]?.resolve(item.result.data) } else if (item.result.type === 'error') { - batch[i]?.reject(new AlphaRSPCError(item.result.data.code, item.result.data.message)) + batch[i]?.reject(new RSPCError(item.result.data.code, item.result.data.message)) } else { console.error('rspc: batch response type mismatch!') } @@ -198,7 +203,7 @@ export function httpBatchLink(opts: HttpBatchLinkOpts): Link { exec: async (resolve, reject) => { if (op.type === 'subscription' || op.type === 'subscriptionStop') { reject( - // TODO: Move to `AlphaRSPCError` type?? + // TODO: Move to `RSPCError` type?? new Error( `Subscribing to '${op.path}' failed as the HTTP transport does not support subscriptions! Maybe try using the websocket transport?` ) diff --git a/packages/client/src/v2/links/link.ts b/packages/client/src/v2/links/link.ts index ac8028ce..4ac89f9a 100644 --- a/packages/client/src/v2/links/link.ts +++ b/packages/client/src/v2/links/link.ts @@ -1,4 +1,4 @@ -import { AlphaRSPCError } from '../error' +import { RSPCError } from '../../error' /** * A map of data that can be used by links to store metadata about the current operation. @@ -29,7 +29,7 @@ export interface Operation { * @internal */ export type LinkResult = { - exec: (resolve: (result: any) => void, reject: (error: Error | AlphaRSPCError) => void) => void + exec: (resolve: (result: unknown) => void, reject: (error: Error | RSPCError) => void) => void abort: () => void } diff --git a/packages/client/src/v2/links/wsLink.ts b/packages/client/src/v2/links/wsLink.ts index aa11397d..4a095a3c 100644 --- a/packages/client/src/v2/links/wsLink.ts +++ b/packages/client/src/v2/links/wsLink.ts @@ -1,7 +1,7 @@ import type { Request as RspcRequest } from '../../bindings' import type { Link } from './link' -import { AlphaRSPCError } from '../error' +import { RSPCError } from '../../error' const timeouts = [1000, 2000, 5000, 10000] // In milliseconds @@ -18,8 +18,8 @@ function newWsManager(opts: WsLinkOpts) { const activeMap = new Map< string, { - resolve: (result: any) => void - reject: (error: Error | AlphaRSPCError) => void + resolve: (result: unknown) => void + reject: (error: Error | RSPCError) => void } >() @@ -35,7 +35,7 @@ function newWsManager(opts: WsLinkOpts) { activeMap.delete(id) } else if (result.type === 'error') { const { message, code } = result.data - activeMap.get(id)?.reject(new AlphaRSPCError(code, message)) + activeMap.get(id)?.reject(new RSPCError(code, message)) activeMap.delete(id) } else { console.error(`rspc: received event of unknown type '${result.type}'`) @@ -52,8 +52,7 @@ function newWsManager(opts: WsLinkOpts) { const reconnectWs = (timeoutIndex = 0) => { let timeout = - // @ts-expect-error // TODO: Fix this - (timeouts[timeoutIndex] ?? timeouts[timeouts.length - 1]) + + (timeouts[timeoutIndex] ?? timeouts[timeouts.length - 1] ?? 0) + (Math.floor(Math.random() * 5000 /* 5 Seconds */) + 1) setTimeout(() => { @@ -113,8 +112,8 @@ export function wsLink(opts: WsLinkOpts): Link { }) if (op.type === 'subscriptionStop') { - if (op.input === null || typeof op.input === 'string' || typeof op.input === 'number') { - send({ id: op.id, method: op.type, params: { input: op.input } }) + if (op.input == null || typeof op.input === 'string' || typeof op.input === 'number') { + send({ id: op.id, method: op.type, params: { input: op.input ?? null } }) } else { throw new Error('Invalid input for subscriptionStop') } @@ -177,15 +176,29 @@ export function wsBatchLink(opts: WsLinkOpts): Link { reject, }) - // @ts-expect-error // TODO: Fix this - batch.push({ - id: op.id, - method: op.type, - params: { - path: op.path, - input: op.input, - }, - }) + if (op.type === 'subscriptionStop') { + if (op.input != null && typeof op.input !== 'string' && typeof op.input !== 'number') { + throw new Error( + `Expected 'input' to be of type 'string' or 'number' for 'subscriptionStop', but got ${typeof op.input}` + ) + } + batch.push({ + id: op.id, + method: op.type, + params: { + input: op.input ?? null, + }, + }) + } else { + batch.push({ + id: op.id, + method: op.type, + params: { + path: op.path, + input: op.input, + }, + }) + } queueBatch() }, abort() { diff --git a/packages/client/src/v2/observable.ts b/packages/client/src/v2/observable.ts index da191be3..6065dcfb 100644 --- a/packages/client/src/v2/observable.ts +++ b/packages/client/src/v2/observable.ts @@ -1,13 +1,14 @@ /** - * TODO + * A function type that represents an abort operation. * * @internal */ export type AbortFn = () => void /** - * TODO + * Represents a promise along with a function to cancel the operation. * + * @template TValue The type of the value that the promise resolves to. * @internal */ export type PromiseAndCancel = { @@ -16,8 +17,8 @@ export type PromiseAndCancel = { } /** - * TODO + * Represents a fake observable with an execution method that returns a promise and a cancel function. * * @internal */ -export type FakeObservable = { exec: () => PromiseAndCancel } +export type FakeObservable = { exec: () => PromiseAndCancel } diff --git a/packages/react/package.json b/packages/react/package.json index f1850dab..1d8c1278 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -1,24 +1,24 @@ { - "name": "@oscartbeaumont-sd/rspc-react", - "version": "0.1.3", + "name": "@spacedrive/rspc-react", + "version": "0.2.0", "description": "A blazingly fast and easy to use TRPC-like server for Rust.", "keywords": [], "author": "Oscar Beaumont", "license": "MIT", - "main": "src/index.ts", + "main": "src/v2.ts", "scripts": { "test": "echo 'No tests yet.' >&2 && exit 1" }, "dependencies": { - "@tanstack/react-query": "^4.36.1" + "@tanstack/react-query": "^5.59" }, "devDependencies": { "@types/react": "^18.3.11", "react": "^18.3.1", - "@oscartbeaumont-sd/rspc-client": "workspace:*" + "@spacedrive/rspc-client": "workspace:*" }, "peerDependencies": { "react": "^18.3.1", - "@oscartbeaumont-sd/rspc-client": "^0.1.3" + "@spacedrive/rspc-client": "^1.0" } } diff --git a/packages/react/src/index.tsx b/packages/react/src/index.tsx deleted file mode 100644 index 51b66ea4..00000000 --- a/packages/react/src/index.tsx +++ /dev/null @@ -1,333 +0,0 @@ -import { - _inferInfiniteQueryProcedureHandlerInput, - _inferProcedureHandlerInput, - Client, - inferInfiniteQueries, - inferInfiniteQueryResult, - inferMutationInput, - inferMutationResult, - inferProcedures, - inferQueryInput, - inferQueryResult, - inferSubscriptionResult, - ProcedureDef, - ProceduresDef, - ProceduresLike, - RSPCError, -} from '@oscartbeaumont-sd/rspc-client' -import { - useInfiniteQuery as __useInfiniteQuery, - useMutation as __useMutation, - useQuery as __useQuery, - hashQueryKey, - QueryClient, - QueryClientProvider, - UseInfiniteQueryOptions, - UseInfiniteQueryResult, - UseMutationOptions, - UseMutationResult, - UseQueryOptions, - UseQueryResult, -} from '@tanstack/react-query' -import React, { useContext as _useContext, createContext, ReactElement, useEffect } from 'react' - -export interface BaseOptions { - rspc?: { - client?: Client - } -} - -export interface SubscriptionOptions { - enabled?: boolean - onStarted?: () => void - onData: (data: TOutput) => void - onError?: (err: RSPCError) => void -} - -export interface Context { - client: Client - queryClient: QueryClient -} - -export function createReactQueryHooks() { - type TProcedures = inferProcedures - type TBaseOptions = BaseOptions - - const Context = createContext>(undefined!) - - function useContext() { - const ctx = _useContext(Context) - if (ctx?.queryClient === undefined) - throw new Error( - 'The rspc context has not been set. Ensure you have the component higher up in your component tree.' - ) - return ctx - } - - type CustomQueryHookReturn = < - K extends TConstrainedProcedures['key'] & string, - TQueryFnData = Extract['result'], - TData = Extract['result'], - >( - keyAndInput: [ - key: K, - ...input: Extract['input'] extends never | null - ? [] - : [Extract['input']], - ], - opts?: Omit< - UseQueryOptions< - TQueryFnData, - RSPCError, - TData, - [K, Extract['input']] - >, - 'queryKey' | 'queryFn' - > & - TBaseOptions - ) => UseQueryResult - - /* - [UNDOCUMENTED]: This function IS NOT and will probably never be completely type safe. It is for people doing crazy stuff on top of rspc. - By using it you accept the risk involved with a lack of type safety. If you can make this more typesafe a PR would be welcome! - */ - function customQuery( - map: ( - key: [key: TConstrainedProcedures['key'], ...input: TConstrainedProcedures['input']] - ) => [ - inferProcedures['queries']['key'] & string, - inferProcedures['queries']['input'], - ] - ): CustomQueryHookReturn { - return (keyAndInput, opts) => { - const { rspc, ...rawOpts } = opts ?? {} - let client = rspc?.client - if (!client) { - client = useContext().client - } - - return __useQuery({ - queryKey: map(keyAndInput as any), - queryFn: async () => { - return await client!.query(map(keyAndInput as any) as any) - }, - ...(rawOpts as any), - }) - } - } - - function useQuery< - K extends inferProcedures['queries']['key'] & string, - TQueryFnData = inferQueryResult, - TData = inferQueryResult, - >( - keyAndInput: [key: K, ...input: _inferProcedureHandlerInput], - opts?: Omit< - UseQueryOptions]>, - 'queryKey' | 'queryFn' - > & - TBaseOptions - ): UseQueryResult { - const { rspc, ...rawOpts } = opts ?? {} - let client = rspc?.client - if (!client) { - client = useContext().client - } - - return __useQuery( - keyAndInput, - async () => { - return await client!.query(keyAndInput) - }, - rawOpts as any - ) - } - - function useInfiniteQuery['key'] & string>( - keyAndInput: [key: K, ...input: _inferInfiniteQueryProcedureHandlerInput], - opts?: Omit< - UseInfiniteQueryOptions< - inferInfiniteQueryResult, - RSPCError, - inferInfiniteQueryResult, - inferInfiniteQueryResult, - [K, inferQueryInput] - >, - 'queryKey' | 'queryFn' - > & - TBaseOptions - ): UseInfiniteQueryResult, RSPCError> { - const { rspc, ...rawOpts } = opts ?? {} - let client = rspc?.client - if (!client) { - client = useContext().client - } - - return __useInfiniteQuery({ - queryKey: keyAndInput, - queryFn: async () => { - throw new Error('TODO') // TODO: Finish this - }, - ...(rawOpts as any), - }) - } - - type CustomMutationHookReturn = < - K extends TConstrainedProcedures['key'] & string, - TContext = unknown, - >( - key: K | [K], - opts?: UseMutationOptions< - Extract['result'], - RSPCError, - Extract['result'] extends never - ? undefined - : Extract['result'], - TContext - > & - TBaseOptions - ) => UseMutationResult< - Extract['result'], - RSPCError, - Extract['input'] extends never - ? undefined - : Extract['input'], - TContext - > - - /* - [UNDOCUMENTED]: This function IS NOT and will probably never be completely type safe. It is for people doing crazy stuff on top of rspc. - By using it you accept the risk involved with a lack of type safety. If you can make this more typesafe a PR would be welcome! - */ - function customMutation( - map: ( - key: [key: TConstrainedProcedures['key'], ...input: [TConstrainedProcedures['input']]] - ) => [ - inferProcedures['mutations']['key'] & string, - inferProcedures['mutations']['input'], - ] - ): CustomMutationHookReturn { - return (key, opts) => { - const { rspc, ...rawOpts } = opts ?? {} - let client = rspc?.client - if (!client) { - client = useContext().client - } - - return __useMutation({ - mutationFn: async input => { - const actualKey = Array.isArray(key) ? key[0] : key - return client!.mutation(map([actualKey, input]) as any) - }, - ...(rawOpts as any), - }) - } - } - - function useMutation( - key: K | [K], - opts?: UseMutationOptions< - inferMutationResult, - RSPCError, - inferMutationInput extends never - ? undefined - : inferMutationInput, - TContext - > & - TBaseOptions - ): UseMutationResult< - inferMutationResult, - RSPCError, - inferMutationInput extends never - ? undefined - : inferMutationInput, - TContext - > { - const { rspc, ...rawOpts } = opts ?? {} - let client = rspc?.client - if (!client) { - client = useContext().client - } - - return __useMutation(async (input: any) => { - const actualKey = Array.isArray(key) ? key[0] : key - return client!.mutation([actualKey, input] as any) - }, rawOpts as any) - } - - function useSubscription< - K extends TProcedures['subscriptions']['key'] & string, - TData = inferSubscriptionResult, - >( - keyAndInput: [key: K, ...input: _inferProcedureHandlerInput], - opts: SubscriptionOptions & TBaseOptions - ) { - let client = opts?.rspc?.client - if (!client) { - client = useContext().client - } - const queryKey = hashQueryKey(keyAndInput) - - const enabled = opts?.enabled ?? true - - return useEffect(() => { - if (!enabled) { - return - } - let isStopped = false - // @ts-ignore // TODO: Fix this - const unsubscribe = client!.addSubscription(keyAndInput as any, { - onStarted: () => { - if (!isStopped) { - opts.onStarted?.() - } - }, - // @ts-ignore// TODO: Fix this - onData: data => { - if (!isStopped) { - opts.onData(data) - } - }, - // @ts-ignore // TODO: Fix this - onError: err => { - if (!isStopped) { - opts.onError?.(err) - } - }, - }) - return () => { - isStopped = true - unsubscribe() - } - }, [queryKey, enabled]) - } - - return { - _rspc_def: undefined! as TProcedures, // This allows inferring the operations type from TS helpers - Provider: ({ - children, - client, - queryClient, - }: { - children?: ReactElement - client: Client - queryClient: QueryClient - }) => ( - - {children} - - ), - useContext, - customQuery, - useQuery, - // useInfiniteQuery, - customMutation, - useMutation, - useSubscription, - } -} diff --git a/packages/react/src/v2.tsx b/packages/react/src/v2.tsx index 876c65ee..15fc2088 100644 --- a/packages/react/src/v2.tsx +++ b/packages/react/src/v2.tsx @@ -1,34 +1,32 @@ -import { +import type { _inferInfiniteQueryProcedureHandlerInput, _inferProcedureHandlerInput, - inferInfiniteQueries, - inferInfiniteQueryResult, inferMutationInput, inferMutationResult, inferProcedureResult, inferQueryInput, inferQueryResult, + KeyAndInput, ProceduresDef, -} from '@oscartbeaumont-sd/rspc-client' -import { AlphaClient, AlphaRSPCError } from '@oscartbeaumont-sd/rspc-client/src/v2' +} from '@spacedrive/rspc-client' +import type { + UseMutationOptions, + UseMutationResult, + UseQueryOptions, + UseQueryResult, +} from '@tanstack/react-query' +import type { ReactElement } from 'react' + +import { AlphaClient, RSPCError } from '@spacedrive/rspc-client' import { useInfiniteQuery as __useInfiniteQuery, useMutation as __useMutation, useQuery as __useQuery, - hashQueryKey, + hashKey, QueryClient, QueryClientProvider, - UseInfiniteQueryOptions, - UseInfiniteQueryResult, - UseMutationOptions, - UseMutationResult, - UseQueryOptions, - UseQueryResult, } from '@tanstack/react-query' -import React, { useContext as _useContext, createContext, ReactElement, useEffect } from 'react' - -// TODO: Reuse one from client but don't export it in public API -type KeyAndInput = [string] | [string, any] +import React, { useContext as _useContext, createContext, useEffect, useMemo } from 'react' export interface BaseOptions { rspc?: { @@ -40,8 +38,7 @@ export interface SubscriptionOptions { enabled?: boolean onStarted?: () => void onData: (data: TOutput) => void - // TODO: Not `| Error` - onError?: (err: AlphaRSPCError | Error) => void + onError?: (err: RSPCError | Error) => void } export interface Context { @@ -49,11 +46,16 @@ export interface Context { queryClient: QueryClient } -// TODO: Share with SolidJS hooks if possible? export type HooksOpts

= { context: React.Context> } +/** + * Creates React Query hooks for the given RSPC client. + * @param client - The RSPC client. + * @param opts - Optional hooks options. + * @returns An object containing the hooks and provider component. + */ export function createReactQueryHooks

( client: AlphaClient

, opts?: HooksOpts

@@ -61,12 +63,13 @@ export function createReactQueryHooks

( type TBaseOptions = BaseOptions

const mapQueryKey: (keyAndInput: KeyAndInput) => KeyAndInput = - (client as any).mapQueryKey || (x => x) - const Context = opts?.context || createContext>(undefined!) + client.mapQueryKey ?? ((x: KeyAndInput) => x) + const Context = + opts?.context ?? createContext>({ client, queryClient: new QueryClient() }) function useContext() { const ctx = _useContext(Context) - if (ctx?.queryClient === undefined) + if (ctx?.queryClient == null) throw new Error( 'The rspc context has not been set. Ensure you have the component higher up in your component tree.' ) @@ -80,80 +83,51 @@ export function createReactQueryHooks

( >( keyAndInput: [key: K, ...input: _inferProcedureHandlerInput], opts?: Omit< - UseQueryOptions]>, + UseQueryOptions]>, 'queryKey' | 'queryFn' > & TBaseOptions - ): UseQueryResult { + ): UseQueryResult { const { rspc, ...rawOpts } = opts ?? {} - let client = rspc?.client - if (!client) { - client = useContext().client - } + const client = rspc?.client ?? useContext().client return __useQuery({ - queryKey: mapQueryKey(keyAndInput as any) as any, + queryKey: mapQueryKey(keyAndInput) as [K, inferQueryInput], queryFn: async () => { - return await client!.query(keyAndInput) + return (await client.query(keyAndInput)) as TQueryFnData }, ...rawOpts, }) } - function useInfiniteQuery['key'] & string>( - keyAndInput: [key: K, ...input: _inferInfiniteQueryProcedureHandlerInput], - opts?: Omit< - UseInfiniteQueryOptions< - inferInfiniteQueryResult, - AlphaRSPCError, - inferInfiniteQueryResult, - inferInfiniteQueryResult, - [K, inferQueryInput] - >, - 'queryKey' | 'queryFn' - > & - TBaseOptions - ): UseInfiniteQueryResult, AlphaRSPCError> { - const { rspc, ...rawOpts } = opts ?? {} - let client = rspc?.client - if (!client) { - client = useContext().client - } - - return __useInfiniteQuery({ - queryKey: mapQueryKey(keyAndInput as any), - queryFn: async () => { - throw new Error('TODO') // TODO: Finish this - }, - ...(rawOpts as any), - }) - } - function useMutation( key: K | [K], opts?: UseMutationOptions< inferMutationResult, - AlphaRSPCError, + RSPCError, inferMutationInput extends never ? undefined : inferMutationInput, TContext > & TBaseOptions ): UseMutationResult< inferMutationResult, - AlphaRSPCError, + RSPCError, inferMutationInput extends never ? undefined : inferMutationInput, TContext > { const { rspc, ...rawOpts } = opts ?? {} - let client = rspc?.client - if (!client) { - client = useContext().client - } - - return __useMutation(async (input: any) => { - const actualKey = Array.isArray(key) ? key[0] : key - return client!.mutation([actualKey, input] as any) - }, rawOpts as any) + const client = rspc?.client ?? useContext().client + + return __useMutation({ + mutationFn: async (...input: inferMutationInput[]) => { + const actualKey = Array.isArray(key) ? key[0] : key + return client.mutation([ + actualKey, + ...(input as _inferProcedureHandlerInput), + ]) + }, + ...rawOpts, + }) } function useSubscription< @@ -163,47 +137,43 @@ export function createReactQueryHooks

( keyAndInput: [key: K, ...input: _inferProcedureHandlerInput], opts: SubscriptionOptions & TBaseOptions ) { - let client = opts?.rspc?.client - if (!client) { - client = useContext().client - } - const queryKey = hashQueryKey(keyAndInput) + const client = opts?.rspc?.client ?? useContext().client + const queryKey = hashKey(keyAndInput) const enabled = opts?.enabled ?? true - return useEffect(() => { + useEffect(() => { if (!enabled) { return } - return client!.addSubscription(keyAndInput, { + const subscription = client.addSubscription(keyAndInput, { onData: opts.onData, onError: opts.onError, }) + return () => { + subscription.then(unsubscribe => unsubscribe()) + } }, [queryKey, enabled]) } - return { - _rspc_def: undefined! as P, // This allows inferring the operations type from TS helpers - Provider: ({ - children, - client, - queryClient, - }: { - children?: ReactElement - client: AlphaClient

- queryClient: QueryClient - }) => ( - + const Provider: React.FC<{ + children?: ReactElement + client: AlphaClient

+ queryClient: QueryClient + }> = ({ children, client, queryClient }) => { + const contextValue = useMemo(() => ({ client, queryClient }), [client, queryClient]) + + return ( + {children} - ), + ) + } + + return { + _rspc_def: {} as P, // This allows inferring the operations type from TS helpers + Provider, useContext, useQuery, - // useInfiniteQuery, useMutation, useSubscription, } diff --git a/packages/tauri/package.json b/packages/tauri/package.json index d84f59d7..4f676b0a 100644 --- a/packages/tauri/package.json +++ b/packages/tauri/package.json @@ -1,18 +1,18 @@ { - "name": "@oscartbeaumont-sd/rspc-tauri", - "version": "0.1.3", + "name": "@spacedrive/rspc-tauri", + "version": "0.2.0", "description": "A blazingly fast and easy to use TRPC-like server for Rust.", "keywords": [], "author": "Oscar Beaumont", "license": "MIT", - "main": "src/index.ts", + "main": "src/v2.ts", "dependencies": { "@tauri-apps/api": "^2.0.1" }, "devDependencies": { - "@oscartbeaumont-sd/rspc-client": "workspace:*" + "@spacedrive/rspc-client": "workspace:*" }, "peerDependencies": { - "@oscartbeaumont-sd/rspc-client": "^0.1.3" + "@spacedrive/rspc-client": "^1.0" } } diff --git a/packages/tauri/src/index.ts b/packages/tauri/src/index.ts deleted file mode 100644 index 86974cb4..00000000 --- a/packages/tauri/src/index.ts +++ /dev/null @@ -1,67 +0,0 @@ -// @ts-nocheck No one asked - -import { OperationType, randomId, RSPCError, Transport } from '@oscartbeaumont-sd/rspc-client' -import { listen, UnlistenFn } from '@tauri-apps/api/event' -import { Window } from '@tauri-apps/api/window' - -export class TauriTransport implements Transport { - private requestMap = new Map void>() - private listener?: Promise - clientSubscriptionCallback?: (id: string, value: any) => void - - constructor() { - this.listener = listen('plugin:rspc:transport:resp', event => { - const { id, result } = event.payload as any - if (result.type === 'event') { - if (this.clientSubscriptionCallback) this.clientSubscriptionCallback(id, result.data) - } else if (result.type === 'response') { - if (this.requestMap.has(id)) { - this.requestMap.get(id)?.({ type: 'response', result: result.data }) - this.requestMap.delete(id) - } - } else if (result.type === 'error') { - const { message, code } = result.data - if (this.requestMap.has(id)) { - this.requestMap.get(id)?.({ type: 'error', message, code }) - this.requestMap.delete(id) - } - } else { - console.error(`Received event of unknown method '${result.type}'`) - } - }) - } - - async doRequest(operation: OperationType, key: string, input: any): Promise { - if (!this.listener) { - await this.listener - } - - const id = randomId() - let resolve: (data: any) => void - const promise = new Promise(res => { - resolve = res - }) - - // @ts-ignore - this.requestMap.set(id, resolve) - - await Window.getCurrent().emit('plugin:rspc:transport', { - id, - method: operation, - params: { - path: key, - input, - }, - }) - - const body = (await promise) as any - if (body.type === 'error') { - const { code, message } = body - throw new RSPCError(code, message) - } else if (body.type === 'response') { - return body.result - } else { - throw new Error(`RSPC Tauri doRequest received invalid body type '${body?.type}'`) - } - } -} diff --git a/packages/tauri/src/v2.ts b/packages/tauri/src/v2.ts index 5c735bb0..4bcceef7 100644 --- a/packages/tauri/src/v2.ts +++ b/packages/tauri/src/v2.ts @@ -1,6 +1,6 @@ -// @ts-nocheck No one asked +import type { Link, RspcRequest, RspcResponse } from '@spacedrive/rspc-client' -import { AlphaRSPCError, Link, RspcRequest } from '@oscartbeaumont-sd/rspc-client/src/v2' +import { RSPCError } from '@spacedrive/rspc-client' import { listen } from '@tauri-apps/api/event' import { Window } from '@tauri-apps/api/window' @@ -9,15 +9,14 @@ import { Window } from '@tauri-apps/api/window' */ export function tauriLink(): Link { const activeMap = new Map< - string, + string | number, { - resolve: (result: any) => void - reject: (error: Error | AlphaRSPCError) => void + resolve: (result: unknown) => void + reject: (error: Error | RSPCError) => void } >() - // @ts-ignore-error - const listener = listen('plugin:rspc:transport:resp', event => { - const { id, result } = event.payload as any + const listener = listen('plugin:rspc:transport:resp', event => { + const { id, result } = event.payload if (activeMap.has(id)) { if (result.type === 'event') { activeMap.get(id)?.resolve(result.data) @@ -26,7 +25,7 @@ export function tauriLink(): Link { activeMap.delete(id) } else if (result.type === 'error') { const { message, code } = result.data - activeMap.get(id)?.reject(new AlphaRSPCError(code, message)) + activeMap.get(id)?.reject(new RSPCError(code, message)) activeMap.delete(id) } else { console.error(`rspc: received event of unknown type '${result.type}'`) @@ -39,21 +38,20 @@ export function tauriLink(): Link { const batch: RspcRequest[] = [] let batchQueued = false const queueBatch = () => { - if (!batchQueued) { - batchQueued = true - setTimeout(() => { - const currentBatch = [...batch] - batch.splice(0, batch.length) - batchQueued = false - ;(async () => { - if (!listener) { - await listener - } + if (batchQueued) return + batchQueued = true - await Window.getCurrent().emit('plugin:rspc:transport', currentBatch) - })() - }) - } + setTimeout(() => { + const currentBatch = [...batch] + // Reset the batch + batch.length = 0 + batchQueued = false + listener + .then(() => Window.getCurrent().emit('plugin:rspc:transport', currentBatch)) + .catch(err => { + console.error('Failed to emit to plugin:rspc:transport', err) + }) + }) } return ({ op }) => { @@ -65,15 +63,29 @@ export function tauriLink(): Link { reject, }) - // @ts-ignore-error - batch.push({ - id: op.id, - method: op.type, - params: { - path: op.path, - input: op.input, - }, - }) + if (op.type === 'subscriptionStop') { + if (op.input != null && typeof op.input !== 'string' && typeof op.input !== 'number') { + throw new Error( + `Expected 'input' to be of type 'string' or 'number' for 'subscriptionStop', but got ${typeof op.input}` + ) + } + batch.push({ + id: op.id, + method: op.type, + params: { + input: op.input ?? null, + }, + }) + } else { + batch.push({ + id: op.id, + method: op.type, + params: { + path: op.path, + input: op.input, + }, + }) + } queueBatch() }, abort() { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 317f8dd8..d0b295d1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,6 +5,7 @@ settings: excludeLinksFromLockfile: false importers: + .: devDependencies: '@ianvs/prettier-plugin-sort-imports': @@ -23,10 +24,10 @@ importers: packages/react: dependencies: '@tanstack/react-query': - specifier: ^4.36.1 - version: 4.36.1(react@18.3.1) + specifier: ^5.59 + version: 5.59.0(react@18.3.1) devDependencies: - '@oscartbeaumont-sd/rspc-client': + '@spacedrive/rspc-client': specifier: workspace:* version: link:../client '@types/react': @@ -42,145 +43,89 @@ importers: specifier: ^2.0.1 version: 2.0.1 devDependencies: - '@oscartbeaumont-sd/rspc-client': + '@spacedrive/rspc-client': specifier: workspace:* version: link:../client packages: + '@ampproject/remapping@2.3.0': - resolution: - { - integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==, - } - engines: { node: '>=6.0.0' } + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + engines: {node: '>=6.0.0'} '@babel/code-frame@7.25.7': - resolution: - { - integrity: sha512-0xZJFNE5XMpENsgfHYTw8FbX4kv53mFLn2i3XPoq69LyhYSCBJtitaHx9QnsVTrsogI4Z3+HtEfZ2/GFPOtf5g==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-0xZJFNE5XMpENsgfHYTw8FbX4kv53mFLn2i3XPoq69LyhYSCBJtitaHx9QnsVTrsogI4Z3+HtEfZ2/GFPOtf5g==} + engines: {node: '>=6.9.0'} '@babel/compat-data@7.25.7': - resolution: - { - integrity: sha512-9ickoLz+hcXCeh7jrcin+/SLWm+GkxE2kTvoYyp38p4WkdFXfQJxDFGWp/YHjiKLPx06z2A7W8XKuqbReXDzsw==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-9ickoLz+hcXCeh7jrcin+/SLWm+GkxE2kTvoYyp38p4WkdFXfQJxDFGWp/YHjiKLPx06z2A7W8XKuqbReXDzsw==} + engines: {node: '>=6.9.0'} '@babel/core@7.25.7': - resolution: - { - integrity: sha512-yJ474Zv3cwiSOO9nXJuqzvwEeM+chDuQ8GJirw+pZ91sCGCyOZ3dJkVE09fTV0VEVzXyLWhh3G/AolYTPX7Mow==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-yJ474Zv3cwiSOO9nXJuqzvwEeM+chDuQ8GJirw+pZ91sCGCyOZ3dJkVE09fTV0VEVzXyLWhh3G/AolYTPX7Mow==} + engines: {node: '>=6.9.0'} '@babel/generator@7.25.7': - resolution: - { - integrity: sha512-5Dqpl5fyV9pIAD62yK9P7fcA768uVPUyrQmqpqstHWgMma4feF1x/oFysBCVZLY5wJ2GkMUCdsNDnGZrPoR6rA==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-5Dqpl5fyV9pIAD62yK9P7fcA768uVPUyrQmqpqstHWgMma4feF1x/oFysBCVZLY5wJ2GkMUCdsNDnGZrPoR6rA==} + engines: {node: '>=6.9.0'} '@babel/helper-compilation-targets@7.25.7': - resolution: - { - integrity: sha512-DniTEax0sv6isaw6qSQSfV4gVRNtw2rte8HHM45t9ZR0xILaufBRNkpMifCRiAPyvL4ACD6v0gfCwCmtOQaV4A==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-DniTEax0sv6isaw6qSQSfV4gVRNtw2rte8HHM45t9ZR0xILaufBRNkpMifCRiAPyvL4ACD6v0gfCwCmtOQaV4A==} + engines: {node: '>=6.9.0'} '@babel/helper-module-imports@7.25.7': - resolution: - { - integrity: sha512-o0xCgpNmRohmnoWKQ0Ij8IdddjyBFE4T2kagL/x6M3+4zUgc+4qTOUBoNe4XxDskt1HPKO007ZPiMgLDq2s7Kw==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-o0xCgpNmRohmnoWKQ0Ij8IdddjyBFE4T2kagL/x6M3+4zUgc+4qTOUBoNe4XxDskt1HPKO007ZPiMgLDq2s7Kw==} + engines: {node: '>=6.9.0'} '@babel/helper-module-transforms@7.25.7': - resolution: - { - integrity: sha512-k/6f8dKG3yDz/qCwSM+RKovjMix563SLxQFo0UhRNo239SP6n9u5/eLtKD6EAjwta2JHJ49CsD8pms2HdNiMMQ==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-k/6f8dKG3yDz/qCwSM+RKovjMix563SLxQFo0UhRNo239SP6n9u5/eLtKD6EAjwta2JHJ49CsD8pms2HdNiMMQ==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 '@babel/helper-simple-access@7.25.7': - resolution: - { - integrity: sha512-FPGAkJmyoChQeM+ruBGIDyrT2tKfZJO8NcxdC+CWNJi7N8/rZpSxK7yvBJ5O/nF1gfu5KzN7VKG3YVSLFfRSxQ==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-FPGAkJmyoChQeM+ruBGIDyrT2tKfZJO8NcxdC+CWNJi7N8/rZpSxK7yvBJ5O/nF1gfu5KzN7VKG3YVSLFfRSxQ==} + engines: {node: '>=6.9.0'} '@babel/helper-string-parser@7.25.7': - resolution: - { - integrity: sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g==} + engines: {node: '>=6.9.0'} '@babel/helper-validator-identifier@7.25.7': - resolution: - { - integrity: sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==} + engines: {node: '>=6.9.0'} '@babel/helper-validator-option@7.25.7': - resolution: - { - integrity: sha512-ytbPLsm+GjArDYXJ8Ydr1c/KJuutjF2besPNbIZnZ6MKUxi/uTA22t2ymmA4WFjZFpjiAMO0xuuJPqK2nvDVfQ==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-ytbPLsm+GjArDYXJ8Ydr1c/KJuutjF2besPNbIZnZ6MKUxi/uTA22t2ymmA4WFjZFpjiAMO0xuuJPqK2nvDVfQ==} + engines: {node: '>=6.9.0'} '@babel/helpers@7.25.7': - resolution: - { - integrity: sha512-Sv6pASx7Esm38KQpF/U/OXLwPPrdGHNKoeblRxgZRLXnAtnkEe4ptJPDtAZM7fBLadbc1Q07kQpSiGQ0Jg6tRA==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-Sv6pASx7Esm38KQpF/U/OXLwPPrdGHNKoeblRxgZRLXnAtnkEe4ptJPDtAZM7fBLadbc1Q07kQpSiGQ0Jg6tRA==} + engines: {node: '>=6.9.0'} '@babel/highlight@7.25.7': - resolution: - { - integrity: sha512-iYyACpW3iW8Fw+ZybQK+drQre+ns/tKpXbNESfrhNnPLIklLbXr7MYJ6gPEd0iETGLOK+SxMjVvKb/ffmk+FEw==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-iYyACpW3iW8Fw+ZybQK+drQre+ns/tKpXbNESfrhNnPLIklLbXr7MYJ6gPEd0iETGLOK+SxMjVvKb/ffmk+FEw==} + engines: {node: '>=6.9.0'} '@babel/parser@7.25.7': - resolution: - { - integrity: sha512-aZn7ETtQsjjGG5HruveUK06cU3Hljuhd9Iojm4M8WWv3wLE6OkE5PWbDUkItmMgegmccaITudyuW5RPYrYlgWw==, - } - engines: { node: '>=6.0.0' } + resolution: {integrity: sha512-aZn7ETtQsjjGG5HruveUK06cU3Hljuhd9Iojm4M8WWv3wLE6OkE5PWbDUkItmMgegmccaITudyuW5RPYrYlgWw==} + engines: {node: '>=6.0.0'} hasBin: true '@babel/template@7.25.7': - resolution: - { - integrity: sha512-wRwtAgI3bAS+JGU2upWNL9lSlDcRCqD05BZ1n3X2ONLH1WilFP6O1otQjeMK/1g0pvYcXC7b/qVUB1keofjtZA==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-wRwtAgI3bAS+JGU2upWNL9lSlDcRCqD05BZ1n3X2ONLH1WilFP6O1otQjeMK/1g0pvYcXC7b/qVUB1keofjtZA==} + engines: {node: '>=6.9.0'} '@babel/traverse@7.25.7': - resolution: - { - integrity: sha512-jatJPT1Zjqvh/1FyJs6qAHL+Dzb7sTb+xr7Q+gM1b+1oBsMsQQ4FkVKb6dFlJvLlVssqkRzV05Jzervt9yhnzg==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-jatJPT1Zjqvh/1FyJs6qAHL+Dzb7sTb+xr7Q+gM1b+1oBsMsQQ4FkVKb6dFlJvLlVssqkRzV05Jzervt9yhnzg==} + engines: {node: '>=6.9.0'} '@babel/types@7.25.7': - resolution: - { - integrity: sha512-vwIVdXG+j+FOpkwqHRcBgHLYNL7XMkufrlaFvL9o6Ai9sJn9+PdyIL5qa0XzTZw084c+u9LOls53eoZWP/W5WQ==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-vwIVdXG+j+FOpkwqHRcBgHLYNL7XMkufrlaFvL9o6Ai9sJn9+PdyIL5qa0XzTZw084c+u9LOls53eoZWP/W5WQ==} + engines: {node: '>=6.9.0'} '@ianvs/prettier-plugin-sort-imports@4.3.1': - resolution: - { - integrity: sha512-ZHwbyjkANZOjaBm3ZosADD2OUYGFzQGxfy67HmGZU94mHqe7g1LCMA7YYKB1Cq+UTPCBqlAYapY0KXAjKEw8Sg==, - } + resolution: {integrity: sha512-ZHwbyjkANZOjaBm3ZosADD2OUYGFzQGxfy67HmGZU94mHqe7g1LCMA7YYKB1Cq+UTPCBqlAYapY0KXAjKEw8Sg==} peerDependencies: '@vue/compiler-sfc': 2.7.x || 3.x prettier: 2 || 3 @@ -189,135 +134,71 @@ packages: optional: true '@jridgewell/gen-mapping@0.3.5': - resolution: - { - integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==, - } - engines: { node: '>=6.0.0' } + resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} + engines: {node: '>=6.0.0'} '@jridgewell/resolve-uri@3.1.2': - resolution: - { - integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==, - } - engines: { node: '>=6.0.0' } + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} '@jridgewell/set-array@1.2.1': - resolution: - { - integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==, - } - engines: { node: '>=6.0.0' } + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} + engines: {node: '>=6.0.0'} '@jridgewell/sourcemap-codec@1.5.0': - resolution: - { - integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==, - } + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} '@jridgewell/trace-mapping@0.3.25': - resolution: - { - integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==, - } - - '@tanstack/query-core@4.36.1': - resolution: - { - integrity: sha512-DJSilV5+ytBP1FbFcEJovv4rnnm/CokuVvrBEtW/Va9DvuJ3HksbXUJEpI0aV1KtuL4ZoO9AVE6PyNLzF7tLeA==, - } - - '@tanstack/react-query@4.36.1': - resolution: - { - integrity: sha512-y7ySVHFyyQblPl3J3eQBWpXZkliroki3ARnBKsdJchlgt7yJLRDUcf4B8soufgiYt3pEQIkBWBx1N9/ZPIeUWw==, - } + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + + '@tanstack/query-core@5.59.0': + resolution: {integrity: sha512-WGD8uIhX6/deH/tkZqPNcRyAhDUqs729bWKoByYHSogcshXfFbppOdTER5+qY7mFvu8KEFJwT0nxr8RfPTVh0Q==} + + '@tanstack/react-query@5.59.0': + resolution: {integrity: sha512-YDXp3OORbYR+8HNQx+lf4F73NoiCmCcSvZvgxE29OifmQFk0sBlO26NWLHpcNERo92tVk3w+JQ53/vkcRUY1hA==} peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-native: '*' - peerDependenciesMeta: - react-dom: - optional: true - react-native: - optional: true + react: ^18 || ^19 '@tauri-apps/api@2.0.1': - resolution: - { - integrity: sha512-eoQWT+Tq1qSwQpHV+nw1eNYe5B/nm1PoRjQCRiEOS12I1b+X4PUcREfXVX8dPcBT6GrzWGDtaecY0+1p0Rfqlw==, - } + resolution: {integrity: sha512-eoQWT+Tq1qSwQpHV+nw1eNYe5B/nm1PoRjQCRiEOS12I1b+X4PUcREfXVX8dPcBT6GrzWGDtaecY0+1p0Rfqlw==} '@types/prop-types@15.7.13': - resolution: - { - integrity: sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==, - } + resolution: {integrity: sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==} '@types/react@18.3.11': - resolution: - { - integrity: sha512-r6QZ069rFTjrEYgFdOck1gK7FLVsgJE7tTz0pQBczlBNUhBNk0MQH4UbnFSwjpQLMkLzgqvBBa+qGpLje16eTQ==, - } + resolution: {integrity: sha512-r6QZ069rFTjrEYgFdOck1gK7FLVsgJE7tTz0pQBczlBNUhBNk0MQH4UbnFSwjpQLMkLzgqvBBa+qGpLje16eTQ==} ansi-styles@3.2.1: - resolution: - { - integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} browserslist@4.24.0: - resolution: - { - integrity: sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==, - } - engines: { node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7 } + resolution: {integrity: sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true caniuse-lite@1.0.30001667: - resolution: - { - integrity: sha512-7LTwJjcRkzKFmtqGsibMeuXmvFDfZq/nzIjnmgCGzKKRVzjD72selLDK1oPF/Oxzmt4fNcPvTDvGqSDG4tCALw==, - } + resolution: {integrity: sha512-7LTwJjcRkzKFmtqGsibMeuXmvFDfZq/nzIjnmgCGzKKRVzjD72selLDK1oPF/Oxzmt4fNcPvTDvGqSDG4tCALw==} chalk@2.4.2: - resolution: - { - integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} color-convert@1.9.3: - resolution: - { - integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==, - } + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} color-name@1.1.3: - resolution: - { - integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==, - } + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} convert-source-map@2.0.0: - resolution: - { - integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==, - } + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} csstype@3.1.3: - resolution: - { - integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==, - } + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} debug@4.3.7: - resolution: - { - integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==, - } - engines: { node: '>=6.0' } + resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} + engines: {node: '>=6.0'} peerDependencies: supports-color: '*' peerDependenciesMeta: @@ -325,174 +206,97 @@ packages: optional: true electron-to-chromium@1.5.32: - resolution: - { - integrity: sha512-M+7ph0VGBQqqpTT2YrabjNKSQ2fEl9PVx6AK3N558gDH9NO8O6XN9SXXFWRo9u9PbEg/bWq+tjXQr+eXmxubCw==, - } + resolution: {integrity: sha512-M+7ph0VGBQqqpTT2YrabjNKSQ2fEl9PVx6AK3N558gDH9NO8O6XN9SXXFWRo9u9PbEg/bWq+tjXQr+eXmxubCw==} escalade@3.2.0: - resolution: - { - integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} escape-string-regexp@1.0.5: - resolution: - { - integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==, - } - engines: { node: '>=0.8.0' } + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} gensync@1.0.0-beta.2: - resolution: - { - integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} globals@11.12.0: - resolution: - { - integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} has-flag@3.0.0: - resolution: - { - integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} js-tokens@4.0.0: - resolution: - { - integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==, - } + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} jsesc@3.0.2: - resolution: - { - integrity: sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==} + engines: {node: '>=6'} hasBin: true json5@2.2.3: - resolution: - { - integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} hasBin: true loose-envify@1.4.0: - resolution: - { - integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==, - } + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true lru-cache@5.1.1: - resolution: - { - integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==, - } + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} ms@2.1.3: - resolution: - { - integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==, - } + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} node-releases@2.0.18: - resolution: - { - integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==, - } + resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==} picocolors@1.1.0: - resolution: - { - integrity: sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==, - } + resolution: {integrity: sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==} prettier@3.3.3: - resolution: - { - integrity: sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==, - } - engines: { node: '>=14' } + resolution: {integrity: sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==} + engines: {node: '>=14'} hasBin: true react@18.3.1: - resolution: - { - integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} + engines: {node: '>=0.10.0'} semver@6.3.1: - resolution: - { - integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==, - } + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true semver@7.6.3: - resolution: - { - integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} + engines: {node: '>=10'} hasBin: true supports-color@5.5.0: - resolution: - { - integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} to-fast-properties@2.0.0: - resolution: - { - integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} + engines: {node: '>=4'} typescript@5.6.2: - resolution: - { - integrity: sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==, - } - engines: { node: '>=14.17' } + resolution: {integrity: sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==} + engines: {node: '>=14.17'} hasBin: true update-browserslist-db@1.1.1: - resolution: - { - integrity: sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==, - } + resolution: {integrity: sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' - use-sync-external-store@1.2.2: - resolution: - { - integrity: sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==, - } - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - ws@8.18.0: - resolution: - { - integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==, - } - engines: { node: '>=10.0.0' } + resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} + engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 utf-8-validate: '>=5.0.2' @@ -503,12 +307,10 @@ packages: optional: true yallist@3.1.1: - resolution: - { - integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==, - } + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} snapshots: + '@ampproject/remapping@2.3.0': dependencies: '@jridgewell/gen-mapping': 0.3.5 @@ -655,13 +457,12 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 - '@tanstack/query-core@4.36.1': {} + '@tanstack/query-core@5.59.0': {} - '@tanstack/react-query@4.36.1(react@18.3.1)': + '@tanstack/react-query@5.59.0(react@18.3.1)': dependencies: - '@tanstack/query-core': 4.36.1 + '@tanstack/query-core': 5.59.0 react: 18.3.1 - use-sync-external-store: 1.2.2(react@18.3.1) '@tauri-apps/api@2.0.1': {} @@ -761,10 +562,6 @@ snapshots: escalade: 3.2.0 picocolors: 1.1.0 - use-sync-external-store@1.2.2(react@18.3.1): - dependencies: - react: 18.3.1 - ws@8.18.0: {} yallist@3.1.1: {} diff --git a/src/router.rs b/src/router.rs index 9049507e..27c99dba 100644 --- a/src/router.rs +++ b/src/router.rs @@ -64,7 +64,7 @@ where if let Some(header) = &self.config.bindings_header { writeln!(file, "{}", header)?; } - writeln!(file, "// This file was generated by [rspc](https://github.com/oscartbeaumont/rspc). Do not edit this file manually.")?; + writeln!(file, "// This file was generated by [rspc](https://github.com/spacedriveapp/rspc). Do not edit this file manually.")?; let config = Typescript::default().bigint(BigIntExportBehavior::BigInt); diff --git a/tsconfig.json b/tsconfig.json index 0350e2f1..a1cddbe1 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,6 +2,7 @@ "compilerOptions": { // Enable latest features "lib": ["ESNext", "DOM"], + "jsx": "react", "target": "ESNext", "module": "ESNext", "moduleDetection": "force", @@ -37,9 +38,11 @@ }, "include": [ "**/*.js", + "**/*.jsx", "**/*.mjs", "**/*.cjs", "**/*.ts", + "**/*.tsx", "**/*.d.ts", "**/*.d.mts", "**/*.d.cts"