Installed @tanstack/powersync-db-collection 0.1.66. The same code is on main, which is at 0.1.67.
Describe the bug
Against @powersync/common 2.x, the collection breaks in two independent ways.
- Row types lose their columns. PowerSync 2.0 removed the public
columnMap getter from Table. ExtractedTableColumns, OptionalExtractedTable and AnyTableColumnType in packages/powersync-db-collection/src/helpers.ts all map over TTable['columnMap'], so every row type keeps only id. All three powerSyncCollectionOptions overloads are affected, including the two that take a schema.
- Sync never starts. PowerSync 2.x replaced the logger's
info, warn and error methods with a single log(record). The first line of start() in dist/esm/powersync.js calls database.logger.info(...), which throws. The .catch handler then calls database.logger.error(...) and throws again, so the original failure is lost. In 0.1.66 the collection calls logger.error 6 times, logger.info 4 times and logger.warn once.
An earlier version of this description called the break types-only. That is true of columnMap, but the logger break stops the collection from working at runtime too.
To Reproduce
Types, with @tanstack/powersync-db-collection@0.1.66, @tanstack/db@0.8.7, @powersync/common@2.2.1 and TypeScript 6.0.3:
import { column, Schema, Table, type AbstractPowerSyncDatabase } from '@powersync/common'
import { createCollection } from '@tanstack/db'
import { powerSyncCollectionOptions } from '@tanstack/powersync-db-collection'
declare const db: AbstractPowerSyncDatabase
const items = new Table({ name: column.text, created_at: column.text })
const schema = new Schema({ items })
const collection = createCollection(
powerSyncCollectionOptions({ database: db, table: schema.props.items }),
)
collection.insert({ id: '1', name: 'squat', created_at: new Date().toISOString() })
error TS2353: Object literal may only specify known properties, and 'name' does not exist in type 'OptionalExtractedTable<Table<{ name: BaseColumnType<string | null>; created_at: BaseColumnType<string | null>; }>> | OptionalExtractedTable<...>[]'.
Reading rows fails the same way:
error TS2339: Property 'name' does not exist on type '{ id: string; readonly $synced: boolean; readonly $origin: VirtualOrigin; readonly $key: string; readonly $collectionId: string; }'.
Runtime: create the same collection over a PowerSyncDatabase from @powersync/react-native 2.2.1 on React Native 0.86.3. At startup:
Uncaught (in promise, id: 0) TypeError: undefined is not a function
The stack points at the .catch handler of start() in dist/esm/powersync.js, on the database.logger.error call.
Expected behavior
Row types include the table's columns and sync starts, as they do with @powersync/common 1.x.
Additional context
Table in @powersync/common 1.49.0 and 1.57.3:
export declare class Table<Columns extends ColumnsType = ColumnsType> {
protected _mappedColumns: Columns;
get columnMap(): Columns;
Table in @powersync/common 2.2.1, with no columnMap:
export declare class Table<Columns extends ColumnsType = ColumnsType> extends ResolvedTable {
protected _mappedColumns: Columns;
The logger in @powersync/common 2.2.1, lib/utils/Logger.d.ts:
export interface PowerSyncLogger {
log(record: LogRecord): void;
}
Possible fixes:
-
Read the columns from the class's type parameter, which both major versions declare:
type TableColumns<TTable extends Table> = TTable extends Table<infer Columns> ? Columns : never
export type ExtractedTableColumns<TTable extends Table> = {
[K in keyof TableColumns<TTable>]: ExtractColumnValueType<TableColumns<TTable>[K]>
}
The same substitution would apply in OptionalExtractedTable and AnyTableColumnType. I checked that TableColumns resolves to exactly the declared column names against @powersync/common 2.2.1. I haven't compiled it against 1.x, although the type parameter is declared the same way there.
-
Log through database.logger.log({ level: LogLevels.error, message, error }), using the LogLevels that @powersync/common 2.x exports.
Separately, the peer range @powersync/common: ^1.41.0 excludes 2.x, so pnpm reports it as unmet.
Workarounds in the meantime, for the types:
declare module '@powersync/common' {
interface Table<Columns extends ColumnsType = ColumnsType> {
readonly columnMap: Columns
}
}
And for the runtime, a logger with both shapes, passed to the database:
import { createConsoleLogger, LogLevels, type PowerSyncLogger } from '@powersync/common'
const base = createConsoleLogger({ prefix: 'PowerSync' })
const logger: PowerSyncLogger & {
info: (message: string, error?: unknown) => void
warn: (message: string, error?: unknown) => void
error: (message: string, error?: unknown) => void
} = {
log: (record) => base.log(record),
info: (message, error) => base.log({ level: LogLevels.info, message, error }),
warn: (message, error) => base.log({ level: LogLevels.warn, message, error }),
error: (message, error) => base.log({ level: LogLevels.error, message, error }),
}
new PowerSyncDatabase({ schema, logger, database: { dbFilename: 'app.db' } })
Environment: @tanstack/powersync-db-collection 0.1.66, @tanstack/db 0.8.7, @tanstack/react-db 0.3.7, @powersync/common 2.2.1, @powersync/react-native 2.2.1, @powersync/web 2.3.1, TypeScript 6.0.3, Expo SDK 57 with React Native 0.86.3.
Installed
@tanstack/powersync-db-collection0.1.66. The same code is onmain, which is at 0.1.67.Describe the bug
Against
@powersync/common2.x, the collection breaks in two independent ways.columnMapgetter fromTable.ExtractedTableColumns,OptionalExtractedTableandAnyTableColumnTypeinpackages/powersync-db-collection/src/helpers.tsall map overTTable['columnMap'], so every row type keeps onlyid. All threepowerSyncCollectionOptionsoverloads are affected, including the two that take aschema.info,warnanderrormethods with a singlelog(record). The first line ofstart()indist/esm/powersync.jscallsdatabase.logger.info(...), which throws. The.catchhandler then callsdatabase.logger.error(...)and throws again, so the original failure is lost. In 0.1.66 the collection callslogger.error6 times,logger.info4 times andlogger.warnonce.An earlier version of this description called the break types-only. That is true of
columnMap, but the logger break stops the collection from working at runtime too.To Reproduce
Types, with
@tanstack/powersync-db-collection@0.1.66,@tanstack/db@0.8.7,@powersync/common@2.2.1and TypeScript 6.0.3:Reading rows fails the same way:
Runtime: create the same collection over a
PowerSyncDatabasefrom@powersync/react-native2.2.1 on React Native 0.86.3. At startup:The stack points at the
.catchhandler ofstart()indist/esm/powersync.js, on thedatabase.logger.errorcall.Expected behavior
Row types include the table's columns and sync starts, as they do with
@powersync/common1.x.Additional context
Tablein@powersync/common1.49.0 and 1.57.3:Tablein@powersync/common2.2.1, with nocolumnMap:The logger in
@powersync/common2.2.1,lib/utils/Logger.d.ts:Possible fixes:
Read the columns from the class's type parameter, which both major versions declare:
The same substitution would apply in
OptionalExtractedTableandAnyTableColumnType. I checked thatTableColumnsresolves to exactly the declared column names against@powersync/common2.2.1. I haven't compiled it against 1.x, although the type parameter is declared the same way there.Log through
database.logger.log({ level: LogLevels.error, message, error }), using theLogLevelsthat@powersync/common2.x exports.Separately, the peer range
@powersync/common: ^1.41.0excludes 2.x, so pnpm reports it as unmet.Workarounds in the meantime, for the types:
And for the runtime, a logger with both shapes, passed to the database:
Environment:
@tanstack/powersync-db-collection0.1.66,@tanstack/db0.8.7,@tanstack/react-db0.3.7,@powersync/common2.2.1,@powersync/react-native2.2.1,@powersync/web2.3.1, TypeScript 6.0.3, Expo SDK 57 with React Native 0.86.3.