{"version":3,"file":"TransactionSerializer.cjs","sources":["../../../src/outbox/TransactionSerializer.ts"],"sourcesContent":["import type {\n  OfflineTransaction,\n  SerializedError,\n  SerializedMutation,\n  SerializedOfflineTransaction,\n} from '../types'\nimport type { Collection, PendingMutation } from '@tanstack/db'\n\nfunction setDataProperty(\n  object: Record<string, unknown>,\n  key: string,\n  value: unknown,\n): void {\n  if (key !== `__proto__`) object[key] = value\n  else\n    Object.defineProperty(object, key, {\n      value,\n      enumerable: true,\n      configurable: true,\n      writable: true,\n    })\n}\n\nexport class TransactionSerializer {\n  private collections: Record<string, Collection<any, any, any, any, any>>\n  private collectionIdToKey: Map<string, string>\n\n  constructor(\n    collections: Record<string, Collection<any, any, any, any, any>>,\n  ) {\n    this.collections = collections\n    // Create reverse lookup from collection.id to registry key\n    this.collectionIdToKey = new Map()\n    for (const [key, collection] of Object.entries(collections)) {\n      this.collectionIdToKey.set(collection.id, key)\n    }\n  }\n\n  serialize(transaction: OfflineTransaction): string {\n    const serialized: SerializedOfflineTransaction = {\n      ...transaction,\n      valueEncoding: 2,\n      createdAt: transaction.createdAt.toISOString(),\n      mutations: transaction.mutations.map((mutation) =>\n        this.serializeMutation(mutation),\n      ),\n    }\n    return JSON.stringify(serialized)\n  }\n\n  deserialize(data: string): OfflineTransaction {\n    // Old records retain their Date-marker meaning. New records also escape\n    // marker-shaped user objects; the encoding tag is not application metadata.\n    const {\n      valueEncoding,\n      ...parsed\n    }: Omit<SerializedOfflineTransaction, `valueEncoding`> & {\n      valueEncoding?: unknown\n    } = JSON.parse(data)\n    if (valueEncoding !== undefined && valueEncoding !== 2) {\n      throw new Error(\n        `Unsupported transaction value encoding: ${valueEncoding}`,\n      )\n    }\n\n    const createdAt = new Date(parsed.createdAt)\n    if (isNaN(createdAt.getTime())) {\n      throw new Error(\n        `Failed to deserialize transaction: invalid createdAt value \"${parsed.createdAt}\"`,\n      )\n    }\n\n    return {\n      ...parsed,\n      createdAt,\n      mutations: parsed.mutations.map((mutationData) =>\n        this.deserializeMutation(mutationData, valueEncoding === 2),\n      ),\n    }\n  }\n\n  private serializeMutation(mutation: PendingMutation): SerializedMutation {\n    const registryKey = this.collectionIdToKey.get(mutation.collection.id)\n    if (!registryKey) {\n      throw new Error(\n        `Collection with id ${mutation.collection.id} not found in registry`,\n      )\n    }\n\n    return {\n      globalKey: mutation.globalKey,\n      type: mutation.type,\n      modified: this.serializeValue(mutation.modified),\n      original: this.serializeValue(mutation.original),\n      changes: this.serializeValue(mutation.changes),\n      collectionId: registryKey, // Store registry key instead of collection.id\n    }\n  }\n\n  private deserializeMutation(\n    data: SerializedMutation,\n    escapedObjects: boolean,\n  ): PendingMutation {\n    const collection = this.collections[data.collectionId]\n    if (!collection) {\n      throw new Error(`Collection with id ${data.collectionId} not found`)\n    }\n\n    const modified = this.deserializeValue(data.modified, escapedObjects)\n\n    // Extract the key from the modified data using the collection's getKey function\n    // This is needed for optimistic state restoration to work correctly\n    const key = modified ? collection.getKeyFromItem(modified) : null\n\n    // Create a partial PendingMutation - we can't fully reconstruct it but\n    // we provide what we can. The executor will need to handle the rest.\n    return {\n      globalKey: data.globalKey,\n      type: data.type as any,\n      modified,\n      original: this.deserializeValue(data.original, escapedObjects),\n      changes: this.deserializeValue(data.changes, escapedObjects) ?? {},\n      collection,\n      // These fields would need to be reconstructed by the executor\n      mutationId: ``, // Will be regenerated\n      key,\n      metadata: undefined,\n      syncMetadata: {},\n      optimistic: true,\n      createdAt: new Date(),\n      updatedAt: new Date(),\n    } as PendingMutation\n  }\n\n  private serializeValue(value: any): any {\n    if (value === null || value === undefined) {\n      return value\n    }\n\n    if (value instanceof Date) {\n      return { __type: `Date`, value: value.toISOString() }\n    }\n\n    if (typeof value === `object`) {\n      const result: any = Array.isArray(value) ? [] : {}\n      for (const key in value) {\n        if (Object.prototype.hasOwnProperty.call(value, key)) {\n          setDataProperty(result, key, this.serializeValue(value[key]))\n        }\n      }\n      return !Array.isArray(value) &&\n        Object.prototype.hasOwnProperty.call(value, `__type`)\n        ? { __type: `Object`, value: result }\n        : result\n    }\n\n    return value\n  }\n\n  private deserializeValue(value: any, escapedObjects: boolean): any {\n    if (value === null || value === undefined) {\n      return value\n    }\n\n    if (typeof value === `object` && value.__type === `Date`) {\n      if (value.value === undefined || value.value === null) {\n        throw new Error(`Corrupted Date marker: missing value field`)\n      }\n      const date = new Date(value.value)\n      if (isNaN(date.getTime())) {\n        throw new Error(\n          `Failed to deserialize Date marker: invalid date value \"${value.value}\"`,\n        )\n      }\n      return date\n    }\n\n    if (typeof value === `object`) {\n      // Unwrap once, then decode only the fields: the object's own __type is data.\n      if (escapedObjects && value.__type === `Object`) {\n        if (\n          value.value === null ||\n          typeof value.value !== `object` ||\n          Array.isArray(value.value)\n        ) {\n          throw new Error(`Corrupted Object marker: expected an object value`)\n        }\n        value = value.value\n      }\n      const result: any = Array.isArray(value) ? [] : {}\n      for (const key in value) {\n        if (Object.prototype.hasOwnProperty.call(value, key)) {\n          setDataProperty(\n            result,\n            key,\n            this.deserializeValue(value[key], escapedObjects),\n          )\n        }\n      }\n      return result\n    }\n\n    return value\n  }\n\n  serializeError(error: Error): SerializedError {\n    return {\n      name: error.name,\n      message: error.message,\n      stack: error.stack,\n    }\n  }\n\n  deserializeError(data: SerializedError): Error {\n    const error = new Error(data.message)\n    error.name = data.name\n    error.stack = data.stack\n    return error\n  }\n}\n"],"names":[],"mappings":"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"}