import type { Observable, BehaviorSubject } from '../utils/rx' import { Unsubscribe } from '../utils/subscriptions' import type { $RE, $ReadOnlyArray } from '../types' import type Database from '../Database' import type Collection from '../Collection' import type CollectionMap from '../Database/CollectionMap' import type { TableName, ColumnName } from '../Schema' import type { Value } from '../QueryDescription' import type { RawRecord, DirtyRaw } from '../RawRecord' export type RecordId = string // NOTE: status 'disposable' MUST NOT ever appear in a persisted record export type SyncStatus = 'synced' | 'created' | 'updated' | 'deleted' | 'disposable' export type BelongsToAssociation = $RE<{ type: 'belongs_to'; key: ColumnName }> export type HasManyAssociation = $RE<{ type: 'has_many'; foreignKey: ColumnName }> export type AssociationInfo = BelongsToAssociation | HasManyAssociation export type Associations = { [tableName: TableName]: AssociationInfo } export function associations(...associationList: [TableName, AssociationInfo][]): Associations export default class Model { // Set this in concrete Models to the name of the database table static table: TableName // Set this in concrete Models to define relationships between different records static associations: Associations // Used by withObservables to differentiate between object types static _wmelonTag: string _raw: RawRecord _isEditing: boolean _preparedState: null | 'create' | 'update' | 'markAsDeleted' | 'destroyPermanently' __changes?: BehaviorSubject _getChanges(): BehaviorSubject get id(): RecordId get syncStatus(): SyncStatus // Modifies the model (using passed function) and saves it to the database. // Touches `updatedAt` if available. // // Example: // someTask.update(task => { // task.name = 'New name' // }) update(recordUpdater?: (_: this) => void): Promise // Prepares an update to the database (using passed function). // Touches `updatedAt` if available. // // After preparing an update, you must execute it synchronously using // database.batch() prepareUpdate(recordUpdater?: (_: this) => void): this prepareMarkAsDeleted(): this prepareDestroyPermanently(): this // Marks this record as deleted (will be permanently deleted after sync) // Note: Use this only with Sync markAsDeleted(): Promise // Pernamently removes this record from the database // Note: Don't use this when using Sync destroyPermanently(): Promise experimentalMarkAsDeleted(): Promise experimentalDestroyPermanently(): Promise // *** Observing changes *** // Returns an observable that emits `this` upon subscription and every time this record changes // Emits `complete` if this record is destroyed observe(): Observable // *** Implementation details *** collection: Collection // Collections of other Models in the same domain as this record get collections(): CollectionMap get database(): Database get db(): Database get asModel(): this // See: Database.batch() // To be used by Model @writer methods only! // TODO: protect batch,callWriter,... from being used outside a @reader/@writer batch(...records: $ReadOnlyArray): Promise // To be used by Model @writer methods only! callWriter(action: () => Promise): Promise // To be used by Model @writer/@reader methods only! callReader(action: () => Promise): Promise // To be used by Model @writer/@reader methods only! subAction(action: () => Promise): Promise get table(): TableName // FIX_TS // Don't use this directly! Use `collection.create()` constructor(collection: Collection, raw: RawRecord) static _prepareCreate(collection: Collection, recordBuilder: (_: Model) => void): Model static _prepareCreateFromDirtyRaw(collection: Collection, dirtyRaw: DirtyRaw): Model static _disposableFromDirtyRaw(collection: Collection, dirtyRaw: DirtyRaw): Model _subscribers: [(isDeleted: boolean) => void, any][] experimentalSubscribe(subscriber: (isDeleted: boolean) => void, debugInfo?: any): Unsubscribe _notifyChanged(): void _notifyDestroyed(): void _getRaw(rawFieldName: ColumnName): Value _setRaw(rawFieldName: ColumnName, rawValue: Value): void // Please don't use this unless you really understand how Watermelon Sync works, and thought long and // hard about risks of inconsistency after sync _dangerouslySetRawWithoutMarkingColumnChange(rawFieldName: ColumnName, rawValue: Value): void __ensureCanSetRaw(): void __ensureNotDisposable(debugName: string): void }