diff --git a/.changeset/nervous-ducks-develop.md b/.changeset/nervous-ducks-develop.md new file mode 100644 index 000000000..94443f7e0 --- /dev/null +++ b/.changeset/nervous-ducks-develop.md @@ -0,0 +1,5 @@ +--- +'@powersync/kysely-driver': minor +--- + +Made `dialect` in `wrapPowerSyncWithKysely` options optional since the method provides a PowerSync dialect by default. diff --git a/.changeset/orange-points-speak.md b/.changeset/orange-points-speak.md new file mode 100644 index 000000000..411b7fa13 --- /dev/null +++ b/.changeset/orange-points-speak.md @@ -0,0 +1,6 @@ +--- +'@powersync/react': patch +'@powersync/vue': patch +--- + +React and Vue helpers should execute queries from compatible query executor methods. This should allow Kysely queries with plugins to function correctly. diff --git a/packages/kysely-driver/src/sqlite/db.ts b/packages/kysely-driver/src/sqlite/db.ts index b89a082af..15e209a74 100644 --- a/packages/kysely-driver/src/sqlite/db.ts +++ b/packages/kysely-driver/src/sqlite/db.ts @@ -1,8 +1,15 @@ -import { PowerSyncDialect } from './sqlite-dialect'; -import { Kysely, type KyselyConfig } from 'kysely'; import { type AbstractPowerSyncDatabase } from '@powersync/common'; +import { Dialect, Kysely, type KyselyConfig } from 'kysely'; +import { PowerSyncDialect } from './sqlite-dialect'; + +/** + * An extension of {@link KyselyConfig} which uses the {@link PowerSyncDialect} by default. + */ +export type PowerSyncKyselyOptions = Omit & { + dialect?: Dialect; +}; -export const wrapPowerSyncWithKysely = (db: AbstractPowerSyncDatabase, options?: KyselyConfig) => { +export const wrapPowerSyncWithKysely = (db: AbstractPowerSyncDatabase, options?: PowerSyncKyselyOptions) => { return new Kysely({ dialect: new PowerSyncDialect({ db diff --git a/packages/react/src/hooks/useQuery.ts b/packages/react/src/hooks/useQuery.ts index 05cb4b9ba..54fc712b6 100644 --- a/packages/react/src/hooks/useQuery.ts +++ b/packages/react/src/hooks/useQuery.ts @@ -1,4 +1,4 @@ -import { type SQLWatchOptions, parseQuery, type CompilableQuery, type ParsedQuery } from '@powersync/common'; +import { parseQuery, type CompilableQuery, type ParsedQuery, type SQLWatchOptions } from '@powersync/common'; import React from 'react'; import { usePowerSync } from './PowerSyncContext'; @@ -85,7 +85,8 @@ export const useQuery = ( const fetchData = async () => { setIsFetching(true); try { - const result = await powerSync.getAll(sqlStatement, queryParameters); + const result = + typeof query == 'string' ? await powerSync.getAll(sqlStatement, queryParameters) : await query.execute(); handleResult(result); } catch (e) { console.error('Failed to fetch data:', e); diff --git a/packages/react/tests/useQuery.test.tsx b/packages/react/tests/useQuery.test.tsx index a7ea4d3ff..b6a5b7771 100644 --- a/packages/react/tests/useQuery.test.tsx +++ b/packages/react/tests/useQuery.test.tsx @@ -1,9 +1,9 @@ -import React from 'react'; +import * as commonSdk from '@powersync/common'; import { renderHook, waitFor } from '@testing-library/react'; -import { vi, describe, expect, it, afterEach } from 'vitest'; -import { useQuery } from '../src/hooks/useQuery'; +import React from 'react'; +import { afterEach, describe, expect, it, vi } from 'vitest'; import { PowerSyncContext } from '../src/hooks/PowerSyncContext'; -import * as commonSdk from '@powersync/common'; +import { useQuery } from '../src/hooks/useQuery'; const mockPowerSync = { currentStatus: { status: 'initial' }, @@ -156,6 +156,23 @@ describe('useQuery', () => { expect(currentResult.isLoading).toEqual(true); }); + it('should execute compatible queries', async () => { + const wrapper = ({ children }) => ( + {children} + ); + + const query = () => + useQuery({ + execute: () => [{ test: 'custom' }] as any, + compile: () => ({ sql: 'SELECT * from lists', parameters: [] }) + }); + const { result } = renderHook(query, { wrapper }); + + await vi.waitFor(() => { + expect(result.current.data[0]?.test).toEqual('custom'); + }); + }); + // The test returns unhandled errors when run with all the others. // TODO: Fix the test so that there are no unhandled errors (this may be a vitest or @testing-library/react issue) it.skip('should show an error if parsing the query results in an error', async () => { diff --git a/packages/vue/src/composables/useQuery.ts b/packages/vue/src/composables/useQuery.ts index b072fc9d2..dc4509309 100644 --- a/packages/vue/src/composables/useQuery.ts +++ b/packages/vue/src/composables/useQuery.ts @@ -1,4 +1,4 @@ -import { type SQLWatchOptions, parseQuery, type CompilableQuery, ParsedQuery } from '@powersync/common'; +import { type CompilableQuery, ParsedQuery, type SQLWatchOptions, parseQuery } from '@powersync/common'; import { type MaybeRef, type Ref, ref, toValue, watchEffect } from 'vue'; import { usePowerSync } from './powerSync'; @@ -87,10 +87,10 @@ export const useQuery = ( error.value = wrappedError; }; - const _fetchData = async (sql: string, parameters: any[]) => { + const _fetchData = async (executor: () => Promise) => { isFetching.value = true; try { - const result = await powerSync.value.getAll(sql, parameters); + const result = await executor(); handleResult(result); } catch (e) { console.error('Failed to fetch data:', e); @@ -104,8 +104,9 @@ export const useQuery = ( onCleanup(() => abortController.abort()); let parsedQuery: ParsedQuery; + const queryValue = toValue(query); try { - parsedQuery = parseQuery(toValue(query), toValue(sqlParameters).map(toValue)); + parsedQuery = parseQuery(queryValue, toValue(sqlParameters).map(toValue)); } catch (e) { console.error('Failed to parse query:', e); handleError(e); @@ -123,7 +124,9 @@ export const useQuery = ( return; } // Fetch initial data - fetchData = () => _fetchData(sql, parameters); + const executor = + typeof queryValue == 'string' ? () => powerSync.value.getAll(sql, parameters) : () => queryValue.execute(); + fetchData = () => _fetchData(executor); await fetchData(); if (options.runQueryOnce) { diff --git a/packages/vue/tests/useQuery.test.ts b/packages/vue/tests/useQuery.test.ts index c96dd5b40..ceac35e2f 100644 --- a/packages/vue/tests/useQuery.test.ts +++ b/packages/vue/tests/useQuery.test.ts @@ -144,6 +144,22 @@ describe('useQuery', () => { expect(isLoading.value).toEqual(false); }); + it('should execute compilable queries', async () => { + vi.spyOn(PowerSync, 'usePowerSync').mockReturnValue(ref(mockPowerSync) as any); + + const [{ isLoading, data }] = withSetup(() => + useQuery({ + execute: () => [{ test: 'custom' }] as any, + compile: () => ({ sql: 'SELECT * from lists', parameters: [] }) + }) + ); + + expect(isLoading.value).toEqual(true); + await flushPromises(); + expect(isLoading.value).toEqual(false); + expect(data.value[0].test).toEqual('custom'); + }); + it('should set error for compilable query on useQuery parameters', async () => { vi.spyOn(PowerSync, 'usePowerSync').mockReturnValue(ref(mockPowerSync) as any);