diff --git a/.changeset/fresh-yaks-lay.md b/.changeset/fresh-yaks-lay.md new file mode 100644 index 00000000..1e166281 --- /dev/null +++ b/.changeset/fresh-yaks-lay.md @@ -0,0 +1,6 @@ +--- +"@tanstack/electric-db-collection": patch +"@tanstack/db": patch +--- + +prefix logs and errors with collection id, when available diff --git a/packages/db/src/collection/change-events.ts b/packages/db/src/collection/change-events.ts index 505f4d97..7a0e41ac 100644 --- a/packages/db/src/collection/change-events.ts +++ b/packages/db/src/collection/change-events.ts @@ -20,7 +20,10 @@ import type { BasicExpression } from "../query/ir.js" export interface CollectionLike< T extends object = Record, TKey extends string | number = string | number, -> extends Pick, `get` | `has` | `entries` | `indexes`> {} +> extends Pick< + Collection, + `get` | `has` | `entries` | `indexes` | `id` + > {} /** * Returns the current state of the collection as an array of changes @@ -109,7 +112,7 @@ export function currentStateAsChanges< } catch (error) { // If anything goes wrong with the where clause, fall back to full scan console.warn( - `Error processing where clause, falling back to full scan:`, + `${collection.id ? `[${collection.id}] ` : ``}Error processing where clause, falling back to full scan:`, error ) diff --git a/packages/db/src/collection/lifecycle.ts b/packages/db/src/collection/lifecycle.ts index f425f4d6..71c09d02 100644 --- a/packages/db/src/collection/lifecycle.ts +++ b/packages/db/src/collection/lifecycle.ts @@ -111,7 +111,10 @@ export class CollectionLifecycleManager< if (newStatus === `ready` && !this.indexes.isIndexesResolved) { // Resolve indexes asynchronously without blocking this.indexes.resolveAllIndexes().catch((error) => { - console.warn(`Failed to resolve indexes:`, error) + console.warn( + `${this.config.id ? `[${this.config.id}] ` : ``}Failed to resolve indexes:`, + error + ) }) } diff --git a/packages/db/src/indexes/auto-index.ts b/packages/db/src/indexes/auto-index.ts index 84553d94..e06f95f0 100644 --- a/packages/db/src/indexes/auto-index.ts +++ b/packages/db/src/indexes/auto-index.ts @@ -58,7 +58,10 @@ export function ensureIndexForField< options: compareFn ? { compareFn, compareOptions } : {}, }) } catch (error) { - console.warn(`Failed to create auto-index for field "${fieldName}":`, error) + console.warn( + `${collection.id ? `[${collection.id}] ` : ``}Failed to create auto-index for field "${fieldName}":`, + error + ) } } diff --git a/packages/electric-db-collection/src/electric.ts b/packages/electric-db-collection/src/electric.ts index 3dcb54b6..bc26bf2e 100644 --- a/packages/electric-db-collection/src/electric.ts +++ b/packages/electric-db-collection/src/electric.ts @@ -176,6 +176,7 @@ export function electricCollectionOptions( const sync = createElectricSync(config.shapeOptions, { seenTxids, seenSnapshots, + collectionId: config.id, }) /** @@ -188,9 +189,12 @@ export function electricCollectionOptions( txId: Txid, timeout: number = 30000 ): Promise => { - debug(`awaitTxId called with txid %d`, txId) + debug( + `${config.id ? `[${config.id}] ` : ``}awaitTxId called with txid %d`, + txId + ) if (typeof txId !== `number`) { - throw new ExpectedNumberInAwaitTxIdError(typeof txId) + throw new ExpectedNumberInAwaitTxIdError(typeof txId, config.id) } // First check if the txid is in the seenTxids store @@ -207,12 +211,15 @@ export function electricCollectionOptions( const timeoutId = setTimeout(() => { unsubscribeSeenTxids() unsubscribeSeenSnapshots() - reject(new TimeoutWaitingForTxIdError(txId)) + reject(new TimeoutWaitingForTxIdError(txId, config.id)) }, timeout) const unsubscribeSeenTxids = seenTxids.subscribe(() => { if (seenTxids.state.has(txId)) { - debug(`awaitTxId found match for txid %o`, txId) + debug( + `${config.id ? `[${config.id}] ` : ``}awaitTxId found match for txid %o`, + txId + ) clearTimeout(timeoutId) unsubscribeSeenTxids() unsubscribeSeenSnapshots() @@ -226,7 +233,7 @@ export function electricCollectionOptions( ) if (visibleSnapshot) { debug( - `awaitTxId found match for txid %o in snapshot %o`, + `${config.id ? `[${config.id}] ` : ``}awaitTxId found match for txid %o in snapshot %o`, txId, visibleSnapshot ) @@ -249,7 +256,7 @@ export function electricCollectionOptions( const txid = handlerResult.txid if (!txid) { - throw new ElectricInsertHandlerMustReturnTxIdError() + throw new ElectricInsertHandlerMustReturnTxIdError(config.id) } // Handle both single txid and array of txids @@ -272,7 +279,7 @@ export function electricCollectionOptions( const txid = handlerResult.txid if (!txid) { - throw new ElectricUpdateHandlerMustReturnTxIdError() + throw new ElectricUpdateHandlerMustReturnTxIdError(config.id) } // Handle both single txid and array of txids @@ -290,7 +297,7 @@ export function electricCollectionOptions( ? async (params: DeleteMutationFnParams) => { const handlerResult = await config.onDelete!(params) if (!handlerResult.txid) { - throw new ElectricDeleteHandlerMustReturnTxIdError() + throw new ElectricDeleteHandlerMustReturnTxIdError(config.id) } // Handle both single txid and array of txids @@ -333,10 +340,10 @@ function createElectricSync>( options: { seenTxids: Store> seenSnapshots: Store> + collectionId?: string } ): SyncConfig { - const { seenTxids } = options - const { seenSnapshots } = options + const { seenTxids, seenSnapshots, collectionId } = options // Store for the relation schema information const relationSchema = new Store(undefined) @@ -445,7 +452,7 @@ function createElectricSync>( hasUpToDate = true } else if (isMustRefetchMessage(message)) { debug( - `Received must-refetch message, starting transaction with truncate` + `${collectionId ? `[${collectionId}] ` : ``}Received must-refetch message, starting transaction with truncate` ) // Start a transaction and truncate the collection @@ -475,7 +482,10 @@ function createElectricSync>( seenTxids.setState((currentTxids) => { const clonedSeen = new Set(currentTxids) if (newTxids.size > 0) { - debug(`new txids synced from pg %O`, Array.from(newTxids)) + debug( + `${collectionId ? `[${collectionId}] ` : ``}new txids synced from pg %O`, + Array.from(newTxids) + ) } newTxids.forEach((txid) => clonedSeen.add(txid)) newTxids.clear() @@ -486,7 +496,10 @@ function createElectricSync>( seenSnapshots.setState((currentSnapshots) => { const seen = [...currentSnapshots, ...newSnapshots] newSnapshots.forEach((snapshot) => - debug(`new snapshot synced from pg %o`, snapshot) + debug( + `${collectionId ? `[${collectionId}] ` : ``}new snapshot synced from pg %o`, + snapshot + ) ) newSnapshots.length = 0 return seen diff --git a/packages/electric-db-collection/src/errors.ts b/packages/electric-db-collection/src/errors.ts index 6d289668..da049d23 100644 --- a/packages/electric-db-collection/src/errors.ts +++ b/packages/electric-db-collection/src/errors.ts @@ -2,48 +2,51 @@ import { TanStackDBError } from "@tanstack/db" // Electric DB Collection Errors export class ElectricDBCollectionError extends TanStackDBError { - constructor(message: string) { - super(message) + constructor(message: string, collectionId?: string) { + super(`${collectionId ? `[${collectionId}] ` : ``}${message}`) this.name = `ElectricDBCollectionError` } } export class ExpectedNumberInAwaitTxIdError extends ElectricDBCollectionError { - constructor(txIdType: string) { - super(`Expected number in awaitTxId, received ${txIdType}`) + constructor(txIdType: string, collectionId?: string) { + super(`Expected number in awaitTxId, received ${txIdType}`, collectionId) this.name = `ExpectedNumberInAwaitTxIdError` } } export class TimeoutWaitingForTxIdError extends ElectricDBCollectionError { - constructor(txId: number) { - super(`Timeout waiting for txId: ${txId}`) + constructor(txId: number, collectionId?: string) { + super(`Timeout waiting for txId: ${txId}`, collectionId) this.name = `TimeoutWaitingForTxIdError` } } export class ElectricInsertHandlerMustReturnTxIdError extends ElectricDBCollectionError { - constructor() { + constructor(collectionId?: string) { super( - `Electric collection onInsert handler must return a txid or array of txids` + `Electric collection onInsert handler must return a txid or array of txids`, + collectionId ) this.name = `ElectricInsertHandlerMustReturnTxIdError` } } export class ElectricUpdateHandlerMustReturnTxIdError extends ElectricDBCollectionError { - constructor() { + constructor(collectionId?: string) { super( - `Electric collection onUpdate handler must return a txid or array of txids` + `Electric collection onUpdate handler must return a txid or array of txids`, + collectionId ) this.name = `ElectricUpdateHandlerMustReturnTxIdError` } } export class ElectricDeleteHandlerMustReturnTxIdError extends ElectricDBCollectionError { - constructor() { + constructor(collectionId?: string) { super( - `Electric collection onDelete handler must return a txid or array of txids` + `Electric collection onDelete handler must return a txid or array of txids`, + collectionId ) this.name = `ElectricDeleteHandlerMustReturnTxIdError` } diff --git a/packages/electric-db-collection/tests/electric.test.ts b/packages/electric-db-collection/tests/electric.test.ts index ba21e5c6..2e259bee 100644 --- a/packages/electric-db-collection/tests/electric.test.ts +++ b/packages/electric-db-collection/tests/electric.test.ts @@ -303,7 +303,7 @@ describe(`Electric Integration`, () => { // @ts-expect-error collection.utils.awaitTxId(`123`) ).rejects.toThrowErrorMatchingInlineSnapshot( - `[ExpectedNumberInAwaitTxIdError: Expected number in awaitTxId, received string]` + `[ExpectedNumberInAwaitTxIdError: [test] Expected number in awaitTxId, received string]` ) // The txid should be tracked and awaitTxId should resolve immediately